Tarkibga o'tish

URL routing

Routing — kelgan so‘rovni uning yo‘li va metodiga qarab kerakli handlerga yo‘naltirish. GET /books so‘rovi kitoblar ro‘yxatini qaytaradigan funksiyaga, DELETE /books/7 esa kitobni o‘chiradigan funksiyaga borishi kerak.

Buni bino ichidagi yo‘l ko‘rsatkichga o‘xshatish mumkin. Qabulxonada kim qaysi xonaga borishi yozilgan: buxgalteriya — 2-qavat, kadrlar bo‘limi — 3-qavat. Router ham so‘rovning manzilini o‘qib, uni kerakli "xona"ga yuboradi.

Go’da routing uchun standart kutubxonadagi http.ServeMux ishlatiladi. Go 1.22 versiyasidan boshlab u ancha kuchaydi: patternlarda HTTP metodini ko‘rsatish va yo‘ldan parametr olish mumkin bo‘ldi. Bu darsdagi misollar Go 1.22 yoki undan yangi versiyani talab qiladi.

Pattern tuzilishi

ServeMux patterni umumiy holda quyidagicha yoziladi:

[METOD ][HOST]/[YO‘L]

Misollar:

Pattern Nimaga mos keladi
/books Istalgan metod bilan aynan /books
GET /books Faqat GET (va HEAD) bilan /books
POST /books Faqat POST bilan /books
GET /books/{id} /books/1, /books/abc va hokazo
/static/ /static/ bilan boshlanadigan barcha yo‘llar
api.example.com/ Faqat shu hostga kelgan so‘rovlar

Metod va yo‘l orasida bitta bo‘shliq bo‘lishi kerak.

Metod bo‘yicha routing

Go 1.22 dan oldin bir yo‘l uchun turli metodlarni handler ichida switch r.Method bilan ajratish kerak edi. Endi buni pattern o‘zi bajaradi.

Misol

main.go
package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("GET /books", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Kitoblar ro‘yxati")
    })
    mux.HandleFunc("POST /books", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Yangi kitob qo‘shildi")
    })

    log.Fatal(http.ListenAndServe(":8080", mux))
}
curl http://localhost:8080/books
curl -X POST http://localhost:8080/books
curl -i -X DELETE http://localhost:8080/books
Kitoblar ro‘yxati
Yangi kitob qo‘shildi
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, POST
...

Uchinchi so‘rov uchun handler yo‘q. /books yo‘li mavjud, lekin DELETE metodi ruxsat etilmagan. Shu sababli mux o‘zi 405 Method Not Allowed qaytaradi. Allow sarlavhasida qaysi metodlar mumkinligi ko‘rsatiladi.

Info

GET patterni HEAD so‘rovlariga ham mos keladi. HEAD — javob tanasisiz GET. Klient faqat sarlavhalarni tekshirmoqchi bo‘lganda ishlatiladi.

Yo‘l parametrlari (wildcard)

/books/1, /books/2, /books/3 uchun alohida pattern yozib bo‘lmaydi. Buning o‘rniga yo‘lning o‘zgaruvchan qismi {nom} ko‘rinishida belgilanadi:

main.go
package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("GET /books/{id}", func(w http.ResponseWriter, r *http.Request) {
        id := r.PathValue("id")
        fmt.Fprintf(w, "Kitob ID: %s\n", id)
    })

    mux.HandleFunc("GET /users/{userID}/books/{bookID}", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Foydalanuvchi: %s, kitob: %s\n",
            r.PathValue("userID"), r.PathValue("bookID"))
    })

    log.Fatal(http.ListenAndServe(":8080", mux))
}
curl http://localhost:8080/books/42
curl http://localhost:8080/users/5/books/17
Kitob ID: 42
Foydalanuvchi: 5, kitob: 17

r.PathValue("id") pattern ichidagi {id} o‘rniga kelgan qiymatni qaytaradi. Qiymat doim string bo‘ladi. Son kerak bo‘lsa, uni strconv.Atoi bilan o‘zgartirib, xatoni tekshirish lozim:

