IlmHamroh
JavaScript Full-stack/11-qism. Asinxron JavaScript va tarmoq18/32-dars20 daqiqa
Mundarija (38)

fetch POST: JSON, FormData va URLSearchParams bilan ma'lumot yuborish

Qisqacha: Ma'lumot yuborish uchun fetch ga ikkinchi argument — sozlamalar obyekti beriladi: fetch(url, { method: "POST", headers, body }). JSON yuborsangiz, body: JSON.stringify(obyekt) va "Content-Type": "application/json" sarlavhasini o'zingiz qo'yasiz. FormData va URLSearchParams da esa Content-Type ni brauzer o'zi qo'yadi — FormData uchun uni qo'lda yozish hatto xato.

Bu darsda

  • fetch ning method, headers va body sozlamalarini ishlata olasiz.
  • Obyektni JSON qilib yuborasiz va serverning 201, 400, 415, 422 javoblarini to'g'ri o'qiysiz.
  • FormData va URLSearchParams ni so'rov tanasi sifatida yuborasiz va farqini tushuntira olasiz.
  • Har bir tana turi uchun Content-Type qanday bo'lishini oldindan ayta olasiz.
  • Bron formasini haqiqiy serverga jo'natib, xatolarni maydonlar yonida ko'rsata olasiz.

Oldin bilishingiz kerak: fetch asoslari, FormData, Forma qiymatlarini o'qish va submit, Parametrda destructuring va options obyekti, HTTP metodlari va status kodlari.

1. Nega bu kerak?

Oldingi darsda «Bahor» menyusini serverdan oldik. Lekin sayt faqat o'qish uchun emas. Mehmon stol bron qiladi, izoh qoldiradi, buyurtma beradi — bu ma'lumotlar serverga borishi kerak.

FormData darsida bron formasini yig'ishni o'rgandik. Lekin u yerda "server"ni setTimeout bilan taqlid qilgan edik: 800 ms kutardik va "Bron qabul qilindi" deb yozardik. Haqiqatda hech narsa hech qayerga ketmagan edi.

Bugun shu formani haqiqiy serverga jo'natamiz. Buning uchun fetch ga uch narsani aytish kerak:

  1. Qanday so'rov — metod: POST, PUT, PATCH, DELETE.
  2. Nima yuboryapmiz — tana (body).
  3. U qanday formatda — Content-Type sarlavhasi.

Uchinchisi eng ko'p xato qilinadigan joy. Pochta o'xshatishi bilan: qutiga narsa soldingiz (tana), lekin ustiga "ichida shisha" deb yozmadingiz (Content-Type). Pochtachi qutini ochib nima qilishni bilmaydi.

2. fetch ning sozlamalar obyekti

2.1 Ikkinchi argument

Parametrda destructuring va options obyekti darsida fetch ni misol qilib aytgan edik: manzil — asosiy argument, method kabi sozlamalar — options obyektida; method berilmasa, "GET" ishlaydi. Endi uni o'zimiz yozamiz:

js
fetch(url, {
  method: "POST",          // so'rov turi
  headers: { ... },        // so'rov sarlavhalari
  body: ...,               // yuboriladigan tana
});

Uchala sozlama ham ixtiyoriy. Hech biri berilmasa — oddiy GET, oldingi darsdagidek.

2.2 Aks-sado: so'rovingizni ko'ring

Mashq API'sida maxsus endpoint bor — /api/mashq/aks. U so'rovni qayta ishlamaydi, balki o'zini qaytaradi: qaysi metod keldi, qaysi sarlavhalar, tanada nima bor. Ko'zgu kabi — fetch aslida nima yuborganini o'z ko'zingiz bilan ko'rasiz.

js
async function aksniKor() {
  const javob = await fetch("https://ilmhamroh.uz/api/mashq/aks", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ taom: "Osh", soni: 2 }),
  });
  console.log(await javob.json());
}

aksniKor();

Konsolda:

text
{
  metod: 'POST',
  yol: '/api/mashq/aks',
  sorov: {},
  sarlavhalar: { 'content-type': 'application/json', authorization: "yo'q" },
  tana: { taom: 'Osh', soni: 2 }
}

