Subroutes
API kattalashgani sari marshrutlar soni ham oshadi. Ularning ko‘pchiligi umumiy prefiksga ega bo‘ladi:
/api/v1/books
/api/v1/books/{id}
/api/v1/users
/api/v1/users/{id}
/api/v1/admin/stats
/api/v1/admin/users/{id}/ban
Har bir marshrutda /api/v1 qismini takrorlash noqulay. Versiyani v2ga o‘zgartirish kerak bo‘lsa, barcha qatorlarni qidirib, tahrirlab chiqishga to‘g‘ri keladi. Bundan tashqari, /admin ostidagi barcha marshrutlarga bir xil himoya qo‘yish kerak.
Subrouter bu muammolarni hal qiladi. U umumiy prefiks yoki shartga ega marshrutlarni bitta guruhga birlashtiradi.
PathPrefix va Subrouter
r := mux.NewRouter()
api := r.PathPrefix("/api/v1").Subrouter()
api.HandleFunc("/books", listBooks).Methods(http.MethodGet) // /api/v1/books
api.HandleFunc("/books/{id}", getBook).Methods(http.MethodGet) // /api/v1/books/{id}
r.PathPrefix("/api/v1")—/api/v1bilan boshlanadigan barcha yo‘llarga mos keladigan marshrut yaratadi..Subrouter()— shu marshrut ichida yangi router qaytaradi.- Subrouter’dagi marshrutlar prefiksga nisbatan yoziladi:
/books,/api/v1/booksemas.
Subrouter’lar ichma-ich joylashishi mumkin:
api := r.PathPrefix("/api/v1").Subrouter()
books := api.PathPrefix("/books").Subrouter()
books.HandleFunc("", listBooks).Methods(http.MethodGet) // /api/v1/books
books.HandleFunc("/{id:[0-9]+}", getBook).Methods(http.MethodGet) // /api/v1/books/{id}
books.HandleFunc("/{id:[0-9]+}/reviews", listReviews).Methods("GET") // /api/v1/books/{id}/reviews
books.HandleFunc("", ...) bo‘sh yo‘l bilan yozilgan. U prefiksning o‘ziga, ya’ni /api/v1/booksga mos keladi.
Resurslarni alohida funksiyalarga ajratish
Subrouter kodni tartibga solish uchun ham qulay. Har bir resurs o‘z marshrutlarini o‘zi ro‘yxatdan o‘tkazadi:
func (h *BookHandler) Register(r *mux.Router) {
r.HandleFunc("", h.list).Methods(http.MethodGet)
r.HandleFunc("", h.create).Methods(http.MethodPost)
r.HandleFunc("/{id:[0-9]+}", h.get).Methods(http.MethodGet)
r.HandleFunc("/{id:[0-9]+}", h.delete).Methods(http.MethodDelete)
}
func (h *UserHandler) Register(r *mux.Router) {
r.HandleFunc("", h.list).Methods(http.MethodGet)
r.HandleFunc("/{id:[0-9]+}", h.get).Methods(http.MethodGet)
}
main.go esa faqat prefikslarni belgilaydi:
api := r.PathPrefix("/api/v1").Subrouter()
bookHandler.Register(api.PathPrefix("/books").Subrouter())
userHandler.Register(api.PathPrefix("/users").Subrouter())
BookHandler o‘zi qaysi prefiks ostida joylashganini bilmaydi. Uni /api/v2/books yoki /library/books ostiga ko‘chirish uchun faqat main.go o‘zgartiriladi. Bu yondashuv clean architecture g‘oyalari bilan yaxshi uyg‘unlashadi.
API versiyalash
API ommaga ochilgach, uning javob formatini o‘zgartirish mavjud klientlarni buzadi. Masalan, mobil ilovaning eski versiyalari yangilanmagan bo‘lishi mumkin. Shuning uchun katta o‘zgarishlar yangi versiya sifatida chiqariladi, eski versiya esa ma’lum muddat ishlashda davom etadi:
v1 := r.PathPrefix("/api/v1").Subrouter()
v1.HandleFunc("/books/{id:[0-9]+}", getBookV1).Methods(http.MethodGet)
v2 := r.PathPrefix("/api/v2").Subrouter()
v2.HandleFunc("/books/{id:[0-9]+}", getBookV2).Methods(http.MethodGet)
Ikkala versiya bitta serverda yonma-yon ishlaydi. Ular bir xil service qatlamidan foydalanadi, faqat javob formatlari farq qiladi.
Guruhga middleware qo‘shish
Subrouter’ning eng foydali xususiyatlaridan biri — middleware faqat shu guruhga qo‘llanishi:
api := r.PathPrefix("/api/v1").Subrouter()
api.HandleFunc("/books", listBooks).Methods(http.MethodGet) // ochiq
admin := api.PathPrefix("/admin").Subrouter()
admin.Use(auth.Require, RequireRole(RoleAdmin))
admin.HandleFunc("/stats", stats).Methods(http.MethodGet) // himoyalangan
admin.HandleFunc("/users", listUsers).Methods(http.MethodGet) // himoyalangan
admin guruhiga keyinchalik qo‘shilgan har bir marshrut avtomatik himoyalanadi. Middleware’lar bilan ishlash keyingi darsda batafsil ko‘riladi.
Boshqa shartlar bo‘yicha subrouter
PathPrefixdan tashqari subrouter’ni boshqa shartlar bo‘yicha ham yaratish mumkin.
Host bo‘yicha
apiHost := r.Host("api.example.uz").Subrouter()
apiHost.HandleFunc("/books", listBooks)
adminHost := r.Host("admin.example.uz").Subrouter()
adminHost.HandleFunc("/", dashboard)
Host ichida ham o‘zgaruvchi ishlatish mumkin. Bu, masalan, har bir mijozga alohida subdomen beriladigan xizmatlar uchun qulay:
tenant := r.Host("{tenant:[a-z0-9-]+}.example.uz").Subrouter()
tenant.HandleFunc("/books", func(w http.ResponseWriter, r *http.Request) {
name := mux.Vars(r)["tenant"] // "maktab-5.example.uz" → "maktab-5"
// ...
})
Sarlavha bo‘yicha
// Versiyani URL emas, sarlavha orqali tanlash
v2 := r.Headers("X-API-Version", "2").Subrouter()
v2.HandleFunc("/books/{id}", getBookV2)
r.HandleFunc("/books/{id}", getBookV1) // sarlavha bo‘lmasa
Versiyani URL’da ko‘rsatish (/api/v2) va sarlavhada yuborish — ikkalasi ham keng tarqalgan. URL’dagi versiya ko‘rinarli va brauzerda sinash oson. Shu sababli u ko‘proq qo‘llanadi.
Info
Subrouter’lar ham ro‘yxatdan o‘tkazilgan tartibda tekshiriladi. Yuqoridagi misolda r.Headers(...) subrouter’i umumiy marshrutdan oldin e’lon qilingan. Tartib teskari bo‘lsa, sarlavha bilan kelgan so‘rov ham birinchi mos kelgan getBookV1 handleriga tushib qoladi.
To‘liq misol
Server ishga tushganda marshrutlar ro‘yxati chiqadi:
2026/09/30 10:00:00 GET /api/v1/books
2026/09/30 10:00:00 GET /api/v1/books/{id:[0-9]+}
2026/09/30 10:00:00 GET /api/v1/books/{id:[0-9]+}/reviews
2026/09/30 10:00:00 GET /api/v1/admin/stats
2026/09/30 10:00:00 GET /api/v2/books/{id:[0-9]+}
r.Walk subrouter’lar ichiga ham kiradi va to‘liq yo‘llarni ko‘rsatadi. if sharti faqat metodli marshrutlarni chiqaradi. PathPrefix yozuvlarining o‘zi ro‘yxatga tushmaydi.
curl http://localhost:8080/api/v1/books
curl http://localhost:8080/api/v1/books/7/reviews
curl http://localhost:8080/api/v2/books/7
curl http://localhost:8080/api/v1/admin/stats
curl -H "X-Admin-Key: maxfiy" http://localhost:8080/api/v1/admin/stats
{"data":["O‘tkan kunlar","Sarob"]}
{"book_id":"7","data":[]}
{"book":{"id":"7"},"meta":{"version":"2"}}
{"error":"admin huquqi kerak"}
{"books":2,"users":10}
Admin middleware’i faqat /api/v1/admin guruhiga qo‘llandi. Kitoblar endpointlari esa ochiq qoldi.
Xulosa
PathPrefix(...).Subrouter()umumiy prefiksli marshrutlarni guruhlaydi. Ichki marshrutlar prefiksga nisbatan yoziladi.- Subrouter’lar ichma-ich joylashishi mumkin.
- Har bir resursning
Registerfunksiyasi o‘z marshrutlarini qo‘shadi, prefiks esamain.goda belgilanadi. - API versiyalari alohida subrouter’larda yonma-yon ishlaydi.
- Middleware subrouter’ga qo‘shilsa, faqat shu guruhga ta’sir qiladi.
- Subrouter host, sarlavha va boshqa shartlar bo‘yicha ham yaratiladi.
Keyingi darsda gorilla/muxda middleware’lar bilan ishlashning xususiyatlarini ko‘rib chiqamiz.