IlmHamroh
JavaScript Full-stack/25-qism. HTTP, Express va REST API12/51-dars18 daqiqa
Mundarija (26)

Express middleware: next(), app.use, tartib qoidasi, xato middleware va request lifecycle

Qisqacha: Middleware — (req, res, next) funksiyasi. So'rov ulardan ro'yxat tartibida o'tadi. Har biri uch ishdan birini qiladi: javob beradi, next() bilan keyingisiga uzatadi yoki next(xato) / throw bilan xato zanjiriga o'tkazadi. Tartib hammasini hal qiladi: express.json() marshrutdan keyin qo'yilsa, req.body bo'sh qoladi. 404 middleware — oxirida. Xato middleware'i — 4 argumentli (xato, req, res, next) va eng oxirida. Express 5 async ishlovchidagi throw ni o'zi ushlaydi. Xato bo'lganda oddiy middleware'lar o'tkazib yuboriladi — biz buni iz bilan ko'rdik.

Bu darsda

  • Middleware zanjiri qanday ishlashini 20 qatorli o'z modelingiz bilan ko'rasiz.
  • app.use, yo'l darajasidagi, router va marshrut darajasidagi middleware'larni farqlaysiz.
  • next(), next('route'), next('router') va next(xato) ning farqini iz bilan ko'rasiz.
  • JSON 404 va JSON xato ishlovchi yozasiz; body-parser xatolarini (400, 413) ushlaysiz.
  • Express bermaydigan 405 ni .all() bilan qo'shasiz.

Oldin bilishingiz kerak: req obyekti, res obyekti, Express'ga kirish, Error obyekti va xato turlari.

1. Nega bu kerak?

Har so'rov uchun bir xil ishlar bor: logga yozish, tanani o'qish, sarlavhalar qo'yish, so'rov ID'si berish, xatoni JSON'ga aylantirish. Ularni har marshrut ichiga ko'chirib yozsak — 20 ta marshrutda 20 nusxa. Bittasini unutsak — xavfsizlik teshigi.

Middleware bu ishlarni bitta joyga yig'adi va hamma so'rovga qo'llaydi. Express'ning deyarli hammasi middleware: express.json(), express.static(), router ham, hatto marshrut ishlovchisi ham.

O'xshatish: aeroportdagi yo'l. Kirishda pasport tekshiruvi, keyin bagaj, keyin xavfsizlik ramkasi, keyin chiqish eshigi. Har nuqta yo'lovchini o'tkazadi (next()), to'xtatadi (javob) yoki muammo bo'lsa maxsus xonaga yuboradi (xato zanjiri). Tartibni almashtirsangiz — masalan, bagajni samolyotga chiqqandan keyin tekshirsangiz — hammasi buziladi.

sequenceDiagram
  participant M as Mijoz
  participant L as log middleware
  participant J as express.json()
  participant R as marshrut
  participant X as xato ishlovchi
  M->>L: POST /vazifalar
  L->>J: next()
  J->>R: next() — req.body tayyor
  R-->>M: 201 (javob)
  Note over R,X: throw bo'lsa — R dan to'g'ri X ga

Diagrammadagi pastki izoh — darsning ikkinchi yarmi: xato bo'lganda zanjir boshqa yo'ldan ketadi.

2. Zanjir qanday ishlaydi

2.1 O'z modelimiz

Express ichidagi g'oya bir necha qator. Middleware'lar — massiv. next — "massivdagi keyingisini chaqir". Xato bo'lsa — faqat 4 argumentlilarni. Bu model saytda ham ishlaydi:

js
// Express'ning zanjir g'oyasi: ro'yxat + next()
function zanjirYarat(qatlamlar) {
  return function ishlat(req, res) {
    let i = 0;
    function next(xato) {
      const q = qatlamlar[i++];
      if (!q) return console.log("tugadi:", xato ? "500" : "404");
      const xatoUchun = q.length === 4; // (xato, req, res, next)
      if (xato && !xatoUchun) return next(xato); // o'tkazib yubor
      if (!xato && xatoUchun) return next();
      try {
        xato ? q(xato, req, res, next) : q(req, res, next);
      } catch (e) {
        next(e);
      }
    }
    next();
  };
}

