Tarkibga o'tish

Path parametrlari

Path parametri — URL yo‘lining o‘zgaruvchan qismi. /books/42 manzilida 42 — kitob ID’si, /users/7/books/3 manzilida esa 7 va 3 — ikkita alohida parametr.

gorilla/mux path parametrlari bilan ishlashda standart routerdan ko‘ra ko‘proq imkoniyat beradi: parametrni regex bilan cheklash, query parametrlar bo‘yicha marshrut tanlash va marshrut nomidan URL yasash. Bu darsda ularni birma-bir ko‘rib chiqamiz.

mux.Vars bilan o‘qish

Parametr marshrut ichida {nom} ko‘rinishida e’lon qilinadi. Handler ichida esa mux.Vars(r) orqali o‘qiladi:

r.HandleFunc("/users/{userID}/books/{bookID}", func(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    userID := vars["userID"]
    bookID := vars["bookID"]
    fmt.Fprintf(w, "Foydalanuvchi: %s, kitob: %s\n", userID, bookID)
}).Methods(http.MethodGet)

mux.Vars(r) map[string]string qaytaradi. Qiymatlar doim satr bo‘ladi. Kalit mavjud bo‘lmasa, bo‘sh satr qaytadi.

Standart router bilan solishtirish:

// net/http (Go 1.22+)
id := r.PathValue("id")

// gorilla/mux
id := mux.Vars(r)["id"]

gorilla/mux parametrlarni so‘rov contextida saqlaydi. Shu sababli r.PathValue() gorilla/mux marshrutlarida bo‘sh satr qaytaradi. Aksincha, mux.Vars() standart router bilan ishlamaydi.

Regex bilan cheklash

Parametrdan keyin : qo‘yib, regex yozish mumkin:

r.HandleFunc("/books/{id:[0-9]+}", getBook)
r.HandleFunc("/users/{username:[a-z0-9_]{3,20}}", getUser)
r.HandleFunc("/orders/{uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}", getOrder)
r.HandleFunc("/archive/{year:[0-9]{4}}/{month:0[1-9]|1[0-2]}", archive)

Regex’ga mos kelmagan so‘rov marshrutga umuman tushmaydi va 404 oladi. Bu ikki foyda beradi:

  1. Handler ichida noto‘g‘ri formatdagi qiymat bilan ishlash shart emas.
  2. Marshrutlar orasidagi chalkashliklar yo‘qoladi. /books/latest va /books/{id:[0-9]+} bir-biriga xalaqit bermaydi.

Warning

Regex qiymatning formatini tekshiradi, ma’nosini emas. {id:[0-9]+} 99999999999999999999 qiymatini ham qabul qiladi. Bu son int turiga sig‘maydi, shuning uchun strconv.Atoi xato qaytaradi. Qiymatni turga o‘tkazganda xatoni baribir tekshiring.

Regex marshrut butun segmentga qo‘llanadi. Uning boshiga ^, oxiriga $ qo‘yish shart emas.

Butun qolgan yo‘lni olish

Standart routerdagi {path...} kabi, yo‘lning qolgan qismini / belgilari bilan birga olish uchun regex ishlatiladi:

r.HandleFunc("/files/{path:.+}", serveFile)
// /files/docs/2026/hisobot.pdf → path = "docs/2026/hisobot.pdf"

Danger

Foydalanuvchi bergan yo‘l bilan fayl ochishda ehtiyot bo‘ling. /files/../../etc/passwd kabi so‘rov server fayl tizimining boshqa joylariga yetib borishga urinadi. Buning oldini olish uchun os.Root (Go 1.24+) yoki http.FileServer ishlatiladi. Batafsil Input sanitizatsiyasi darsida.

Parametrni turga o‘tkazish uchun yordamchi

func intVar(r *http.Request, name string) (int64, error) {
    s := mux.Vars(r)[name]
    n, err := strconv.ParseInt(s, 10, 64)
    if err != nil || n <= 0 {
        return 0, fmt.Errorf("%s musbat butun son bo‘lishi kerak", name)
    }
    return n, nil
}

Regex formatni kafolatlaydi, bu funksiya esa qiymat chegarasini tekshiradi.

Query parametrlar bo‘yicha moslashtirish

gorilla/mux marshrutni query parametrlar bo‘yicha ham tanlay oladi:

r.HandleFunc("/search", searchByISBN).Queries("isbn", "{isbn:[0-9-]{10,17}}")
r.HandleFunc("/search", searchByText).Queries("q", "{q}")
r.HandleFunc("/search", searchHelp)
So‘rov Handler
/search?isbn=978-9943-01-234-5 searchByISBN
/search?q=qodiriy searchByText
/search searchHelp

Query qiymatlari ham mux.Vars(r) ichiga tushadi:

func searchByText(w http.ResponseWriter, r *http.Request) {
    q := mux.Vars(r)["q"]
    // ...
}

Bu imkoniyatni ehtiyotkorlik bilan ishlating. Query parametrlar odatda ixtiyoriy bo‘ladi: filtrlash, saralash va sahifalash uchun. Marshrutni ularga bog‘lash API’ni murakkablashtiradi. Aksariyat hollarda query parametrlarni handler ichida r.URL.Query() bilan o‘qish soddaroq. Bu usul URL va query parametrlar darsida ko‘rilgan.

Nomli marshrutlar va URL yasash

Marshrutga nom berib, keyin shu nom orqali URL yasash mumkin:

r.HandleFunc("/users/{userID:[0-9]+}/books/{bookID:[0-9]+}", getUserBook).
    Methods(http.MethodGet).
    Name("user-book")