id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
    http.Error(w, "id son bo‘lishi kerak", http.StatusBadRequest)
    return
}

Wildcard yo‘lning bitta to‘liq segmentiga mos keladi. Segment — ikki / orasidagi qism. /books/{id} patterni /books/42ga mos keladi, lekin /books/42/reviewsga mos kelmaydi.

Segmentning bir qismini wildcard qilib bo‘lmaydi: /books/id-{id} noto‘g‘ri pattern.

Qolgan butun yo‘lni olish: {nom...}

Wildcard oxirida ... yozilsa, u yo‘lning qolgan barcha qismiga mos keladi:

mux.HandleFunc("GET /files/{path...}", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Fayl yo‘li: %s\n", r.PathValue("path"))
})

/files/docs/2026/hisobot.pdf so‘rovida path qiymati docs/2026/hisobot.pdf bo‘ladi. Bunday wildcard faqat pattern oxirida turishi mumkin.

Yakuniy / va aniq moslik

Pattern / bilan tugasa, u shu prefiks bilan boshlanadigan barcha yo‘llarga mos keladi:

mux.HandleFunc("/static/", handler) // /static/, /static/css/app.css, /static/a/b/c
mux.HandleFunc("/about", handler)   // faqat /about

Eng muhim oqibat / patternida ko‘rinadi. U hech qaysi boshqa patternga mos kelmagan barcha yo‘llarni ushlab oladi:

mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Bosh sahifa")
})

Endi /qandaydir-yoq-sahifa ham Bosh sahifa qaytaradi. 404 kutilgan joyda bu chalkashlik keltirib chiqaradi.

Faqat aynan / yo‘lini ushlash uchun maxsus {$} belgisi ishlatiladi:

mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Bosh sahifa")
})

{$} "yo‘l shu yerda tugaydi" degan ma’noni bildiradi. /{$} faqat /ga, /books/{$} esa faqat /books/ga mos keladi.

Avtomatik yo‘naltirish

/static/ patterni ro‘yxatdan o‘tgan bo‘lsa-yu, klient /static (oxirida / siz) so‘rasa, mux uni avtomatik ravishda /static/ manziliga yo‘naltiradi (redirect). Bu brauzer uchun qulay, lekin API’da kutilmagan bo‘lishi mumkin. API yo‘llarini yakuniy / siz yozish odatiy: /books, /books/{id}.

Ustuvorlik qoidalari

Bir so‘rovga bir nechta pattern mos kelishi mumkin. Bunday holatda mux eng aniq (most specific) patternni tanlaydi. Patternlarning qaysi tartibda ro‘yxatdan o‘tgani ahamiyatga ega emas.

mux.HandleFunc("GET /books/{id}", kitobniOlish)
mux.HandleFunc("GET /books/latest", oxirgiKitob)

/books/latest so‘rovi ikkala patternga mos keladi. Lekin /books/latest aniqroq, chunki u faqat bitta yo‘lga mos keladi. /books/{id} esa ko‘p yo‘llarga mos keladi. Shu sababli oxirgiKitob chaqiriladi.

Metod ham aniqlikka ta’sir qiladi: GET /books patterni /books patternidan aniqroq.

Ziddiyatli patternlar

Ba’zan ikki pattern bir xil so‘rovlarga mos keladi, lekin qaysi biri aniqroq ekanini aniqlab bo‘lmaydi:

mux.HandleFunc("/books/{id}", a)
mux.HandleFunc("/{resource}/latest", b)

/books/latest ikkalasiga ham mos keladi. Birinchi pattern birinchi segmentda, ikkinchisi ikkinchi segmentda aniqroq. Go bunday holatda qaror qabul qilmaydi va ikkinchi patternni ro‘yxatdan o‘tkazayotganda panic beradi. Bu xato dastur ishga tushishi bilanoq ko‘rinadi, shuning uchun production’ga yetib bormaydi.