const ishlat = zanjirYarat([
  (req, res, next) => { console.log("1 log:", req.url); next(); },
  (req, res, next) => {
    if (req.url === "/buz") throw new Error("buzildi");
    next();
  },
  (req, res, next) => {
    if (req.url === "/salom") return console.log("3 javob: salom");
    next();
  },
  (xato, req, res, next) => console.log("4 xato:", xato.message),
]);

ishlat({ url: "/salom" }, {});
ishlat({ url: "/buz" }, {});
ishlat({ url: "/yoq" }, {});

Konsolda:

text
1 log: /salom
3 javob: salom
1 log: /buz
4 xato: buzildi
1 log: /yoq
tugadi: 404

Uch so'rov — uch yo'l:

  • /salom — 1, 2, 3; uchinchisi javob berdi va next() chaqirmadi. Zanjir to'xtadi, xato ishlovchiga yetmadi.
  • /buz — 2-qatlam throw qildi. 3-qatlam o'tkazib yuborildi, to'g'ri 4-ga.
  • /yoq — hech kim javob bermadi, zanjir oxiri. Express bu yerda o'zining HTML 404 sahifasini beradi (Express'ga kirish).

Express xato ishlovchini argumentlar soni bo'yicha taniydi — q.length === 4. Shuning uchun xato middleware'ida next ishlatilmasa ham, to'rtinchi parametr yozilishi shart.

2.2 Middleware uch ishdan birini qiladi

Harakat Natija
Javob (res.json, res.sendStatus …) zanjir to'xtaydi
next() keyingi mos middleware
next(xato) yoki throw keyingi xato middleware
Hech narsa so'rov osilib qoladi

Oxirgi qator — eng yoqimsiz xato. Logda hech narsa yo'q, mijoz kutaveradi, keyin timeout. Har middleware'da "har yo'l javob yoki next bilan tugaydimi?" deb tekshiring.

Tekshirib ko'ring: Middleware if (!req.get("x-kalit")) res.status(401).json(...) deb yozilgan, keyin next(). Kalitsiz so'rovda nima bo'ladi?

Javob

