HTTP router ба handler
Өмнөх хичээлд бид энгийн HTTP server үүсгэж сурсан. Гэвч бодит программд олон өөр URL хаяг байдаг — /users, /products, /orders гэх мэт. Эдгээр хаягийг зохих handler функц руу чиглүүлэх үйлдлийг routing гэнэ. Энэ хичээлд Go-ийн стандарт net/http package-ийн ServeMux router-ийг болон гуравдагч талын chi router-ийг судална.
ServeMux — суурилагдсан router
Go 1.22-аас эхлэн net/http.ServeMux нь method болон URL pattern дэмжих болсон. Энэ нь гуравдагч талын router суулгахгүйгээр олон route удирдах боломжийг олгодог.
package main
import (
"fmt"
"net/http"
)
func нүүрХуудас(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Нүүр хуудас")
}
func хэрэглэгчидHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Хэрэглэгчдийн жагсаалт")
}
func main() {
mux := http.NewServeMux()
// GET / → нүүр хуудас
mux.HandleFunc("GET /", нүүрХуудас)
// GET /users → хэрэглэгчид
mux.HandleFunc("GET /users", хэрэглэгчидHandler)
// POST /users → шинэ хэрэглэгч үүсгэх
mux.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Шинэ хэрэглэгч үүсгэлээ")
})
fmt.Println("Server :8080 дээр ажиллаж байна...")
http.ListenAndServe(":8080", mux)
}
"GET /users" гэж бичихэд зөвхөн GET method-той хүсэлтүүд тэр handler руу очно. POST хүсэлт өөр handler руу очно — энэ маш тохиромжтой!
URL parameter уншиж авах
Бодит программд URL-д ID байдаг: /users/42, /products/99 гэх мэт. Go 1.22-аас эдгээр wildcard pattern дэмждэг болсон.
package main
import (
"fmt"
"net/http"
)
func хэрэглэгчHandler(w http.ResponseWriter, r *http.Request) {
// URL-аас {id} утгыг авна
id := r.PathValue("id")
fmt.Fprintf(w, "Хэрэглэгчийн ID: %s\n", id)
}
func бүтээгдэхүүнHandler(w http.ResponseWriter, r *http.Request) {
категори := r.PathValue("category")
id := r.PathValue("id")
fmt.Fprintf(w, "Категори: %s, ID: %s\n", категори, id)
}
func main() {
mux := http.NewServeMux()
// /users/42 → id = "42"
mux.HandleFunc("GET /users/{id}", хэрэглэгчHandler)
// /shop/electronics/5 → category="electronics", id="5"
mux.HandleFunc("GET /shop/{category}/{id}", бүтээгдэхүүнHandler)
http.ListenAndServe(":8080", mux)
}
r.PathValue("id") нь URL-ийн {id} хэсгийн утгыг буцаана. Энэ нь маш цэвэр, ойлгомжтой синтакс юм.
Handler-ийг бүлэглэх — хариулагч бүтэц
Томоохон программд handler-уудыг бүтэц (struct) дотор бүлэглэх нь зүйтэй. Ингэснээр handler-ууд хооронд өгөгдөл хуваалцах боломжтой болно.
package main
import (
"encoding/json"
"fmt"
"net/http"
)
// Хэрэглэгчийн handler бүтэц
type ХэрэглэгчийнHandler struct {
хэрэглэгчид []string // бодит программд энэ DB байна
}
func (h *ХэрэглэгчийнHandler) жагсаалт(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(h.хэрэглэгчид)
}
func (h *ХэрэглэгчийнHandler) нэмэх(w http.ResponseWriter, r *http.Request) {
// Бодит программд энд JSON уншиж DB-д хадгална
fmt.Fprintln(w, "Хэрэглэгч нэмэгдлээ")
}
func main() {
handler := &ХэрэглэгчийнHandler{
хэрэглэгчид: []string{"Болд", "Энхээ", "Дорж"},
}
mux := http.NewServeMux()
mux.HandleFunc("GET /users", handler.жагсаалт)
mux.HandleFunc("POST /users", handler.нэмэх)
fmt.Println("Server :8080 дээр ажиллаж байна...")
http.ListenAndServe(":8080", mux)
}
Энэ хэлбэрийн кодыг handler pattern гэдэг бөгөөд Go-ийн backend хөгжүүлэлтэд маш өргөн хэрэглэгддэг.
Query parameter уншиж авах
URL-д ?name=Болд&age=25 гэх мэт query parameter байдаг. Эдгээрийг r.URL.Query() ашиглан унших боломжтой.
func хайлтHandler(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
нэр := query.Get("name") // "Болд"
хуудас := query.Get("page") // "2"
if нэр == "" {
нэр = "Хэрэглэгч"
}
if хуудас == "" {
хуудас = "1"
}
fmt.Fprintf(w, "Хайлт: %s, Хуудас: %s\n", нэр, хуудас)
}
// mux.HandleFunc("GET /search", хайлтHandler)
// GET /search?name=Болд&page=2 → Хайлт: Болд, Хуудас: 2
Дараагийн хичээлд:
HTTP router-ийн дараагийн чухал ойлголт бол middleware юм. Middleware нь хүсэлт handler-т хүрэхээс өмнө дундаас нь логг бичих, authentication шалгах, rate limiting хийх зэрэг ажлуудыг гүйцэтгэдэг давхарга юм.