Server bizning obyektimizni qaytarib berdi: tana: { taom: 'Osh', soni: 2 }. sarlavhalar da u ikkitasini ko'rsatadi: content-type (biz qo'ydik) va authorization (kirish tokeni bormi — keyinroq, Autentifikatsiyali so'rovlar darsida). sorov — URL'dagi ?... parametrlari, bu safar bo'sh.

aks — bu dars davomidagi asosiy "tekshiruv asbobimiz". Shubha bo'lsa — so'rovni aks ga yuboring va nima ketganini ko'ring.

3. JSON yuborish

3.1 Uch shart

API'lar bilan eng ko'p ishlatiladigan format — JSON. JSON yuborish uchun uchta narsa kerak:

  1. method: "POST" (yoki PUT, PATCH).
  2. body: JSON.stringify(obyekt) — obyektni JSON matniga aylantirish (JSON asoslari). Tarmoq orqali obyekt emas, faqat matn yoki baytlar ketadi.
  3. headers: { "Content-Type": "application/json" } — serverga "bu matn JSON" deb aytish.

3.2 Haqiqiy bron

/api/mashq/bronlar — stol bron qilish endpoint'i. U ism, odamlar, sana va vaqt ni kutadi; telefon ixtiyoriy. Dilshod aka 10-oktabr kuni soat 19:00 ga to'rt kishilik stol bron qilyapti:

js
const API = "https://ilmhamroh.uz/api/mashq";

async function bronQil(bron) {
  const javob = await fetch(`${API}/bronlar`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(bron),
  });
  console.log(javob.status, javob.headers.get("Location"));
  console.log(await javob.json());
}

bronQil({
  ism: "Dilshod aka",
  odamlar: 4,
  sana: "2026-10-10",
  vaqt: "19:00",
});

Konsolda:

text
201 /api/mashq/bronlar/5897
{
  id: 5897,
  ism: 'Dilshod aka',
  odamlar: 4,
  sana: '2026-10-10',
  vaqt: '19:00',
  holat: 'qabul qilindi'
}

Javobni o'qiymiz:

  • 201 Created — "yaratildi". 200 dan farqi: server yangi narsa (bron) yaratdi (HTTP metodlari va status kodlari).
  • Location sarlavhasi — yangi bronning manzili. Ko'p API'lar 201 bilan birga shuni qaytaradi.
  • Tana — saqlangan bron: biz yuborgan maydonlar, server bergan id va holat.

id — 5897. Bu son tasodifiy emas: mashq serveri uni ma'lumotdan hisoblaydi. Bir xil bronni qayta yuborsangiz — yana 5897. Shu sababli darsdagi natija sizda ham aynan shunday chiqadi.

Maslahat: Mashq serveri bronni hech qayerga saqlamaydi — faqat tekshiradi va javob beradi. Location dagi /api/mashq/bronlar/5897 ni ochsangiz, 404 olasiz. Haqiqiy serverda esa u yerda saqlangan bron turardi.

3.3 Server rad etganda: 422

Endi xato ma'lumot yuboramiz: bir harfli ism, 0 ta odam, 30-fevral va kechasi 23:30:

js
async function xatoBron() {
  const url = "https://ilmhamroh.uz/api/mashq/bronlar";
  const javob = await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      ism: "D",
      odamlar: 0,
      sana: "2026-02-30",
      vaqt: "23:30",
    }),
  });
  const malumot = await javob.json();
  console.log(javob.status, malumot.xato);
  for (const [maydon, sabab] of Object.entries(malumot.maydonlar)) {
    console.log(`- ${maydon}: ${sabab}`);
  }
}

xatoBron();

Konsolda:

text
422 Ma'lumot noto'g'ri
- ism: 2 dan 40 gacha belgi bo'lishi kerak
- odamlar: 1 dan 20 gacha bo'lishi kerak
- sana: YYYY-MM-DD ko'rinishidagi haqiqiy sana bo'lishi kerak
- vaqt: 10:00 dan 22:00 gacha bo'lishi kerak

422 Unprocessable Content — "so'rov to'g'ri tuzilgan, lekin ma'lumot qoidaga zid". JSON yaroqli, server uni o'qidi — lekin qiymatlar noto'g'ri. Mashq serveri qaysi maydon nega xato ekanini maydonlar obyektida aytadi. Object.entries — obyektni [kalit, qiymat] juftliklariga aylantiradi (Obyekt bo'ylab yurish).

Bron vaqti 10:00 dan 22:00 gacha. «Bahor» 7:00 da ochilsa ham, ertalab stol bron qilinmaydi.

Esingizdami, fetch 422 da ham xato bermaydi (fetch asoslari). Shuning uchun biz status ni o'qib, xatoni o'zimiz ko'rsatdik. Bu xatolarni formada, har maydon yonida chiqarishni dars oxiridagi «Bron formasi» bo'limida qilamiz.

Tekshirib ko'ring: Brauzer formada required va min="1" bor. Server baribir tekshirishi kerakmi?

Javob

Ha, albatta. Brauzer tekshiruvi — foydalanuvchiga qulaylik. Uni DevTools'da o'chirish yoki so'rovni formasiz, to'g'ridan-to'g'ri fetch bilan yuborish mumkin — xuddi biz hozir qilganimizdek. Server esa hech kimga ishonmaydi va har bir so'rovni o'zi tekshiradi. 422 javobi — aynan shu tekshiruv natijasi.