401 javobi yuboriladi, keyin next() ham chaqiriladi — marshrut ishlaydi va ikkinchi javob yozishga uriniadi: ERR_HTTP_HEADERS_SENT (res darsi). Bundan ham yomoni — marshrutdagi amal (masalan o'chirish) bajarilib ketadi. return res.status(401)... kerak.

3. Haqiqiy Express: iz bilan

3.1 Darajalar

Middleware turli joylarga ulanadi:

Daraja Qanday Qachon ishlaydi
Ilova app.use(mw) hamma so'rovda
Yo'l app.use("/a", mw) /a va /a/... da
Router router.use(mw) shu routerga kelganda
Marshrut app.get("/b", mw, ishlovchi) faqat shu metod va yo'lda
Xato app.use((xato, req, res, next) => …) faqat xato bo'lganda

Kanon tajribasi har bir darajani iz bilan tekshirdi. Ilova tuzilishi (qisqartirilgan):

js
app.use(log);                         // 1
app.use("/a", yolMw);                 // 2
const r = express.Router();
r.use(routerMw);                      // 3
r.get("/b", marshrutMw, ishlovchi);   // 4, 5
r.get("/b", keyingiIshlovchi);        // 6
app.use("/a", r);
app.get("/a/b", appIshlovchi);        // 7
app.get("/xato", async () => { throw new Error("buzildi"); }); // 8
app.use(oddiyMw);                     // 9
app.use((err, req, res, next) => …);  // 10

3.2 Oddiy yo'l

Izdagi har qator — bitta qatlam ishlagani; boshidagi raqam yuqoridagi ro'yxatdagi raqam. Qaysi raqamlar yo'q ekaniga ham qarang:

text
GET /a/b → 200 "b"
   1:app.use
   2:app.use('/a') req.url=/b baseUrl=/a
   3:router.use
   4:route mw
   5:handler (javob)
   5b:res.send() dan keyingi kod ham ishlaydi
   finish 200

Shu izni so'rov hayoti (request lifecycle) jadvali sifatida o'qiymiz:

Qadam Middleware Natija
1 app.use(log) hamma so'rovda ishladi → next()
2 app.use("/a", yolMw) yo'l /a bilan boshlanadi → ishladi, next()
3 r.use(routerMw) so'rov routerga kirdi → next()
4 marshrutMw GET /b ga mos → next()
5 ishlovchi javob berdi (200 "b") — zanjir to'xtadi
6, 7 keyingiIshlovchi, appIshlovchi chaqirilmadi: javob allaqachon ketgan
9, 10 oddiyMw, xato ishlovchi chaqirilmadi

Nima bo'ldi? So'rov ro'yxatni yuqoridan pastga yurdi va birinchi javob bergan joyda to'xtadi. Yana ikki narsaga qarang. Birinchidan, 2-qatlamda req.url — /b, req.baseUrl — /a. app.use("/a", …) yo'lning boshini "kesib" beradi — ichkaridagi middleware o'zini ildizda deb biladi. Shuning uchun routerni istalgan yo'lga ulash mumkin (Router darsi). Ikkinchidan, 5b — javobdan keyin ham kod ishladi (res darsidagi return saboq).

3.3 next('route') va next('router')

text
GET /a/b?otkaz=1 → 200 "b2"
   1:app.use
   2:app.use('/a') req.url=/b?otkaz=1 baseUrl=/a
   3:router.use
   4:route mw
   6:keyingi route (next('route'))
   finish 200
GET /a/b?chiq=router → 200 "app"
   1:app.use
   2:app.use('/a') req.url=/b?chiq=router baseUrl=/a
   3:router.use
   7:app.get (next('router') dan keyin)
   finish 200
  • next('route') — shu marshrutning qolgan ishlovchilarini (5) tashlab, keyingi mos marshrutga (6) o'tadi. Faqat marshrut darajasidagi middleware'da ishlaydi. Misol: "admin bo'lsa — to'liq javob, bo'lmasa — keyingi, qisqa variant".
  • next('router') — butun routerdan chiqib, ilovadagi keyingi mosga (7) o'tadi.

Ikkalasi ham kam ishlatiladi, lekin eski kodda uchraydi va izsiz tushunish qiyin.

3.4 Xato yo'li va noma'lum yo'l

Ikki so'rov: biri throw qiladi, ikkinchisiga hech kim mos kelmaydi. 9-qatlam qaysi izda bor, qaysida yo'q — shunga qarang:

text
GET /xato → 500 "xato"
   1:app.use
   8:async handler throw
   10:xato mw (4 argument): buzildi
   finish 500
GET /yoq → 404 "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n"
   1:app.use
   9:oddiy mw (xato bo'lsa O'TKAZIB yuboriladi)
   finish 404

/xato da 9-qatlam yo'q — xato bo'lganda oddiy middleware o'tkazib yuborildi, bizning modeldagidek. Ishlovchi async — Express 5 rad etilgan Promise'ni o'zi ushlab, xato zanjiriga berdi. Express 4 da bu so'rov osilib qolardi (Express 5 darsi). /yoq da hech qaysi marshrut mos kelmadi: 9 ishladi, keyin zanjir tugadi va Express HTML 404 berdi.

4. Tartib qoidasi

Middleware'lar app.use chaqirilgan tartibda ishlaydi. Shundan kelib chiqadigan qoidalar:

  1. Tana o'qiydiganlar marshrutlardan oldin. app.post(...) dan keyin app.use(express.json()) — marshrut req.body ni undefined ko'radi.
  2. Arzon va umumiylar — boshida: log, so'rov ID, xavfsizlik sarlavhalari. Javob berib yuboradigan erta qatlam keyingilarini ishlatmaydi.
  3. static — API'dan oldin yoki keyin — loyihaga qarab. Oldin bo'lsa, public/ dagi fayl API yo'lini "yopib" qo'yishi mumkin. Kanonda static birinchi, chunki public/ da faqat index.html.
  4. 404 — barcha marshrutlardan keyin. U hamma narsani ushlaydi; o'rtaga qo'ysangiz, keyingi marshrutlar o'lik bo'ladi.
  5. Xato ishlovchi — eng oxirida. Undan keyingi app.use ga xato yetib bormaydi.

Kanondagi yakuniy ilovaYarat (holat 13) aynan shu tartibda. Sizdagi oraliq faylda app.use("/vazifalar", …) qatori o'rnida hozircha app.route(...) marshrutlari turibdi — ularni keyingi darsda routerga ko'chiramiz:

ts
app.disable("x-powered-by");
app.use(express.static(join(import.meta.dirname, "..", "public")));
app.use(express.json({ limit: "16kb" }));
app.use("/vazifalar", vazifalarRouteri(vazifaXizmati(ombor)));
app.use((req) => {
  throw new HttpXato(404, `${req.method} ${req.path} topilmadi`);
});
app.use(xatoIshlovchi);

404 middleware javob bermaydi — throw qiladi. Shunda 404 ham xato ishlovchidan o'tadi va hamma xato bir xil JSON shaklida chiqadi. 404 tanasi bitta joyda yaratiladi.

Tekshirib ko'ring: Kimdir app.use(helmet()) ni xato ishlovchidan keyin qo'shdi. Nima bo'ladi?

Javob

Muvaffaqiyatli so'rovlarda helmet umuman ishlamaydi — marshrutlar javob berib, zanjirni to'xtatgan. 404 va xatolarda ham: xato ishlovchi javob beradi va next() chaqirmaydi. Helmet hech qachon chaqirilmaydi, xavfsizlik sarlavhalari yo'q — va bu sezilmaydi. Shuning uchun xavfsizlik middleware'lari ro'yxat boshida (mashhur middleware'lar).

