Tarkibga o'tish

net/http paketining tuzilishi

net/http — Go standart kutubxonasidagi HTTP bilan ishlash paketi. U orqali web server ham, HTTP klient ham yoziladi. Go’da API qurishni boshlashdan oldin shu paketning qanday qismlardan iboratini tushunib olish foydali.

Tasavvur qiling, siz restoran ochdingiz. Eshik oldida mehmonlarni kutib oladigan xodim bor, u mehmonni kerakli stolga yo‘naltiradi. Har bir stolga alohida ofitsiant xizmat qiladi. net/httpda ham xuddi shunday: server ulanishlarni qabul qiladi, router so‘rovni kerakli joyga yo‘naltiradi, handler esa javobni tayyorlaydi.

Asosiy qismlar

net/http paketida ko‘p tur va funksiya bor. Lekin server tomonida deyarli hamma narsa quyidagi beshta tushuncha atrofida quriladi:

Qism Vazifasi
http.Handler So‘rovga javob beradigan har qanday narsa uchun interfeys
http.HandlerFunc Oddiy funksiyani Handlerga aylantiradigan tur
http.ServeMux Router: URL yo‘liga qarab kerakli handlerni tanlaydi
http.Server Portni tinglaydi, ulanishlarni boshqaradi
*http.Request va http.ResponseWriter Kelgan so‘rov va yuboriladigan javob

Klient tomonida esa http.Client va http.Response ishlatiladi. Ular bilan API ga so‘rov yuborish darsida tanishgansiz.

So‘rov serverga kelgandan boshlab javob qaytgunicha quyidagi yo‘lni bosib o‘tadi:

Klient
  │  TCP ulanish
  ↓
http.Server          → ulanishni qabul qiladi, HTTP so‘rovni o‘qiydi
  │
  ↓
http.ServeMux        → yo‘l va metodga qarab handlerni tanlaydi
  │
  ↓
http.Handler         → ServeHTTP(w, r) chaqiriladi
  │
  ↓
http.ResponseWriter  → javob klientga yoziladi

Endi har bir qismni alohida ko‘rib chiqamiz.

http.Handler interfeysi

Butun paketning markazida bitta kichik interfeys turadi:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

ServeHTTP metodiga ega har qanday tur — handler. Server so‘rovni qabul qilgach, aynan shu metodni chaqiradi.

Misol

main.go
package main

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

type salomHandler struct {
    ism string
}

func (h salomHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Salom, %s!\n", h.ism)
}

func main() {
    h := salomHandler{ism: "Go"}

    log.Println("Server: http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", h))
}
curl http://localhost:8080
Salom, Go!

salomHandler oddiy struct. Unga ServeHTTP metodi qo‘shilgani uchun u http.Handler interfeysini qanoatlantiradi. Shu sababli uni to‘g‘ridan-to‘g‘ri ListenAndServega berish mumkin.

Bu yerda router yo‘q. Shuning uchun /, /abc yoki /users — qaysi yo‘lga murojaat qilmang, bir xil javob keladi.

Info

Handler struct bo‘lishi uning ichida bog‘liqliklarni saqlash imkonini beradi: database ulanishi, logger, konfiguratsiya va hokazo. Bu usul keyingi darslarda, ayniqsa clean architecture mavzusida ko‘p ishlatiladi.

http.HandlerFunc turi

Har bir handler uchun alohida struct yozish noqulay. Ko‘pincha oddiy funksiya yetarli. Buning uchun http.HandlerFunc turi bor:

type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

HandlerFunc — funksiya turi. Unga ServeHTTP metodi biriktirilgan va bu metod funksiyaning o‘zini chaqiradi. Natijada mos imzoli istalgan funksiyani http.HandlerFunc(...) ko‘rinishida Handlerga aylantirish mumkin:

main.go
package main

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

func salom(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Salom, dunyo!")
}

func main() {
    var h http.Handler = http.HandlerFunc(salom)

    log.Fatal(http.ListenAndServe(":8080", h))
}