4. Content-Type ni unutsangiz

4.1 415: "bu turdagi ma'lumot qabul qilinmaydi"

HTTP sarlavhalari darsida ogohlantirgan edik: "JSON yuborib, Content-Type ni unutish — server tanani JSON deb tanimaydi". Endi buni haqiqiy serverda ko'ramiz. Sarlavhani olib tashlaymiz:

js
async function sarlavhasiz() {
  const url = "https://ilmhamroh.uz/api/mashq/bronlar";
  const javob = await fetch(url, {
    method: "POST",
    body: JSON.stringify({ ism: "Dilshod aka", odamlar: 4 }),
  });
  console.log(javob.status, await javob.json());
}

sarlavhasiz();

Konsolda:

text
415 {
  xato: "Content-Type application/json bo'lishi kerak",
  keldi: 'text/plain'
}

415 Unsupported Media Type — "bu turdagi ma'lumotni qabul qilmayman". Va keldi: 'text/plain' — qiziq! Biz hech qanday tur aytmagan edik. Kim text/plain deb yozdi?

fetch ning o'zi. Tanaga satr bersangiz, fetch uni oddiy matn deb hisoblaydi va Content-Type: text/plain;charset=UTF-8 ni avtomatik qo'yadi. JSON.stringify natijasi — oddiy satr, fetch uning ichida JSON borligini bilmaydi. Shuning uchun JSON uchun sarlavhani har doim o'zingiz qo'yasiz.

4.2 stringify ni unutsangiz

Teskari xato — sarlavha bor, lekin obyekt to'g'ridan-to'g'ri body ga berilgan:

js
async function stringifySiz() {
  const javob = await fetch("https://ilmhamroh.uz/api/mashq/aks", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: { ism: "Dilshod aka" },
  });
  console.log(javob.status, await javob.json());
}

stringifySiz();

Konsolda:

text
400 { xato: 'JSON buzilgan' }

fetch obyektni JSON qilishni bilmaydi. U obyektni String() bilan satrga aylantirdi — natija "[object Object]" (Turlarni almashtirish). Serverga [object Object] degan matn bordi, "bu JSON" degan yorliq bilan. Server uni tahlil qila olmadi — 400 Bad Request.

Xato jim o'tib ketmagani yaxshi: server ogohlantirdi. Ba'zi serverlar bunday holatda bo'sh obyekt deb qabul qilib yuboradi — unda xatoni topish ancha qiyin.

Tekshirib ko'ring: Uchta xato javobni farqlang: 400, 415, 422. Qaysi biri "so'rovni umuman o'qiy olmadim", qaysi biri "o'qidim, lekin qiymatlar noto'g'ri"?

Javob

415 — "bu turdagi tanani qabul qilmayman" (yorliq noto'g'ri). 400 — "tana buzilgan, o'qiy olmadim" (yorliq JSON, ichi esa JSON emas). 422 — "o'qidim, tuzilishi to'g'ri, lekin qiymatlar qoidaga zid". Xatoni tuzatish joyi ham shundan bilinadi: 415 va 400 — kodingizda, 422 — foydalanuvchi kiritgan ma'lumotda.

5. Formadan yuborish: URLSearchParams va FormData

5.1 URLSearchParams — oddiy forma formati

HTML forma method="post" bilan o'zi yuborilganda, ma'lumot ism=Dilshod+aka&odamlar=4 ko'rinishida ketadi (Forma qanday ishlaydi). Bu format application/x-www-form-urlencoded deyiladi. JavaScript'da uni URLSearchParams obyekti yasaydi:

js
const parametrlar = new URLSearchParams({
  ism: "Dilshod aka",
  izoh: "piyozsiz & achchiq",
});
console.log(parametrlar.toString());

Konsolda:

text
ism=Dilshod+aka&izoh=piyozsiz+%26+achchiq

Bo'sh joy + ga, & belgisi esa %26 ga aylandi. Nega? & bu formatda juftliklarni ajratadi. Matn ichidagi & ni "kodlamasa", server achchiq ni alohida maydon deb o'ylardi. URLSearchParams buni o'zi qiladi — qo'lda satr yig'ishdan shuning uchun yaxshi. URLSearchParams ning boshqa imkoniyatlari — URL va URLSearchParams darsida.

Uni body ga beramiz. Content-Type ni yozmaymiz:

js
async function formaFormatida() {
  const url = "https://ilmhamroh.uz/api/mashq/bronlar";
  const javob = await fetch(url, {
    method: "POST",
    body: new URLSearchParams({
      ism: "Dilshod aka",
      odamlar: "4",
      sana: "2026-10-10",
      vaqt: "19:00",
    }),
  });
  console.log(javob.status, (await javob.json()).id);
}