5. Xato middleware: JSON va xavfsiz

5.1 Kanondagi xatoIshlovchi

ts
const xatoIshlovchi: ErrorRequestHandler = (
  xato: unknown, _req, res, next,
) => {
  if (res.headersSent) {
    next(xato); // javob yarmida — Express o'zi ulanishni uzadi
    return;
  }
  if (xato instanceof HttpXato) {
    res.status(xato.holat).json({ xato: xato.message });
    return;
  }
  // body-parser xatolari: `type` maydoni bilan (http-errors)
  const tur =
    typeof xato === "object" && xato !== null && "type" in xato
      ? xato.type
      : undefined;
  if (tur === "entity.parse.failed") {
    res.status(400).json({ xato: "Tana yaroqli JSON emas" });
    return;
  }
  if (tur === "entity.too.large") {
    res.status(413).json({ xato: "Tana 16 KB dan katta" });
    return;
  }
  // Boshqa kutubxonalar (serve-static: 416, 403...) http-errors
  // tashlaydi: status + expose
  if (
    typeof xato === "object" && xato !== null
    && "status" in xato && typeof xato.status === "number"
    && xato.status < 500
    && "expose" in xato && xato.expose === true
    && xato instanceof Error
  ) {
    res.status(xato.status).json({ xato: xato.message });
    return;
  }
  console.error(xato);
  res.status(500).json({ xato: "Server xatosi" });
};

(Kanon, uzun qatorlar telefon uchun bo'lingan — siz o'tgan darsda ilova.ts ga aynan shuni ko'chirgansiz.) Qatorma-qator:

  • headersSent — javob yarmida bo'lsa, yangi status yozib bo'lmaydi. next(xato) — Express'ning sukut ishlovchisi ulanishni uzadi (res darsi).
  • HttpXato — bizning xatolarimiz (404, 405, 409, 415, 422): status va xabar to'g'ridan.
  • entity.parse.failed, entity.too.large — express.json() ning xatolari. U ularni type maydoni bilan tashlaydi. Usiz buzuq JSON 500 bo'lib qaytardi — aslida mijoz xatosi (400).
  • 500 — qolgan hamma narsa. Haqiqiy xabar faqat server logiga (console.error). Mijozga — umumiy matn. res darsida ko'rgan stack trace oqishi shu bilan yopiladi.

xato: unknown — TypeScript'da ushlangan xato turi noma'lum. Shuning uchun har tekshiruv instanceof yoki "type" in xato bilan. Bu Express + TypeScript darsida chuqurroq.

5.2 Testlar topgan xato: 416 → 500

express.static ga o'tgach, kanon testi Range: bytes=99999- so'radi va 416 kutdi. Server 500 qaytardi. Sabab: serve-static noto'g'ri Range'ni next(err) bilan bizga uzatadi. Xato http-errors kutubxonasidan: status: 416, expose: true ("xabarni mijozga ko'rsatish xavfsiz"). Bizning ishlovchi faqat HttpXato ni tanirdi — qolgan hamma narsa 500.

Tuzatish — uchinchi tarmoq: status < 500 va expose === true bo'lgan xatoning statusi va xabari mijozga ketadi. Shu bilan serve-static ning 403 (traversal), 416 va boshqa kutubxonalarning 4xx xatolari to'g'ri chiqadi. Saboq: o'zingiz yozmagan middleware ham next(xato) chaqiradi — xato ishlovchi faqat o'z xato turlaringizni bilishi yetmaydi. Bu tarmoqni Global error handler darsida tartibga solamiz.

6. 405 — Express bermaydigan javob

Express'ga kirish darsida ko'rdik: PUT /vazifalar → HTML 404. Express yo'l bor, lekin metod yo'qligini bilmaydi. Kanon yechimi — har yo'lning oxiriga .all():

ts
// Express metod mos kelmasa 404 beradi — 405 + Allow ni o'zimiz
function metodYoq(allow: string): RequestHandler {
  return (req, res) => {
    res.set("Allow", allow);
    throw new HttpXato(405, `${req.method} bu manzilda yo'q`);
  };
}

r.route("/")
  .get(royxatIshlovchi)
  .post(jsonKerak, yaratIshlovchi)
  .all(metodYoq("GET, HEAD, POST"));

metodYoq — middleware fabrikasi: parametr oladi va middleware qaytaradi. express.json({ limit }) ham shunday. .all() zanjir oxirida — GET va POST undan oldin ushlanadi, unga faqat qolgan metodlar yetadi.

Kanon jonli tekshiruvi (holat 13):

text
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, POST
Content-Type: application/json; charset=utf-8
Content-Length: 31
…
{"xato":"PUT bu manzilda yo'q"}

HEAD ro'yxatda: Express uni GET dan o'zi yasaydi. OPTIONS esa CORS preflight uchun keyinroq kerak bo'ladi (mashhur middleware'lar darsi).

