Custom Middleware — yaratish, next(), xatolarni uzatish

~20 daq · Express · Middleware

O'z middleware ingizni yozish

Tayyor middleware lar ko'p narsani hal qiladi. Lekin loyihaga xos mantiq — o'zingiz yozasiz. Masalan: foydalanuvchi rolini tekshirish, so'rov tezligini o'lchash, maxsus log format.

Middleware — uch argument oluvchi oddiy funksiya:

function myMiddleware(req, res, next) {
  // 1. req ni o'qish yoki o'zgartirish
  // 2. res ga yozish (va to'xtatish)
  // 3. next() chaqirish (davom etish)
}

Amaliy misol — request timer

Har bir so'rov qancha vaqt olganini o'lchash:

function requestTimer(req, res, next) {
  const start = Date.now();

  // res.json original funksiyasini "o'rab olish":
  const originalJson = res.json.bind(res);
  res.json = function(data) {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.url} — ${duration}ms`);
    return originalJson(data);
  };

  next();
}

app.use(requestTimer);

Amaliy misol — autentifikatsiya middleware

const jwt = require('jsonwebtoken');

function authenticate(req, res, next) {
  const authHeader = req.headers['authorization'];

  // Header bor-yo'qligini tekshirish
  if (!authHeader) {
    return res.status(401).json({ message: 'Token kerak' });
  }

  // Format: "Bearer <token>"
  const token = authHeader.split(' ')[1];
  if (!token) {
    return res.status(401).json({ message: 'Token formati noto\'g\'ri' });
  }

  // Tokenni tekshirish
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded; // keyingi handler lar uchun
    next();
  } catch (err) {
    return res.status(401).json({ message: 'Token yaroqsiz' });
  }
}

// Ishlatish:
app.get('/profile', authenticate, (req, res) => {
  res.json(req.user);
});

Amaliy misol — rol tekshirish

Autentifikatsiya va avtorizatsiya — ikki farqli narsa. Autentifikatsiya: "sen kimsiz?" Avtorizatsiya: "senga ruxsat bormi?"

function requireRole(...roles) {
  return function(req, res, next) {
    if (!req.user) {
      return res.status(401).json({ message: 'Login kerak' });
    }

    if (!roles.includes(req.user.role)) {
      return res.status(403).json({ message: 'Ruxsat yo\'q' });
    }

    next();
  };
}

// Ishlatish:
app.delete('/users/:id',
  authenticate,                    // avval kim ekanini aniqlash
  requireRole('admin', 'moderator'), // keyin ruxsatni tekshirish
  (req, res) => {
    res.json({ message: 'O\'chirildi' });
  }
);

requireRole — middleware factory: funksiya qaytaruvchi funksiya. Bu pattern middleware ni moslashuvchan qiladi.


next(err) — xatoni uzatish

Middleware da xato chiqsa — next(err) bilan xato middleware ga uzatiladi:

function authenticate(req, res, next) {
  try {
    const token = req.headers['authorization']?.split(' ')[1];
    req.user = jwt.verify(token, process.env.JWT_SECRET);
    next();
  } catch (err) {
    next(err); // xato middleware ga uzatish
  }
}

next() — davom etish. next(err) — xato bilan to'xtatish, error middleware ga o'tish.


Asinxron middleware

Asinxron operatsiyalar (ma'lumotlar bazasi, API) bilan ishlashda async/await ishlatiladi. Lekin xatolarni ushlamaslik — server crash bo'ladi:

// Xato — xato ushlanmaydi:
app.use(async (req, res, next) => {
  const user = await db.findUser(req.userId); // xato chiqsa — crash
  req.user = user;
  next();
});

// To'g'ri — try/catch bilan:
app.use(async (req, res, next) => {
  try {
    const user = await db.findUser(req.userId);
    req.user = user;
    next();
  } catch (err) {
    next(err); // xato middleware ga uzatish
  }
});

Har safar try/catch yozish zerikarli. Yordamchi funksiya:

function asyncHandler(fn) {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
}

// Ishlatish — try/catch shart emas:
app.get('/users', asyncHandler(async (req, res) => {
  const users = await db.getUsers();
  res.json(users);
}));

asyncHandler xatoni avtomatik next(err) ga uzatadi.


Middleware ni alohida faylga chiqarish

Loyiha o'sganda middleware larni alohida faylga chiqarish — yaxshi amaliyot:

// middleware/auth.js
const jwt = require('jsonwebtoken');

module.exports = function authenticate(req, res, next) {
  try {
    const token = req.headers['authorization']?.split(' ')[1];
    if (!token) return res.status(401).json({ message: 'Token kerak' });
    req.user = jwt.verify(token, process.env.JWT_SECRET);
    next();
  } catch {
    res.status(401).json({ message: 'Token yaroqsiz' });
  }
};
// app.js
const authenticate = require('./middleware/auth');

app.get('/profile', authenticate, (req, res) => {
  res.json(req.user);
});

Tez-tez uchraydigan xatolar

next() ni ikki marta chaqirish:

app.use((req, res, next) => {
  next();
  next(); // Xato! Ikki marta chaqirildi
});

next() faqat bir marta chaqiriladi.

Asinxron xatoni ushlamaslik:

app.use(async (req, res, next) => {
  const data = await fetchData(); // xato chiqsa — UnhandledPromiseRejection
  next();
});
// try/catch yoki asyncHandler kerak

next(err) o'rniga throw ishlatish:

// Xato — Express ushlamaydi:
app.use((req, res, next) => {
  throw new Error('Xato'); // sinxron — ishlaydi
});

app.use(async (req, res, next) => {
  throw new Error('Xato'); // asinxron — Express ushlamaydi!
  // next(err) yoki asyncHandler kerak
});

Xulosa

Custom middleware — loyihaga xos mantiq uchun. Autentifikatsiya, avtorizatsiya, logging, so'rov o'lchash — bularning hammasi middleware sifatida yoziladi.

next() — davom etish. next(err) — xato bilan to'xtatish. Asinxron middleware da try/catch + next(err) yoki asyncHandler ishlatiladi.

Middleware factory pattern — requireRole('admin') kabi moslashuvchan middleware yaratish uchun.


Keyingi dars

11-dars: Error Middleware — 4 parametrli, 404 handler