formaFormatida();

Konsolda:

text
201 5897

O'sha bron, o'sha id. fetch URLSearchParams ni tanidi va Content-Type: application/x-www-form-urlencoded;charset=UTF-8 ni o'zi qo'ydi. E'tibor bering: odamlar — "4", satr. Bu formatda hamma qiymat satr bo'ladi; mashq serveri "4" ni songa o'zi aylantiradi.

5.2 FormData — fayllar bilan

FormData darsida "fayl ham FormData ning ichida qoladi; uni fetch bilan jo'natganda brauzer kerakli multipart/form-data shaklini o'zi tanlaydi" degan edik. Sinaymiz. Formadagi fayl maydonini kodda File obyekti bilan almashtiramiz — 5 baytli izoh.txt:

js
async function faylBilan() {
  const malumot = new FormData();
  malumot.append("ism", "Dilshod aka");
  malumot.append(
    "rasm",
    new File(["salom"], "izoh.txt", { type: "text/plain" }),
  );

  const javob = await fetch("https://ilmhamroh.uz/api/mashq/aks", {
    method: "POST",
    body: malumot,
  });
  const aks = await javob.json();
  console.log(aks.sarlavhalar["content-type"].split(";")[0]);
  console.log(aks.tana);
}

faylBilan();

Konsolda:

text
multipart/form-data
[
  { nom: 'ism', qiymat: 'Dilshod aka' },
  { nom: 'rasm', fayl: 'izoh.txt', hajm: 5, tur: 'text/plain' }
]

multipart/form-data — "ko'p qismli" format: tana bir nechta qismga bo'linadi, har qism — alohida maydon, fayllar esa o'z nomi va turi bilan. Aks-sado har qismni ko'rsatdi: oddiy maydon qiymat bilan, fayl — nomi, hajmi (5 bayt) va turi bilan.

Nega split(";")[0]? To'liq sarlavha multipart/form-data; boundary=----... ko'rinishida. Boundary (chegara) — qismlarni ajratuvchi tasodifiy satr, har so'rovda yangi. Uni chiqarmadik, chunki har safar boshqacha.

File — fayl obyekti, File va Blob darsida batafsil. Haqiqiy formada u <input type="file"> dan keladi va new FormData(forma) uni o'zi qo'shadi.

5.3 FormData da Content-Type ni qo'lda yozmang

Ko'p uchraydigan xato: "multipart yuboryapman, demak sarlavhada aytishim kerak":

js
async function qoldaYozilgan() {
  const malumot = new FormData();
  malumot.append("ism", "Dilshod aka");
  const javob = await fetch("https://ilmhamroh.uz/api/mashq/aks", {
    method: "POST",
    headers: { "Content-Type": "multipart/form-data" },
    body: malumot,
  });
  console.log(javob.status, await javob.json());
}

qoldaYozilgan();

Konsolda:

text
400 { xato: "multipart/form-data buzilgan (boundary yo'q)" }

Siz yozgan sarlavha fetch nikini almashtirdi — va unda boundary yo'q. Server qismlar qayerda bo'linishini bilmaydi. Qoida: FormData va URLSearchParams da Content-Type ni yozmang — fetch uni to'g'ri, boundary bilan qo'yadi.

5.4 Server FormData ni qabul qilmasa

/bronlar faqat JSON yoki forma formatini (urlencoded) qabul qiladi. Unga FormData yuborsangiz — 415 va keldi: 'multipart/form-data'. Bunday holatda ikki yo'l bor:

js
// 1) FormData → URLSearchParams (fayl bo'lmasa)
body: new URLSearchParams(new FormData(forma))

// 2) FormData → obyekt → JSON
body: JSON.stringify(Object.fromEntries(new FormData(forma)))

Ikkinchi yo'lda bitta tuzoq bor: formadan kelgan hamma qiymat — satr. odamlar: "4" JSON'da ham satr bo'lib ketadi va mashq serveri 422 qaytaradi: odamlar: "butun son bo'lishi kerak". Forma formatida server satrni o'zi songa aylantiradi, JSON'da esa turlar qat'iy. Shuning uchun JSON'dan oldin sonlarni Number() bilan aylantiring — natijadagi kod shunday qiladi.

6. Content-Type qoidalari — bitta jadvalda

body ga nima berasiz fetch qo'yadigan Content-Type O'zingiz yozasizmi?
JSON.stringify(...) (satr) text/plain;charset=UTF-8 ha: application/json
URLSearchParams application/x-www-form-urlencoded;charset=UTF-8 yo'q
FormData multipart/form-data; boundary=... yo'q, yozmang
Blob / File uning type i odatda yo'q