7. Hujumchi nigohi

Zaiflik Qanday paydo bo'ladi Himoya
Stack trace oqishi xato ishlovchi yo'q yoki xato.message mijozga umumiy 500 matni, tafsilot logda
Auth tekshiruvi chetlab o'tildi auth middleware marshrutdan keyin ulangan himoya middleware'lari — oldin
return siz javob 401 dan keyin ham amal bajarildi return res... yoki throw
Osilgan so'rovlar (DoS) next() ham, javob ham yo'q har yo'l tugashini tekshirish, timeout'lar
500 da ichki ma'lumot res.json({ xato: e.message }) faqat HttpXato va expose xabarlari

Oxirgi qator: bazaning xato xabarida jadval nomlari, ba'zan parol bo'lgan ulanish satri bo'lishi mumkin. Kanon Problem Details darsida buni test bilan tekshiradi: 500 javobida "parol=maxfiy" yo'q, logda bor.

8. API qadami: middleware tartibi va xato ishlovchi (holat 13, 4-qism)

Hali commit qilmang — keyingi darsda. Bugun ham kod o'zgarmaydi — o'tgan darslarda ko'chirgan ilova.ts da bularning hammasi bor:

  • «Tartib qoidasi» bo'limidagi tartib: disable → static → json → marshrutlar (app.route) → 404 (throw) → xatoIshlovchi.
  • xatoIshlovchi — uch tarmoq (HttpXato, body-parser, http-errors + expose) va 500.
  • metodYoq(allow) — .all() bilan 405.
  • jsonKerak — marshrut darajasida (req darsi).

Kanon jonli tekshiruvidagi xato javoblari endi hammasi JSON:

text
{"xato":"Tana yaroqli JSON emas"}
{"xato":"Content-Type: application/json bo'lishi kerak"}
{"xato":"matn 1–200 belgi bo'lishi kerak"}
{"xato":"PUT bu manzilda yo'q"}
{"xato":"Tana 16 KB dan katta"}
{"xato":"GET /yoq topilmadi"}

Statuslar mos ravishda: 400, 415, 422, 405, 413, 404. Holat 07 da bu javoblarni o'z json() yordamchimiz yozardi. Endi — bitta xato ishlovchi.

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
req.body — undefined express.json() marshrutdan keyin tartib
Xato ishlovchi chaqirilmaydi 3 argumentli (xato, req, res, next) — to'rttasi
Xato ishlovchidan keyin HTML 404 404 xato ishlovchidan keyin 404 → xato ishlovchi
So'rov osilib qoladi next() ham, javob ham yo'q har yo'lni tekshiring
Buzuq JSON → 500 body-parser xatosi ushlanmagan entity.parse.failed → 400
416 / 403 → 500 kutubxona xatosi (http-errors) noma'lum status < 500 && expose
PUT → HTML 404 Express 405 bermaydi .all(metodYoq(...))

10. Mashqlar

Yechimlar kurs/mashqlar/25/12-middleware/ da.