u, err := r.Get("user-book").URL("userID", "7", "bookID", "42")
// u.String() == "/users/7/books/42"

Foydasi — URL’lar kodda qo‘lda yig‘ilmaydi. Marshrut yo‘li o‘zgarsa, masalan /users/... o‘rniga /members/... bo‘lsa, barcha yasalgan URL’lar avtomatik yangilanadi.

URL() qiymatlarni regex bilan ham tekshiradi:

_, err := r.Get("user-book").URL("userID", "abc", "bookID", "42")
// err: mux: variable "abc" doesn't match, expected "^[0-9]+$"

Bu imkoniyat, ayniqsa, 201 Created javobidagi Location sarlavhasi va HATEOAS uslubidagi havolalar uchun foydali:

loc, _ := router.Get("user-book").URL("userID", uid, "bookID", bid)
w.Header().Set("Location", loc.String())

To‘liq misol

main.go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"

    "github.com/gorilla/mux"
)

func writeJSON(w http.ResponseWriter, status int, v any) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(v)
}

func intVar(r *http.Request, name string) (int64, error) {
    n, err := strconv.ParseInt(mux.Vars(r)[name], 10, 64)
    if err != nil || n <= 0 {
        return 0, fmt.Errorf("%s musbat butun son bo‘lishi kerak", name)
    }
    return n, nil
}

type api struct {
    router *mux.Router
}

func (a *api) getUserBook(w http.ResponseWriter, r *http.Request) {
    userID, err := intVar(r, "userID")
    if err != nil {
        writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
        return
    }
    bookID, err := intVar(r, "bookID")
    if err != nil {
        writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
        return
    }

    self, _ := a.router.Get("user-book").URL(
        "userID", strconv.FormatInt(userID, 10),
        "bookID", strconv.FormatInt(bookID, 10))
    owner, _ := a.router.Get("user").URL("userID", strconv.FormatInt(userID, 10))

    writeJSON(w, http.StatusOK, map[string]any{
        "data":  map[string]any{"user_id": userID, "book_id": bookID},
        "links": map[string]string{"self": self.String(), "owner": owner.String()},
    })
}

func (a *api) getUser(w http.ResponseWriter, r *http.Request) {
    writeJSON(w, http.StatusOK, map[string]string{"user_id": mux.Vars(r)["userID"]})
}

func (a *api) archive(w http.ResponseWriter, r *http.Request) {
    v := mux.Vars(r)
    writeJSON(w, http.StatusOK, map[string]string{"year": v["year"], "month": v["month"]})
}

func (a *api) searchByISBN(w http.ResponseWriter, r *http.Request) {
    writeJSON(w, http.StatusOK, map[string]string{"mode": "isbn", "isbn": mux.Vars(r)["isbn"]})
}

func (a *api) searchByText(w http.ResponseWriter, r *http.Request) {
    writeJSON(w, http.StatusOK, map[string]string{"mode": "text", "q": mux.Vars(r)["q"]})
}

func main() {
    a := &api{router: mux.NewRouter()}
    r := a.router

    r.HandleFunc("/users/{userID:[0-9]+}", a.getUser).Methods(http.MethodGet).Name("user")
    r.HandleFunc("/users/{userID:[0-9]+}/books/{bookID:[0-9]+}", a.getUserBook).
        Methods(http.MethodGet).Name("user-book")
    r.HandleFunc("/archive/{year:[0-9]{4}}/{month:0[1-9]|1[0-2]}", a.archive).Methods(http.MethodGet)
    r.HandleFunc("/search", a.searchByISBN).Queries("isbn", "{isbn:[0-9-]{10,17}}")
    r.HandleFunc("/search", a.searchByText).Queries("q", "{q}")

    log.Println("Server: http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", r))
}
curl http://localhost:8080/users/7/books/42
curl http://localhost:8080/users/7/books/99999999999999999999
curl http://localhost:8080/archive/2026/09
curl http://localhost:8080/archive/2026/13
curl "http://localhost:8080/search?isbn=978-9943-01-234-5"
curl "http://localhost:8080/search?q=qodiriy"
{"data":{"book_id":42,"user_id":7},"links":{"owner":"/users/7","self":"/users/7/books/42"}}
{"error":"bookID musbat butun son bo‘lishi kerak"}
{"month":"09","year":"2026"}
404 page not found
{"isbn":"978-9943-01-234-5","mode":"isbn"}
{"mode":"text","q":"qodiriy"}
  • Juda katta son regex’dan o‘tdi, lekin intVar uni rad etdi.
  • 13-oy regex’ga mos kelmadi, shuning uchun marshrut topilmadi.
  • links bo‘limidagi URL’lar marshrut nomlari orqali yasaldi.

Xulosa

  • Parametrlar {nom} ko‘rinishida e’lon qilinadi va mux.Vars(r) bilan o‘qiladi.
  • {nom:regex} parametrni format bo‘yicha cheklaydi. Mos kelmagan so‘rov 404 oladi.
  • Regex qiymat ma’nosini tekshirmaydi. Turga o‘tkazishda xatoni baribir tekshiring.
  • .Queries() marshrutni query parametrlar bo‘yicha tanlaydi, lekin undan ehtiyotkorlik bilan foydalaning.
  • .Name() va .URL() URL’larni qo‘lda yig‘ish zaruratini yo‘qotadi.

Keyingi darsda marshrutlarni guruhlash uchun subrouter’lardan foydalanamiz.