Qaysi formatni tanlashni sxemadan ko'ring:

flowchart TD
  A{"Fayl bormi?"} -->|"ha"| F["FormData<br/>(Content-Type yozmang)"]
  A -->|"yo'q"| B{"Server nimani<br/>kutadi?"}
  B -->|"JSON (ko'p API)"| J["JSON.stringify +<br/>application/json"]
  B -->|"oddiy forma"| U["URLSearchParams<br/>(Content-Type yozmang)"]

Zamonaviy API'larning ko'pchiligi JSON kutadi. Fayl yuklash — deyarli doim FormData. Qaysi birini kutishini API hujjatidan bilasiz.

Tekshirib ko'ring: body: "osh" bilan POST yuborildi, sarlavhasiz. Server qanday Content-Type ko'radi?

Javob

text/plain;charset=UTF-8. Tana — satr, fetch satr uchun shu turni avtomatik qo'yadi. aks ga yuborsangiz, tana: 'osh' va shu sarlavhani qaytaradi.

7. Boshqa metodlar: PUT, PATCH, DELETE

7.1 HTML forma qila olmaydigan ish

Forma qanday ishlaydi darsida ogohlantirgan edik: method="put" yozsangiz, brauzer jimgina GET yuboradi — HTML forma faqat GET va POST biladi. fetch esa istalgan metodni yubora oladi:

js
const API = "https://ilmhamroh.uz/api/mashq";
const json = { "Content-Type": "application/json" };

async function metodlar() {
  const sorovlar = [
    { method: "PUT", headers: json, body: '{"narx":36000}' },
    { method: "PATCH", headers: json, body: '{"narx":36000}' },
    { method: "DELETE" },
  ];
  for (const sozlama of sorovlar) {
    const javob = await fetch(`${API}/aks`, sozlama);
    const aks = await javob.json();
    console.log(aks.metod, aks.tana);
  }
}

metodlar();

Konsolda:

text
PUT { narx: 36000 }
PATCH { narx: 36000 }
DELETE null

Ma'nosini HTTP metodlari darsidan eslang: PUT — butunlay almashtirish, PATCH — qisman o'zgartirish, DELETE — o'chirish. DELETE odatda tanasiz. So'rovlar for...of ichida await bilan ketma-ket ketdi — shuning uchun konsol tartibi har doim bir xil (Ketma-ket va parallel bajarish).

Bu metodlarni haqiqiy ma'lumot bilan — vazifalar ro'yxatini yaratish, o'zgartirish, o'chirish — API klient qatlami darsida ishlatamiz.

7.2 Metod nomini katta harfda yozing

fetch get, post, put, delete, head, options ni o'zi katta harfga aylantiradi. patch ni esa aylantirmaydi — standart shunday. method: "patch" bilan so'rov serverga kichik harfda boradi. Node 24 buni sezib, ogohlantiradi:

text
[UNDICI-FETCH-patch] Warning: Using `patch` is highly likely to result in a `405 Method Not Allowed`. `PATCH` is much more likely to succeed.

Tarjimasi: "patch ishlatish deyarli aniq 405 Method Not Allowed ga olib keladi. PATCH ancha ko'proq ishlaydi." Mashq serverimizda esa undan ham oldin, veb-server darajasida, 400 Bad Request keladi. Brauzerda esa so'rov CORS tekshiruvidan o'tmaydi (CORS mijoz tomondan). Qoida: metodni doim katta harfda yozing: "PATCH".

7.3 GET ga tana berib bo'lmaydi

js
async function sinab() {
  await fetch("https://ilmhamroh.uz/api/mashq/aks", {
    method: "GET",
    body: "osh",
  });
}

sinab();

Node 24 da: TypeError: Request with GET/HEAD method cannot have body. Chrome'da xabar oldiga Failed to execute 'fetch' on 'Window': qo'shiladi. Tarjimasi: "GET yoki HEAD so'rovida tana bo'lishi mumkin emas." GET bilan qo'shimcha ma'lumot kerak bo'lsa — URL parametrlariga (?turkum=taom) qo'ying.

Tekshirib ko'ring: Sardor oshning narxini 36 000 ga o'zgartirmoqchi, taomning qolgan maydonlari (nomi, turkumi) o'z joyida qolsin. PUT mi yoki PATCH mi? method: "patch" deb yozsa nima bo'ladi?

Javob

PATCH — faqat bitta maydon o'zgaryapti, bu qisman o'zgartirish. PUT esa butun taomni almashtiradi: unga hamma maydonni yuborish kerak bo'lardi. Kichik harfli "patch" ni esa fetch katta harfga aylantirmaydi. Natija: Node ogohlantiradi va server 400/405 qaytaradi, brauzerda esa CORS xatosi chiqadi.

