Tarkibga o'tish

ResponseWriter va *Request obyektlari

Har bir Go handleri ikki argument oladi: w http.ResponseWriter va r *http.Request. Birinchisi orqali klientga javob yoziladi. Ikkinchisida klient yuborgan so‘rov haqidagi barcha ma’lumot bor.

Buni pochta bo‘limiga o‘xshatish mumkin. r — sizga kelgan xat: konvertida kimdan kelgani va qayerga yuborilgani yozilgan, ichida esa xatning o‘zi bor. w — javob xati uchun bo‘sh konvert va qog‘oz.

*http.Request — kelgan so‘rov

http.Request — struct. Handlerga uning ko‘rsatkichi (*http.Request) beriladi. Eng ko‘p ishlatiladigan maydonlar:

Maydon yoki metod Nima beradi Misol qiymat
r.Method HTTP metodi "POST"
r.URL.Path Yo‘l "/books/42"
r.URL.Query() Query parametrlar ?page=2 → page: [2]
r.Header So‘rov sarlavhalari Content-Type: application/json
r.Body So‘rov tanasi JSON, forma yoki fayl
r.Host So‘ralgan host "localhost:8080"
r.RemoteAddr Klient manzili "127.0.0.1:53422"
r.ContentLength Tana hajmi, bayt 57
r.Context() So‘rovga bog‘langan context —
r.PathValue("id") Yo‘ldagi parametr (Go 1.22+) "42"

Misol: so‘rov haqidagi ma’lumotni qaytarish

main.go
package main

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

func info(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Metod:      %s\n", r.Method)
    fmt.Fprintf(w, "Yo‘l:       %s\n", r.URL.Path)
    fmt.Fprintf(w, "Query:      %s\n", r.URL.RawQuery)
    fmt.Fprintf(w, "Host:       %s\n", r.Host)
    fmt.Fprintf(w, "Klient:     %s\n", r.RemoteAddr)
    fmt.Fprintf(w, "User-Agent: %s\n", r.UserAgent())
}

func main() {
    http.HandleFunc("/", info)
    log.Fatal(http.ListenAndServe(":8080", nil))
}
curl "http://localhost:8080/books?page=2"
Metod:      GET
Yo‘l:       /books
Query:      page=2
Host:       localhost:8080
Klient:     127.0.0.1:53422
User-Agent: curl/8.5.0

r.UserAgent() — r.Header.Get("User-Agent") ning qisqa shakli.

Sarlavhalarni o‘qish

r.Header turi — http.Header, aslida map[string][]string. Bitta sarlavha bir necha qiymatga ega bo‘lishi mumkin, shuning uchun qiymat slice ko‘rinishida saqlanadi.

ct := r.Header.Get("Content-Type")     // birinchi qiymat yoki ""
langs := r.Header.Values("Accept-Language") // barcha qiymatlar

Get sarlavha nomining harf katta-kichikligiga qaramaydi: content-type va Content-Type bir xil ishlaydi. Go nomlarni ichida kanonik ko‘rinishga keltiradi.

Warning

r.Header["content-type"] ko‘rinishida map’dan to‘g‘ridan-to‘g‘ri o‘qish ishlamaydi, chunki kalit Content-Type shaklida saqlangan. Doim Get yoki Values metodidan foydalaning.

So‘rov tanasini o‘qish

r.Body turi io.ReadCloser. Tana oqim (stream) ko‘rinishida keladi, shuning uchun uni faqat bir marta o‘qish mumkin.

body, err := io.ReadAll(r.Body)
if err != nil {
    http.Error(w, "tanani o‘qib bo‘lmadi", http.StatusBadRequest)
    return
}

Server tomonida r.Bodyni yopish shart emas, server buni o‘zi bajaradi. Klient tomonidagi resp.Body esa albatta yopilishi kerak.

Tana hajmini cheklash muhim. Aks holda kimdir bir necha gigabaytlik so‘rov yuborib, server xotirasini to‘ldirib qo‘yishi mumkin:

r.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1 MB

Limitdan oshsa, o‘qish xato qaytaradi. JSON tanani to‘g‘ri o‘qish Request body parsing darsida alohida ko‘riladi.

Forma ma’lumotlari

HTML forma application/x-www-form-urlencoded formatida yuboriladi. Uni o‘qish uchun r.ParseForm() va r.FormValue() ishlatiladi:

func login(w http.ResponseWriter, r *http.Request) {
    if err := r.ParseForm(); err != nil {
        http.Error(w, "forma noto‘g‘ri", http.StatusBadRequest)
        return
    }

    email := r.PostFormValue("email") // faqat tanadan
    page := r.FormValue("page")       // tanadan yoki URL query'dan
    fmt.Fprintln(w, email, page)
}

PostFormValue qiymatni faqat so‘rov tanasidan oladi. FormValue esa tanaga ham, URL query’ga ham qaraydi. Qiymat qayerdan kelishi muhim bo‘lsa, PostFormValue aniqroq.

Cookie’larni o‘qish

c, err := r.Cookie("session_id")
if errors.Is(err, http.ErrNoCookie) {
    http.Error(w, "session topilmadi", http.StatusUnauthorized)
    return
}
fmt.Fprintln(w, "Session:", c.Value)

r.Cookie cookie topilmasa, http.ErrNoCookie xatosini qaytaradi.

So‘rov contexti

ctx := r.Context()

Har bir so‘rovning o‘z contexti bor. Klient ulanishni uzsa yoki server to‘xtasa, bu context bekor bo‘ladi. Uni database so‘rovlari va tashqi API chaqiruvlariga uzatish kerak. Bu mavzu Context bilan ishlash darsida alohida ko‘riladi.

http.ResponseWriter — javob yozish

ResponseWriter — interfeys. Uning uchta metodi bor:

Header() http.Header         // javob sarlavhalari
WriteHeader(statusCode int)  // status kodni yuborish
Write([]byte) (int, error)   // tanani yozish

Javob yozish tartibi

HTTP javobi tarmoq orqali qat’iy tartibda yuboriladi: avval status qatori, keyin sarlavhalar, oxirida tana. Shu sababli handlerda ham tartib shunday bo‘lishi kerak:

1. w.Header().Set(...)    → sarlavhalarni tayyorlash
2. w.WriteHeader(kod)     → status va sarlavhalar yuboriladi
3. w.Write(...)           → tana yoziladi

Misol

main.go
package main

import (
    "log"
    "net/http"
)

func yaratish(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.Header().Set("Location", "/books/7")
    w.WriteHeader(http.StatusCreated)
    w.Write([]byte(`{"id":7,"title":"O‘tkan kunlar"}`))
}

func main() {
    http.HandleFunc("POST /books", yaratish)
    log.Fatal(http.ListenAndServe(":8080", nil))
}
curl -i -X POST http://localhost:8080/books
HTTP/1.1 201 Created
Content-Type: application/json
Location: /books/7
Date: Wed, 30 Sep 2026 10:00:00 GMT
Content-Length: 34

{"id":7,"title":"O‘tkan kunlar"}

curl -i javob sarlavhalarini ham ko‘rsatadi. Date va Content-Length sarlavhalarini Go o‘zi qo‘shdi.

Tartib buzilsa nima bo‘ladi?

Write yoki WriteHeader chaqirilgan zahoti sarlavhalar klientga yuboriladi. Shundan keyin ularni o‘zgartirib bo‘lmaydi.

func notogri(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("salom"))                         // 200 OK avtomatik yuborildi
    w.Header().Set("Content-Type", "application/json") // ta’sir qilmaydi
    w.WriteHeader(http.StatusCreated)                // ta’sir qilmaydi
}

Oxirgi qatorda Go serverning logiga ogohlantirish yozadi:

http: superfluous response.WriteHeader call from main.notogri (main.go:12)

Bu ogohlantirish ko‘pincha handlerda return unutilganini bildiradi:

if err != nil {
    http.Error(w, "xato", http.StatusBadRequest)
    // return yo‘q — kod davom etadi
}
w.WriteHeader(http.StatusOK) // superfluous WriteHeader

Info

WriteHeader chaqirilmasa, birinchi Write paytida avtomatik 200 OK yuboriladi. Content-Type o‘rnatilmagan bo‘lsa, Go tananing birinchi 512 baytiga qarab turini taxmin qiladi. API’da bu taxminga ishonmang, Content-Typeni doim o‘zingiz belgilang.

Set, Add va Del

w.Header().Set("Cache-Control", "no-store")     // mavjud qiymatni almashtiradi
w.Header().Add("Vary", "Accept-Encoding")       // yangi qiymat qo‘shadi
w.Header().Add("Vary", "Origin")                // endi ikki qiymat bor
w.Header().Del("X-Powered-By")                  // o‘chiradi

Tanani yozishning turli usullari

ResponseWriter io.Writer interfeysini qanoatlantiradi. Shu sababli unga turli yo‘llar bilan yozish mumkin:

w.Write([]byte("matn"))                  // baytlar
io.WriteString(w, "matn")                // satr
fmt.Fprintf(w, "Salom, %s", ism)         // formatlangan matn
json.NewEncoder(w).Encode(data)          // JSON
http.SetCookie(w, &http.Cookie{
    Name:     "session_id",
    Value:    "abc123",
    Path:     "/",
    MaxAge:   3600,
    HttpOnly: true,
    Secure:   true,
    SameSite: http.SameSiteLaxMode,
})

http.SetCookie javobga Set-Cookie sarlavhasini qo‘shadi. Sarlavha bo‘lgani uchun uni ham WriteHeader va Writedan oldin chaqirish kerak. Cookie parametrlarining ma’nosi Session va Token-based auth farqi darsida tushuntiriladi.

Hammasi bitta misolda

Quyidagi handler so‘rovni o‘qiydi, tekshiradi va turli holatlarda turli javob qaytaradi:

main.go
package main

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

func echo(w http.ResponseWriter, r *http.Request) {
    if ct := r.Header.Get("Content-Type"); !strings.HasPrefix(ct, "text/plain") {
        http.Error(w, "faqat text/plain qabul qilinadi", http.StatusUnsupportedMediaType)
        return
    }

    r.Body = http.MaxBytesReader(w, r.Body, 1024)
    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "tana juda katta yoki o‘qib bo‘lmadi", http.StatusRequestEntityTooLarge)
        return
    }

    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, "Siz %d bayt yubordingiz: %s\n", len(body), body)
}

func main() {
    http.HandleFunc("POST /echo", echo)
    log.Fatal(http.ListenAndServe(":8080", nil))
}
curl -X POST -H "Content-Type: text/plain" -d "Salom server" http://localhost:8080/echo
Siz 12 bayt yubordingiz: Salom server

Content-Type noto‘g‘ri bo‘lsa, 415 Unsupported Media Type qaytadi. Tana 1024 baytdan oshsa, 413 Request Entity Too Large qaytadi. Har bir xato holatidan keyin return bor. Shu sababli handler xatodan keyin davom etib, ikkinchi marta javob yozmaydi.

Xulosa

  • *http.Request ichida metod, URL, sarlavhalar, tana, cookie va context bor.
  • r.Body oqim, uni faqat bir marta o‘qish mumkin. Hajmini http.MaxBytesReader bilan cheklang.
  • http.ResponseWriterga yozish tartibi: sarlavhalar → WriteHeader → Write.
  • Birinchi Write yoki WriteHeaderdan keyin sarlavhalarni o‘zgartirib bo‘lmaydi.
  • Xato javobini yozgandan keyin return qilishni unutmang.

Keyingi darsda so‘rovni yo‘l va metodga qarab kerakli handlerga yo‘naltirish, ya’ni URL routing bilan shug‘ullanamiz.