Error Middleware — 4 parametrli, 404 handler
Muammodan boshlaylik
Har bir route da xatolarni alohida boshqarish:
app.get('/users/:id', async (req, res) => {
try {
const user = await db.findUser(req.params.id);
res.json(user);
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Server xatosi' }); // takror
}
});
app.get('/products', async (req, res) => {
try {
const products = await db.getProducts();
res.json(products);
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Server xatosi' }); // yana takror
}
});
50 ta route bo'lsa — 50 ta joyda bir xil catch bloki. Xato xabarini o'zgartirmoqchi bo'lsangiz — 50 ta joyni o'zgartirish kerak.
Error middleware bu muammoni hal qiladi: xatolarni bir markaziy joyda boshqarish.
Error Middleware nima?
Oddiy middleware uchta argument oladi: req, res, next. Error middleware esa to'rtta: err, req, res, next. Birinchi argument — xato ob'ekti.
Express bu ikkalasini qanday farqlaydi? Faqat parametrlar soniga qarab. To'rtta parametr bo'lsa — error middleware. Uchtasi bo'lsa — oddiy middleware. Shuning uchun next ishlatmasangiz ham — yozilishi shart.
Qanday ishlaydi? Istalgan joyda next(err) chaqirilsa — Express barcha oddiy middleware va route larni o'tkazib yuborib, to'g'ridan-to'g'ri error middleware ga o'tadi. Xuddi "favqulodda chiqish" kabi — oddiy yo'lni tashlab, maxsus yo'ldan boradi.
So'rov → middleware → route → next(err)
↓
(boshqa route lar o'tkazib yuboriladi)
↓
Error Middleware
↓
Javob yuboriladi
Eng oddiy error middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: 'Server xatosi' });
});
Bu funksiya faqat next(err) chaqirilganda ishlaydi — oddiy so'rovlarda emas.
Muhim qoida: error middleware barcha route lardan keyin yoziladi. Oldin yozilsa — route lardan kelgan xatolar unga yetmaydi.
Route da xatoni uzatish
Endi har bir route da catch bloki yozish o'rniga — next(err) bilan markaziy error middleware ga uzatiladi:
app.get('/users/:id', async (req, res, next) => {
try {
const user = await db.findUser(req.params.id);
if (!user) return res.status(404).json({ message: 'Topilmadi' });
res.json(user);
} catch (err) {
next(err); // error middleware ga uzatish — boshqa hech narsa yozmasdan
}
});
app.get('/products', async (req, res, next) => {
try {
const products = await db.getProducts();
res.json(products);
} catch (err) {
next(err); // bir xil — markaziy joyga uzatiladi
}
});
// Barcha xatolar shu yerda boshqariladi:
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: 'Server xatosi' });
});
Endi xato xabarini o'zgartirmoqchi bo'lsangiz — faqat bir joyni o'zgartirasiz.
Turli xatolar uchun turli javob
Hamma xato bir xil emas. Ma'lumotlar bazasiga ulanib bo'lmasa — 500. Foydalanuvchi topilmasa — 404. Token yaroqsiz bo'lsa — 401.
Buning uchun maxsus xato sinfi yaratiladi. G'oya oddiy: xato ob'ektiga statusCode qo'shiladi, error middleware uni o'qib to'g'ri status qaytaradi.
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
}
}
Endi route da:
app.get('/users/:id', async (req, res, next) => {
try {
const user = await db.findUser(+req.params.id);
if (!user) throw new AppError('Foydalanuvchi topilmadi', 404);
res.json(user);
} catch (err) {
next(err);
}
});
Error middleware esa statusCode bor-yo'qligiga qarab farqlaydi:
app.use((err, req, res, next) => {
// AppError — biz ataylab chiqargan xato
if (err.statusCode) {
return res.status(err.statusCode).json({ message: err.message });
}
// Kutilmagan xato — ma'lumotlar bazasi, tarmoq va h.k.
console.error(err.stack); // faqat kutilmagan xatolarni log qilamiz
res.status(500).json({ message: 'Server xatosi' });
});
Bu farq muhim: AppError — biz bilgan holat (topilmadi, ruxsat yo'q). Kutilmagan xato — biz bilmagan holat (server muammosi). Ikkinchisi log qilinadi, birinchisi emas.
404 handler — topilmagan route
Foydalanuvchi /api/userss (xato yozilgan) ga so'rov yubordi. Hech bir route mos kelmaydi. Express standart bo'yicha HTML sahifa qaytaradi — API uchun bu noto'g'ri.
404 handler — barcha route lardan keyin, error middleware dan oldin yoziladi. Bu oddiy middleware — next(err) emas, to'g'ridan-to'g'ri javob yuboradi:
// Barcha route lar...
// 404 — hech bir route mos kelmadi:
app.use((req, res) => {
res.status(404).json({
message: `${req.method} ${req.url} — bunday endpoint yo'q`,
});
});
// Error middleware — eng oxirida:
app.use((err, req, res, next) => {
if (err.statusCode) {
return res.status(err.statusCode).json({ message: err.message });
}
console.error(err.stack);
res.status(500).json({ message: 'Server xatosi' });
});
asyncHandler — try/catch ni qisqartirish
Har bir asinxron route da try/catch + next(err) yozish zerikarli. Yordamchi funksiya bu ishni avtomatik qiladi:
const asyncHandler = fn => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
Bu funksiya nima qiladi? Asinxron funksiyani o'rab oladi. Xato chiqsa — avtomatik next(err) chaqiradi. Siz try/catch yozmasangiz ham bo'ladi:
// try/catch siz:
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await db.findUser(+req.params.id);
if (!user) throw new AppError('Topilmadi', 404);
res.json(user);
}));
Xato chiqsa — asyncHandler uni tutib, next(err) ga uzatadi. Error middleware ishlaydi.
Tez-tez uchraydigan xatolar
Error middleware ni route lardan oldin qo'yish:
Bu eng ko'p uchraydigan xato. Error middleware route lardan oldin bo'lsa — route lardan kelgan xatolar unga yetmaydi, chunki Express uni oddiy middleware deb qabul qiladi.
next parametrini yozmaslik:
app.use((err, req, res) => { ... }); // 3 parametr — Express bu ni error middleware deb tanımaydi!
Express parametrlar soniga qarab farqlaydi. next ishlatilmasa ham — yozilishi shart.
Asinxron xatoni next ga uzatmaslik:
app.use(async (req, res, next) => {
const data = await fetchData(); // xato chiqsa — Express ushlamaydi
next();
});
Asinxron middleware da try/catch + next(err) yoki asyncHandler kerak.
Xulosa
Error middleware — to'rtta parametrli (err, req, res, next) funksiya. Barcha route lardan keyin yoziladi. next(err) chaqirilganda ishlaydi.
Foyda: xatolarni bir markaziy joyda boshqarish. 50 ta route da bir xil catch bloki o'rniga — bitta error middleware.
AppError sinfi — turli xatolar uchun turli status kodlar. 404 handler — topilmagan route lar uchun. asyncHandler — try/catch ni qisqartirish uchun.
Keyingi dars
12-dars: Router — alohida fayllarga bo'lish, modullar