8. Bron formasi — haqiqiy serverga

Endi hammasini birlashtiramiz. Forma qiymatlarini o'qish va submit darsidagi naqsh: submit → preventDefault → ma'lumotni yig'ish → serverga fetch → natijani ko'rsatish. Formada qiymatlar oldindan to'ldirilgan. «Bron qilish» ni bosing, keyin odamlar sonini 0 ga, vaqtni 23:30 ga o'zgartirib, yana bosing:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  label { display: block; margin: 0.4rem 0; }
  input { font: inherit; padding: 0.2rem 0.4rem; }
  button { font: inherit; padding: 0.4rem 0.8rem; }
  .xato { color: #b3261e; font-size: 0.9rem; margin-left: 0.4rem; }
  #natija { font-weight: 600; }
</style>
<form id="bron" novalidate>
  <label>Ism <input name="ism" value="Dilshod aka"></label>
  <label>Odamlar
    <input name="odamlar" type="number" value="4"></label>
  <label>Sana
    <input name="sana" type="date" value="2026-10-10"></label>
  <label>Vaqt <input name="vaqt" type="time" value="19:00"></label>
  <button type="submit">Bron qilish</button>
</form>
<p id="natija" role="status"></p>
<script>
  const API = "https://ilmhamroh.uz/api/mashq";
  const forma = document.getElementById("bron");
  const natija = document.getElementById("natija");

  function xatolarniTozala() {
    for (const s of forma.querySelectorAll(".xato")) s.remove();
  }

  function xatoniKorsat(maydon, matn) {
    const input = forma.elements[maydon];
    const span = document.createElement("span");
    span.className = "xato";
    span.textContent = matn;
    input.after(span);
  }

  forma.addEventListener("submit", async (event) => {
    event.preventDefault();
    const tugma = event.submitter;
    const bron = Object.fromEntries(new FormData(forma));
    bron.odamlar = Number(bron.odamlar);

    tugma.disabled = true;
    natija.textContent = "Yuborilmoqda...";
    xatolarniTozala();
    try {
      const javob = await fetch(`${API}/bronlar`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(bron),
      });
      const malumot = await javob.json();
      if (javob.status === 201) {
        natija.textContent = `Bron qabul qilindi: №${malumot.id}`;
        console.log("201, id:", malumot.id);
      } else if (javob.status === 422) {
        natija.textContent = "Ma'lumotni tekshiring.";
        const xatolar = malumot.maydonlar;
        for (const [maydon, matn] of Object.entries(xatolar)) {
          xatoniKorsat(maydon, matn);
        }
        console.log("422:", Object.keys(xatolar).join(", "));
      } else {
        natija.textContent = `Server xatosi: ${javob.status}`;
      }
    } catch (xato) {
      natija.textContent = "Internet bilan muammo. Qayta urining.";
    } finally {
      tugma.disabled = false;
    }
  });
</script>

Nimani ko'rdingiz:

  • Birinchi bosishda: "Yuborilmoqda..." va bir lahzadan keyin "Bron qabul qilindi: №5897". Konsolda 201, id: 5897.
  • 0 odam va 23:30 bilan: maydonlar yonida qizil xabarlar — "1 dan 20 gacha bo'lishi kerak", "10:00 dan 22:00 gacha bo'lishi kerak". Konsolda 422: odamlar, vaqt.
  • Yuborish davomida tugma o'chiq — FormData darsidagi ikki marta bosishdan himoya. Endi u setTimeout ni emas, haqiqiy server javobini kutadi. finally — javob qanday bo'lmasin, tugmani qayta yoqadi.

Kodda uchta muhim qaror bor:

  • bron.odamlar = Number(bron.odamlar) — formadan kelgan "4" ni songa aylantirish. Bu qatorni o'chirib ko'ring: server odamlar: butun son bo'lishi kerak deydi.
  • novalidate — brauzerning o'z tekshiruvini o'chirdik, server xatolarini ko'rishingiz uchun. Haqiqiy saytda ikkalasi ham bo'ladi: brauzer tekshiruvi — tez yordam, server tekshiruvi — kafolat.
  • 422 va boshqa xatolar alohida: 422 — foydalanuvchi tuzatadi; 500 yoki tarmoq xatosi — "keyinroq urinib ko'ring".