Hostga qarab routing

Pattern hostni ham o‘z ichiga olishi mumkin:

mux.HandleFunc("api.example.com/", apiHandler)
mux.HandleFunc("admin.example.com/", adminHandler)

Host ko‘rsatilgan patternlar hostsiz patternlardan ustun turadi. Bu usul bir server bir nechta domenga xizmat qilganda foydali.

404 va 405 javoblari

Mux mos pattern topmasa, standart 404 page not found matnini qaytaradi. Yo‘l topilib, metod mos kelmasa, 405 Method Not Allowed qaytaradi.

ServeMuxda maxsus NotFound sozlamasi yo‘q. 404 javobini o‘zgartirish kerak bo‘lsa, eng oddiy usul — / patterniga o‘z handleringizni qo‘yish:

mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusNotFound)
    w.Write([]byte(`{"error":"endpoint topilmadi"}`))
})

/ patternida metod yo‘q va u eng umumiy pattern. Shuning uchun boshqa hech narsa mos kelmaganda aynan u ishlaydi.

Warning

Bu usulning bir kamchiligi bor. Metodsiz / patterni istalgan metod va istalgan yo‘lga mos keladi. Natijada PUT /books/3 so‘rovi endi 405 emas, 404 oladi, chunki mux uchun mos pattern topildi. 405 javobi muhim bo‘lsa, / patternini ro‘yxatdan o‘tkazmang va standart 404 matniga rozi bo‘ling. Barcha xato javoblarini JSON ko‘rinishiga keltirish kerak bo‘lsa, buni middleware bilan qilish mumkin. Middleware keyingi darslarda ko‘riladi.

To‘liq misol: kitoblar API’sining routeri

main.go
package main

import (
    "fmt"
    "log"
    "net/http"
    "strconv"
)

func listBooks(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Barcha kitoblar")
}

func createBook(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusCreated)
    fmt.Fprintln(w, "Kitob yaratildi")
}

func getBook(w http.ResponseWriter, r *http.Request) {
    id, err := strconv.Atoi(r.PathValue("id"))
    if err != nil || id <= 0 {
        http.Error(w, "id musbat son bo‘lishi kerak", http.StatusBadRequest)
        return
    }
    fmt.Fprintf(w, "%d-kitob\n", id)
}

func deleteBook(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusNoContent)
}

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("GET /books", listBooks)
    mux.HandleFunc("POST /books", createBook)
    mux.HandleFunc("GET /books/{id}", getBook)
    mux.HandleFunc("DELETE /books/{id}", deleteBook)

    log.Println("Server: http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", mux))
}

Barcha endpointlar bir joyda ko‘rinib turibdi. Har bir handler faqat o‘z ishini bajaradi, metodni tekshirish esa router zimmasida.

So‘rov Natija
GET /books 200 — Barcha kitoblar
POST /books 201 — Kitob yaratildi
GET /books/3 200 — 3-kitob
GET /books/abc 400 — id musbat son bo‘lishi kerak
DELETE /books/3 204 — tanasiz javob
PUT /books/3 405 — metod ruxsat etilmagan
GET /authors 404 — page not found

Xulosa

  • Go 1.22 dan boshlab ServeMux patternida metod ko‘rsatiladi: "GET /books".
  • {id} yo‘lning bitta segmentiga, {path...} esa qolgan barcha qismiga mos keladi. Qiymat r.PathValue() bilan olinadi.
  • / bilan tugaydigan pattern prefiks sifatida ishlaydi, aniq moslik uchun {$} ishlatiladi.
  • Bir nechta pattern mos kelsa, eng aniq pattern tanlanadi. Aniqlab bo‘lmaydigan ziddiyat panicga olib keladi.
  • Yo‘l bor, lekin metod mos kelmasa, 405, yo‘l umuman topilmasa, 404 qaytadi.

Keyingi darsda URL’ning yana bir qismi — query parametrlar bilan ishlashni ko‘ramiz.