Go / HTTP router ба handler

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 удирдах боломжийг олгодог.

go
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 дэмждэг болсон.

go
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-ууд хооронд өгөгдөл хуваалцах боломжтой болно.

go
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() ашиглан унших боломжтой.

go
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 хийх зэрэг ажлуудыг гүйцэтгэдэг давхарга юм.