Xato matnlari textContent bilan qo'yildi — server matni HTML sifatida o'qilmaydi (Kontentni o'zgartirish va XSS xavfi).

9. Hujumchi nigohi

1. Brauzer tekshiruvini aylanib o'tish. Hujumchi formani ishlatmaydi. U DevTools konsolida o'zi fetch yozadi yoki curl bilan odamlar: -5, ism: "<script>..." yuboradi. required, min, maxlength — hech biri ishlamaydi. Qarshi chora: server har maydonni o'zi tekshiradi — mashq serveri buni 422 bilan ko'rsatdi. Brauzer tekshiruvi — faqat qulaylik, himoya emas.

2. Maxfiy ma'lumotni URL'da yuborish. fetch("/kirish?parol=bahor123") — GET va parametr URL'da. URL esa brauzer tarixida, server loglarida, Referer sarlavhasida qoladi. Qarshi chora: parol, karta raqami, token — faqat POST tanasida (va HTTPS orqali). Kirish so'rovini Autentifikatsiyali so'rovlar darsida aynan shunday yuboramiz.

10. Ko'p uchraydigan xatolar

10.1 JSON uchun Content-Type yo'q

Server 415 qaytaradi yoki tanani bo'sh deb o'qiydi. Tuzatish: headers: { "Content-Type": "application/json" }.

10.2 JSON.stringify yo'q

Tanaga [object Object] ketadi — 400. Tuzatish: body: JSON.stringify(obyekt).

10.3 FormData ga qo'lda Content-Type

boundary yo'qoladi — server tanani bo'la olmaydi. Tuzatish: sarlavhani olib tashlang.

10.4 Formadagi sonlar — satr

Object.fromEntries(new FormData(forma)) da hamma qiymat satr. JSON API'ga yuborishdan oldin sonlarni Number() bilan, belgilash katagini === "on" bilan aylantiring.

10.5 Javobni tekshirmasdan "muvaffaqiyat" deyish

js
await fetch(url, { method: "POST", body });
natija.textContent = "Bron qabul qilindi!"; // ❌ 422 bo'lsa ham

fetch 422 da ham bajariladi. Tuzatish: javob.ok yoki aniq status ni tekshiring, keyin xabar chiqaring.

10.6 patch kichik harfda

Node'da ogohlantirish va 400/405, brauzerda CORS xatosi. Tuzatish: method: "PATCH".

11. Mashqlar

1-mashq (oson): Buyurtmani aks ettiring

/api/mashq/aks ga POST bilan { taom: "Manti", soni: 3, stol: 5 } ni JSON qilib yuboring. Javobdan faqat tana ni chiqaring. Ishora: uchta sozlama — method, headers, body.

Yechim
js
async function buyurtmaYubor() {
  const javob = await fetch("https://ilmhamroh.uz/api/mashq/aks", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ taom: "Manti", soni: 3, stol: 5 }),
  });
  const aks = await javob.json();
  console.log(aks.tana);
}

buyurtmaYubor();

Konsolda:

text
{ taom: 'Manti', soni: 3, stol: 5 }

Server obyektni o'zgarishsiz qaytardi. Sonlar son bo'lib qoldi — JSON turlarni saqlaydi.

2-mashq (o'rta): bronYubor funksiyasi

bronYubor(bron) funksiyasini yozing. U /bronlar ga JSON yuborsin va natijani bitta shaklda qaytarsin: muvaffaqiyatda { ok: true, id }, 422 da { ok: false, xatolar } (maydonlar obyekti), boshqa statusda Error tashlasin. Ikki holatni sinang: to'g'ri bron va odamlar: 25.

Yechim
js
async function bronYubor(bron) {
  const url = "https://ilmhamroh.uz/api/mashq/bronlar";
  const javob = await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(bron),
  });
  const malumot = await javob.json();
  if (javob.status === 201) return { ok: true, id: malumot.id };
  if (javob.status === 422) {
    return { ok: false, xatolar: malumot.maydonlar };
  }
  throw new Error(`Kutilmagan javob: ${javob.status}`);
}

async function sina() {
  const asos = {
    ism: "Dilshod aka",
    sana: "2026-10-10",
    vaqt: "19:00",
  };
  console.log(await bronYubor({ ...asos, odamlar: 4 }));
  console.log(await bronYubor({ ...asos, odamlar: 25 }));
}

sina();

Konsolda:

text
{ ok: true, id: 5897 }
{ ok: false, xatolar: { odamlar: "1 dan 20 gacha bo'lishi kerak" } }

Funksiya chaqiruvchidan HTTP tafsilotlarini yashiradi: u faqat "bo'ldimi, bo'lmasa nima xato" degan javobni oladi. Yozuvdagi { ...asos, odamlar: 4 } — umumiy maydonlarga bittasini qo'shish (spread). 422 — kutilgan holat (foydalanuvchi xatosi), shuning uchun u oddiy natija sifatida qaytadi; kutilmagan status esa throw. Bunday "API funksiyalari"ni API klient qatlami darsida bitta modulga yig'amiz.