http.HandlerFunc(salom) — funksiya chaqiruvi emas, balki tur o‘zgartirish (type conversion). salom funksiyasi HandlerFunc turiga o‘tkaziladi va endi uning ServeHTTP metodi bor.

Bu naqsh (pattern) Go’da juda keng tarqalgan. Middleware yozishda ham aynan shu usul ishlatiladi.

http.ServeMux — router

Haqiqiy API’da har bir yo‘l o‘z handleriga ega bo‘ladi: /books bitta ishni, /users boshqa ishni bajaradi. Yo‘lga qarab handler tanlash vazifasini multiplexer (qisqacha mux) yoki router bajaradi.

main.go
package main

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

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

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

    log.Fatal(http.ListenAndServe(":8080", mux))
}

http.NewServeMux() yangi router yaratadi. mux.HandleFunc unga yo‘l va handlerni ro‘yxatdan o‘tkazadi.

E’tibor bering: ServeMuxning o‘zi ham ServeHTTP metodiga ega, ya’ni u ham http.Handler. Shu sababli ListenAndServega to‘g‘ridan-to‘g‘ri beriladi. Server so‘rovni mux’ga uzatadi, mux esa mos handlerni topib, uning ServeHTTP metodini chaqiradi.

"GET /books" ko‘rinishidagi yozuv Go 1.22 versiyasidan beri ishlaydi: pattern ichida HTTP metodi ham ko‘rsatiladi. Routing qoidalari URL routing darsida batafsil ko‘riladi.

DefaultServeMux

Oldingi darslarda http.HandleFunc("/", ...) va http.ListenAndServe(":8080", nil) yozgan edik. Bu yerda mux yaratilmagan, lekin routing ishlagan. Sababi paket ichida tayyor global router bor — http.DefaultServeMux.

http.HandleFunc("/", handler)          // DefaultServeMux'ga qo‘shadi
http.ListenAndServe(":8080", nil)      // nil → DefaultServeMux ishlatiladi

Kichik misollar uchun bu qulay. Lekin loyiha kattalashganda o‘z mux’ingizni yaratish yaxshiroq:

  • global o‘zgaruvchi bo‘lgani uchun istalgan import qilingan paket unga handler qo‘shib qo‘yishi mumkin;
  • testlarda har bir test uchun toza router yaratish oson bo‘ladi;
  • qaysi yo‘llar ro‘yxatdan o‘tganini bitta joyda ko‘rish mumkin.

http.Server — server sozlamalari

http.ListenAndServe qulay funksiya, lekin uning ichida http.Server yaratiladi va ko‘p sozlamalar standart holatda qoladi. Server ustidan to‘liq nazorat kerak bo‘lsa, http.Serverni o‘zingiz yarating:

main.go
package main

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

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Server ishlayapti")
    })

    srv := &http.Server{
        Addr:              ":8080",
        Handler:           mux,
        ReadHeaderTimeout: 5 * time.Second,
        ReadTimeout:       10 * time.Second,
        WriteTimeout:      10 * time.Second,
        IdleTimeout:       60 * time.Second,
    }

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

Maydonlarning ma’nosi:

  • Addr — tinglanadigan manzil va port.
  • Handler — barcha so‘rovlarni qabul qiladigan asosiy handler, odatda router.
  • ReadHeaderTimeout — so‘rov sarlavhalarini o‘qish uchun ajratilgan vaqt.
  • ReadTimeout — butun so‘rovni, ya’ni sarlavha va tanani o‘qish uchun vaqt.
  • WriteTimeout — javobni yozib tugatish uchun vaqt.
  • IdleTimeout — keep-alive ulanish keyingi so‘rovni qancha kutishi.

Warning

http.ListenAndServe yaratgan serverda timeoutlar yo‘q. Sekin yoki qasddan juda sekin ma’lumot yuborayotgan klientlar ulanishlarni uzoq vaqt band qilib turishi mumkin. Production’da doim http.Serverni timeoutlar bilan yarating.

*http.Request va http.ResponseWriter

Har bir handler ikki argument oladi.

*http.Request — klient yuborgan so‘rov. Unda metod, URL, sarlavhalar, tana, cookie’lar va context bor:

r.Method              // "GET", "POST", ...
r.URL.Path            // "/books/42"
r.URL.Query()         // query parametrlar
r.Header.Get("Accept")
r.Body                // so‘rov tanasi (io.ReadCloser)
r.Context()           // so‘rovga bog‘langan context

http.ResponseWriter — javobni yozish uchun interfeys. Uning uchta metodi bor:

type ResponseWriter interface {
    Header() Header
    Write([]byte) (int, error)
    WriteHeader(statusCode int)
}

Header() orqali javob sarlavhalari o‘rnatiladi. WriteHeader() status kodni yuboradi. Write() esa javob tanasini yozadi.

Bu ikki obyekt bilan ishlash ResponseWriter va *Request obyektlari darsida batafsil ko‘riladi.

Paketdagi yordamchi funksiyalar

net/http tez-tez uchraydigan ishlar uchun tayyor funksiyalar ham beradi:

Funksiya Nima qiladi
http.Error(w, xabar, kod) Matnli xato javobini yuboradi
http.NotFound(w, r) 404 page not found javobini yuboradi
http.Redirect(w, r, url, kod) Klientni boshqa manzilga yo‘naltiradi
http.ServeFile(w, r, yo‘l) Diskdagi faylni javob sifatida yuboradi
http.FileServer(fs) Katalogdagi statik fayllarni tarqatadigan handler
http.StripPrefix(prefiks, h) URL boshidagi prefiksni olib tashlab, keyingi handlerga uzatadi
http.TimeoutHandler(h, vaqt, xabar) Handler berilgan vaqtda tugamasa, 503 qaytaradi
http.MaxBytesReader(w, body, n) So‘rov tanasi hajmini cheklaydi

Status kodlar uchun raqam o‘rniga konstantalardan foydalaning: http.StatusOK, http.StatusCreated, http.StatusNotFound. Metodlar uchun ham shunday: http.MethodGet, http.MethodPost. Bunday kod o‘qishga oson va xato yozish ehtimoli kam.

Misol: statik fayllar va handler birga

main.go
package main

import (
    "log"
    "net/http"
)

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

    // ./public katalogidagi fayllar /static/ yo‘li orqali beriladi
    fs := http.FileServer(http.Dir("./public"))
    mux.Handle("GET /static/", http.StripPrefix("/static/", fs))

    mux.HandleFunc("GET /eski-sahifa", func(w http.ResponseWriter, r *http.Request) {
        http.Redirect(w, r, "/static/index.html", http.StatusMovedPermanently)
    })

    log.Fatal(http.ListenAndServe(":8080", mux))
}

/static/logo.png so‘rovi kelganda StripPrefix yo‘lning boshidagi /static/ qismini olib tashlaydi. FileServer esa ./public/logo.png faylini qidiradi. /eski-sahifa ochilsa, klient 301 status bilan yangi manzilga yo‘naltiriladi.

E’tibor bering, mux.Handle tayyor http.Handlerni, mux.HandleFunc esa oddiy funksiyani qabul qiladi.

Klient tomoni

net/http faqat server uchun emas. Tashqi API’ga murojaat qilish uchun xuddi shu paketdagi http.Client ishlatiladi:

client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get("https://example.com")

Server va klient bir xil Request, Response va Header turlaridan foydalanadi. Shu sababli bir tomonni bilsangiz, ikkinchisini o‘rganish oson kechadi.

Xulosa

Bu darsda net/http paketining asosiy qismlari bilan tanishdik:

  • http.Handler — ServeHTTP metodiga ega har qanday tur;
  • http.HandlerFunc — oddiy funksiyani handlerga aylantiradi;
  • http.ServeMux — so‘rovni yo‘l va metodga qarab kerakli handlerga yo‘naltiradi;
  • http.Server — portni tinglaydi, timeoutlar va boshqa sozlamalarni boshqaradi;
  • *http.Request va http.ResponseWriter — kirish va chiqish.

Bu qismlarning barchasi Handler interfeysi orqali bir-biriga ulanadi. Keyingi darsda http.HandleFunc va http.ListenAndServe funksiyalarini batafsil ko‘rib chiqamiz.