Request body parsing
POST va PUT so‘rovlarida klient ma’lumotni so‘rov tanasida (request body) JSON ko‘rinishida yuboradi. Uni Go struct’iga o‘qish bitta qatorda bajariladi:
Oddiy ko‘rinadi. Lekin server internetga ochiq bo‘lsa, unga faqat yaxshi niyatli klientlar murojaat qilmaydi. Kimdir 5 GB hajmli tana yuboradi, kimdir JSON o‘rniga matn yuboradi, kimdir maydon nomini xato yozadi. Bu darsda shu holatlarning barchasini to‘g‘ri qayta ishlaydigan decodeJSON funksiyasini bosqichma-bosqich yozamiz.
Oddiy usulning muammolari
func createBook(w http.ResponseWriter, r *http.Request) {
var in BookInput
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// ...
}
Bu kodda beshta muammo bor:
- Hajm cheklanmagan. Katta tana server xotirasini to‘ldirishi mumkin.
- Notanish maydonlar jimgina tashlab yuboriladi. Klient
"titel"deb xato yozsa,Titlebo‘sh qoladi va xato sababi tushunarsiz bo‘ladi. - Ortiqcha ma’lumot tekshirilmaydi.
{"title":"A"}{"title":"B"}yoki{"title":"A"} qandaydir matnham xatosiz qabul qilinadi, chunkiDecodefaqat birinchi JSON qiymatni o‘qiydi. - Xato xabarlari texnik. Klient
json: cannot unmarshal string into Go struct field BookInput.year of type intkabi xabarni oladi. Unda Go’dagi tur nomlari ham ko‘rinib qoladi. Content-Typetekshirilmaydi.
Endi ularni birma-bir hal qilamiz.
1. Hajmni cheklash
http.MaxBytesReader tanani o‘rab oladi. Belgilangan chegaradan ko‘p o‘qishga urinilsa, *http.MaxBytesError xatosini qaytaradi. Bundan tashqari, serverga ulanishni yopish kerakligi haqida signal beradi.
Chegara endpointga qarab tanlanadi. Oddiy JSON so‘rovlar uchun 1 MB yetarli bo‘ladi. Fayl yuklash endpointlari uchun esa kattaroq chegara belgilanadi.
2. Notanish maydonlarni rad etish
Endi {"titel": "Sarob"} so‘rovi json: unknown field "titel" xatosini beradi. Klient dasturchisi xatoni darhol ko‘radi.
Info
DisallowUnknownFields API’ni qat’iyroq qiladi. Lekin bir kamchiligi bor: kelajakda klient yangi maydon yuborsa-yu, server hali yangilanmagan bo‘lsa, so‘rov rad etiladi. Ko‘pchilik ichki va ommaviy API’lar uchun qat’iylik foydaliroq. Shunday bo‘lsa-da, bu qarorni ongli ravishda qabul qiling.
3. Ortiqcha ma’lumotni tekshirish
Decode birinchi JSON qiymatni o‘qigach to‘xtaydi. Tanada boshqa ma’lumot qolmaganini tekshirish uchun yana bir marta o‘qishga urinamiz:
if err := dec.Decode(&struct{}{}); !errors.Is(err, io.EOF) {
return errors.New("tanada faqat bitta JSON obyekt bo‘lishi kerak")
}
Ikkinchi Decode io.EOF qaytarsa, tana tugagan. Boshqa har qanday natija tanada ortiqcha ma’lumot borligini bildiradi.
4. Xatolarni tushunarli qilish
encoding/json qaytaradigan xatolar bir necha turga bo‘linadi. Ularni errors.As bilan ajratib, har biriga mos xabar tayyorlaymiz:
| Xato | Qachon yuz beradi | Klientga xabar |
|---|---|---|
*json.SyntaxError |
JSON sintaksisi buzilgan | "JSON 15-baytda noto‘g‘ri" |
io.ErrUnexpectedEOF |
JSON oxiriga yetmay uzilgan | "JSON to‘liq emas" |
*json.UnmarshalTypeError |
Maydon turi mos emas | "year maydoni son bo‘lishi kerak" |
io.EOF |
Tana bo‘sh | "so‘rov tanasi bo‘sh" |
*http.MaxBytesError |
Tana juda katta | "tana 1048576 baytdan oshmasligi kerak" |
json: unknown field |
Notanish maydon | "notanish maydon: titel" |
UnmarshalTypeErrorning Field maydonida xato qaysi JSON maydonida yuz bergani yoziladi. Ichki maydonlar uchun u author.name ko‘rinishida bo‘ladi.
Notanish maydon xatosi alohida tur sifatida e’lon qilinmagan. Uni faqat xato matni orqali aniqlash mumkin. Bu encoding/json paketining ma’lum cheklovi.
5. Content-Typeni tekshirish
ct := r.Header.Get("Content-Type")
mediaType, _, _ := mime.ParseMediaType(ct)
if mediaType != "application/json" {
// 415 Unsupported Media Type
}
mime.ParseMediaType application/json; charset=utf-8 ko‘rinishidagi qiymatdan faqat application/json qismini ajratib oladi.
Bu tekshiruv qat’iylikdan tashqari xavfsizlik uchun ham foydali. Brauzer boshqa saytdan application/json turidagi so‘rovni oldindan ruxsat so‘ramasdan (CORS preflight’siz) yubora olmaydi. Content-Type qat’iy tekshirilsa, CSRF hujumlarining bir turi to‘silib qoladi. Bu haqda CSRF & XSS darsida batafsil gaplashamiz.
Tayyor decodeJSON funksiyasi
Barcha qismlarni birlashtiramiz:
| main.go | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
Sinab ko‘rish
Qulaylik uchun curl buyrug‘ini qisqartiramiz:
curl -X POST $URL -H "$CT" -d '{"title":"Sarob","author":"Qahhor","year":1937}'
curl -X POST $URL -d '{"title":"Sarob"}'
curl -X POST $URL -H "$CT" -d '{"title":"Sarob",}'
curl -X POST $URL -H "$CT" -d '{"title":"Sarob"'
curl -X POST $URL -H "$CT" -d '{"title":"Sarob","year":"1937"}'
curl -X POST $URL -H "$CT" -d '["Sarob"]'
curl -X POST $URL -H "$CT" -d '{"titel":"Sarob"}'
curl -X POST $URL -H "$CT" -d '{"title":"A"}{"title":"B"}'
curl -X POST $URL -H "$CT"
Qabul qilindi: {Title:Sarob Author:Qahhor Year:1937}
Content-Type application/json bo‘lishi kerak
JSON 18-baytda noto‘g‘ri
JSON to‘liq emas
year maydonining turi noto‘g‘ri
JSON obyekt kutilgan edi
notanish maydon: "titel"
tanada faqat bitta JSON obyekt bo‘lishi kerak
so‘rov tanasi bo‘sh
Har bir xato holatida klient nima noto‘g‘ri ekanini aniq biladi. Go ichki tuzilishiga oid tafsilotlar esa tashqariga chiqmaydi.
Katta tanani tekshirish uchun 2 MB hajmli fayl yaratib yuboramiz:
python3 -c 'print("{\"title\":\"" + "a"*2_000_000 + "\"}")' > big.json
curl -X POST $URL -H "$CT" --data-binary @big.json
Tekshiruvlar tartibi
decodeJSON faqat sintaktik tekshiruvni bajaradi: JSON to‘g‘rimi va turlar mosmi? Ma’noviy tekshiruv esa undan keyin keladi: title bo‘sh emasmi, year mantiqiy oraliqdami, email to‘g‘ri formatdami?
1. Content-Type va hajm → decodeJSON
2. JSON sintaksisi va turlar → decodeJSON
3. Qiymatlar mazmuni → validatsiya
4. Biznes qoidalari → service
Uchinchi qadam uchun qo‘lda if yozish o‘rniga validator kutubxonasi ishlatiladi.
Xulosa
- Tana hajmini doim
http.MaxBytesReaderbilan cheklang. DisallowUnknownFieldsxato yozilgan maydonlarni aniqlashga yordam beradi.- Ikkinchi
Decodechaqiruvi tanada ortiqcha ma’lumot yo‘qligini tekshiradi. json.SyntaxError,json.UnmarshalTypeErrorvahttp.MaxBytesErrorxatolarini tushunarli xabarlarga aylantiring.Content-Typeqat’iy tekshirilsa, API aniqroq va xavfsizroq bo‘ladi.- Barcha qoidalarni bitta
decodeJSONfunksiyasida jamlang va barcha handlerlarda qayta ishlating.
Keyingi darsda javob tomoniga o‘tamiz va JSON javoblarni izchil formatda qaytarishni o‘rganamiz.