3-mashq (qiyin): Uch formatni solishtiring

Bitta bron ma'lumotini /aks ga uch xil yuboring: JSON, URLSearchParams va FormData. Har biri uchun Content-Type ning ; gacha qismini va odamlar qiymatining turini (typeof) chiqaring. Faylni kurs/mashqlar/11/18-yuborish/formatlar.mjs ga saqlang.

Ishora: FormData uchun aks javobidagi tana — massiv: { nom, qiymat } qismlari. Undan odamlar ni find bilan toping (find).

Yechim
js
const AKS = "https://ilmhamroh.uz/api/mashq/aks";
const bron = { ism: "Dilshod aka", odamlar: 4 };
const JSON_SARLAVHA = { "Content-Type": "application/json" };

function tanalar() {
  const fd = new FormData();
  fd.append("ism", bron.ism);
  fd.append("odamlar", String(bron.odamlar));
  return [
    ["JSON", JSON.stringify(bron), JSON_SARLAVHA],
    ["URLSearchParams", new URLSearchParams(fd)],
    ["FormData", fd],
  ];
}

async function solishtir() {
  for (const [nom, body, headers] of tanalar()) {
    const javob = await fetch(AKS, { method: "POST", headers, body });
    const aks = await javob.json();
    const tur = aks.sarlavhalar["content-type"].split(";")[0];
    const odamlar = Array.isArray(aks.tana)
      ? aks.tana.find((q) => q.nom === "odamlar").qiymat
      : aks.tana.odamlar;
    console.log(`${nom}: ${tur}, odamlar — ${typeof odamlar}`);
  }
}

solishtir();

Konsolda:

text
JSON: application/json, odamlar — number
URLSearchParams: application/x-www-form-urlencoded, odamlar — string
FormData: multipart/form-data, odamlar — string

Faqat JSON sonni son sifatida saqlaydi. Forma formatlarida hamma narsa matn — sonlarga aylantirish serverning ishi. headers uchinchi elementi bo'lmagan qatorlarda undefined — fetch buni "sarlavha yo'q" deb tushunadi.

bash
git add 11/18-yuborish/formatlar.mjs
git commit -m "11/18: uch xil tana formatini solishtir"

12. Real ishda

  • Har bir forma. Ro'yxatdan o'tish, izoh, savatga qo'shish, Click/Payme to'lov sahifasiga yo'naltirishdan oldingi buyurtma — hammasi POST + JSON. DevTools → Network → so'rovni bosing → Payload tabida yuborilgan tanani ko'rasiz.
  • Fayl yuklash. Avatar, hujjat, rasm — FormData bilan. Yuklash progressini ko'rsatish — Fayl yuklash va progress darsida.
  • Xatolar shartnomasi. Jamoalar "422 da maydonlar obyekti keladi" kabi qoidani oldindan kelishib oladi. Shunda frontend har xatoni maydon yonida ko'rsata oladi. Backend tomonini Express va NestJS qismlarida o'zingiz yozasiz.
  • Intervyu. "FormData yuborganda Content-Type ni qo'yish kerakmi?" — "Yo'q, boundary ni brauzer qo'yadi". "400 va 422 farqi?" — bugungi jadval.

Xulosa

  • Ma'lumot yuborish: fetch(url, { method, headers, body }). method bo'lmasa — GET.
  • JSON: body: JSON.stringify(obyekt) + "Content-Type": "application/json" — sarlavhani o'zingiz qo'yasiz.
  • URLSearchParams va FormData uchun Content-Type ni fetch o'zi qo'yadi; FormData da uni qo'lda yozish boundary ni buzadi.
  • Javoblar: 201 — yaratildi (+ Location), 400 — tana buzilgan, 415 — tur noto'g'ri, 422 — qiymatlar noto'g'ri.
  • Formadan kelgan qiymatlar — satr; JSON'dan oldin sonlarni aylantiring. Server baribir hammasini o'zi tekshiradi.
  • Metod nomlari — katta harfda; GET ga tana berilmaydi.

Keyingi dars: Request, Response, Headers va fetch opsiyalari — fetch ning ichidagi uchta obyekt va kamroq ko'rinadigan, lekin muhim sozlamalar: cookie, kesh, yo'naltirish va sahifa yopilganda ham yetib boradigan so'rov.

Manbalar

  • MDN: "Using the Fetch API" (Setting a body, Setting headers), "FormData", "URLSearchParams" — developer.mozilla.org
  • WHATWG Fetch Standard: "BodyInit", "extract a body" — fetch.spec.whatwg.org
  • RFC 9110: HTTP Semantics — 201, 400, 415, 422 status kodlari
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
fetch POST: JSON, FormData va URLSearchParams bilan ma'lumot yuborish — IlmHamroh