1-mashq (oson): to'ldiring

  • Xato middleware'ining argumentlar soni —
  • Joriy marshrutning qolgan ishlovchilarini tashlab keyingi marshrutga o'tish — next('')
  • Xato bo'lganda oddiy middleware'lar — yuboriladi
  • 404 middleware'ining joyi — barcha marshrutlardan
  • express.json() buzuq JSON'da beradigan xato turi — entity.parse.
Yechim
  • 4 — (xato, req, res, next); Express length ga qaraydi.
  • route — faqat marshrut darajasidagi middleware'da.
  • o'tkazib — iz: /xato da 9-qatlam yo'q.
  • keyin — aks holda keyingi marshrutlar o'lik.
  • failed — entity.parse.failed → 400; entity.too.large → 413.

2-mashq (o'rta): modelga marshrut qo'shish

«O'z modelimiz» bo'limidagi zanjirYarat ni kengaytiring: qatlam funksiya yoki { yol, fn } obyekt bo'lishi mumkin. Obyekt bo'lsa — faqat req.url yol bilan boshlanganda ishlaydi (app.use("/a", mw) kabi). Sinang: /admin ga faqat admin qatlami, /salom ga — yo'q. Eslatma: startsWith — satr metodlari.

Yechim
js
function zanjirYarat(qatlamlar) {
  return function ishlat(req) {
    let i = 0;
    function next(xato) {
      const q = qatlamlar[i++];
      if (!q) return console.log("  tugadi:", xato ? "500" : "404");
      const fn = typeof q === "function" ? q : q.fn;
      if (q.yol && !req.url.startsWith(q.yol)) return next(xato);
      const xatoUchun = fn.length === 4;
      if (xato && !xatoUchun) return next(xato);
      if (!xato && xatoUchun) return next();
      try {
        xato ? fn(xato, req, null, next) : fn(req, null, next);
      } catch (e) {
        next(e);
      }
    }
    next();
  };
}

const ishlat = zanjirYarat([
  { yol: "/admin", fn: (req, res, next) => {
    console.log("  admin tekshiruvi");
    if (!req.admin) throw new Error("ruxsat yo'q");
    next();
  } },
  (req, res, next) => {
    if (req.url === "/salom") return console.log("  javob: salom");
    next();
  },
  (xato, req, res, next) => console.log("  xato:", xato.message),
]);

for (const req of [{ url: "/salom" }, { url: "/admin/panel" }]) {
  console.log(req.url);
  ishlat(req);
}

Konsolda:

text
/salom
  javob: salom
/admin/panel
  admin tekshiruvi
  xato: ruxsat yo'q

/salom admin qatlamiga umuman kirmadi — yo'l mos emas. Haqiqiy Express buni yo'lni "kesib" ham qiladi (req.url=/panel, baseUrl=/admin), bizning model esa faqat tekshiradi.

3-mashq (qiyin): to'liq zanjir Express'da

Express 5.3.0 bilan oltita qismdan iborat ilova yozing. (1) Log middleware: hamma so'rovni res.on("finish") bilan "METOD yo'l → status" ko'rinishida yig'sin. (2) express.json({ limit: "1kb" }). (3) POST /taomlar — 201 va tana. (4) GET /buz — async ishlovchi: throw new Error("baza ulanmadi: parol=maxfiy"). (5) JSON 404. (6) xato ishlovchi: buzuq JSON → 400, katta tana → 413, qolgani → 500 va umumiy matn. Besh so'rovni yuborib, javoblar va logni chiqaring. Eslatma: res.on("finish", fn) — javob to'liq yuborilganda (EventEmitter).

Yechim
js
import express from "express";

const app = express();
const iz = [];
app.use((req, res, next) => {
  res.on("finish", () =>
    iz.push(`${req.method} ${req.path} → ${res.statusCode}`));
  next();
});
app.use(express.json({ limit: "1kb" }));
app.post("/taomlar", (req, res) => res.status(201).json(req.body));
app.get("/buz", async () => {
  throw new Error("baza ulanmadi: parol=maxfiy");
});
app.use((req, res) => {
  const xato = `${req.method} ${req.path} topilmadi`;
  res.status(404).json({ xato });
});
app.use((xato, req, res, next) => {
  if (res.headersSent) return next(xato);
  if (xato.type === "entity.parse.failed") {
    return res.status(400).json({ xato: "JSON buzuq" });
  }
  if (xato.type === "entity.too.large") {
    return res.status(413).json({ xato: "tana juda katta" });
  }
  res.status(500).json({ xato: "Server xatosi" }); // ichki xabar yo'q
});

const server = app.listen(0, "127.0.0.1");
await new Promise((ok) => server.once("listening", ok));
const a = `http://127.0.0.1:${server.address().port}`;
const json = (body) => ({ method: "POST",
  headers: { "Content-Type": "application/json" }, body });
const katta = JSON.stringify({ nom: "x".repeat(2000) });
const sorovlar = [
  ["/taomlar", json('{"nom":"osh"}')],
  ["/taomlar", json("{nom:")],
  ["/taomlar", json(katta)],
  ["/buz", undefined],
  ["/yoq", undefined],
];
for (const [yol, opts] of sorovlar) {
  const r = await fetch(a + yol, opts);
  console.log(r.status, await r.text());
}
console.log(iz.join("\n"));
server.closeAllConnections();
server.close();

Konsolda:

text
201 {"nom":"osh"}
400 {"xato":"JSON buzuq"}
413 {"xato":"tana juda katta"}
500 {"xato":"Server xatosi"}
404 {"xato":"GET /yoq topilmadi"}
POST /taomlar → 201
POST /taomlar → 400
POST /taomlar → 413
GET /buz → 500
GET /yoq → 404

500 javobida parol=maxfiy yo'q. Log middleware'i birinchi turgani uchun hamma so'rovni, jumladan xatolarni ham ko'rdi: finish hodisasi javob qaysi qatlamda yozilganidan qat'i nazar keladi.

4-mashq: API qadami

Ikki tajriba, har biridan keyin asl holiga qaytaring. (1) xatoIshlovchi dan http-errors tarmog'ini (status < 500 && expose) vaqtincha o'chiring va npm test qiling. Qaysi tekshiruv yiqiladi va qanday status keladi? (2) app.use(express.json(...)) qatorini marshrutlardan keyinga ko'chiring. curl yoki fetch bilan POST /vazifalar yuboring. Javob nima va nega? Commit — keyingi darsda.

Yechim

(1) Biz ishga tushirdik: 6 test o'tdi, 1 yiqildi — static testida actual: 500, expected: 416. Aynan kanon testlari topgan xato («Testlar topgan xato: 416 → 500» bo'limi). (2) Marshrut req.body ni undefined ko'radi. yangiVazifaniTekshir(undefined) 422 qaytaradi ("tana — JSON obyekt bo'lishi kerak") — mijoz to'g'ri JSON yuborgan bo'lsa ham. Tartib xatosi jim, xato xabari esa noto'g'ri tomonga ishora qiladi.

11. Real ishda

  • Har Express loyihasi — middleware ro'yxati: helmet, cors, so'rov ID, log, body parser, auth, marshrutlar, 404, xato ishlovchi. Tartibni yangi dasturchiga tushuntirish — onboarding'ning bir qismi.
  • NestJS ham shu modelda: guard, interceptor, pipe, exception filter — middleware g'oyasining kengaytmasi.
  • Bug'larning ko'pi — tartib xatosi: auth'dan oldingi marshrut, body parser'dan oldingi webhook (express.raw).
  • Intervyu: "Middleware nima?", "Xato middleware'ini Express qanday taniydi?", "next('route') nima qiladi?", "Express 4 va 5 da async xato farqi?".

Xulosa

  • Middleware — (req, res, next); javob, next() yoki next(xato)/throw — hech narsa qilmasa, so'rov osiladi.
  • Tartib = app.use tartibi: xavfsizlik va body parser — boshida, 404 — marshrutlardan keyin, xato ishlovchi — oxirida.
  • Xato ishlovchi 4 argumentli; xato bo'lganda oddiy middleware'lar o'tkazib yuboriladi; Express 5 async throw ni o'zi ushlaydi.
  • Bitta JSON xato ishlovchi: o'z xatolaringiz, body-parser type, http-errors (status < 500 && expose), qolgani — umumiy 500.
  • Express 405 bermaydi — .all(metodYoq("...")).

Keyingi dars: express.Router va qatlamli loyiha tuzilmasi — routerlar, marshrut → xizmat → ombor qatlamlari va holat 13 commit'i.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Express middleware: next(), app.use, tartib qoidasi, xato middleware va request lifecycle — IlmHamroh