Node.jsМодуль 4: HTTP и Express
Middleware
Встроенные, сторонние и кастомные middleware
Цель урока
В этом уроке ты научишься:
- Понимать концепцию middleware
- Использовать встроенные и сторонние middleware
- Создавать собственные middleware
Что такое Middleware
Middleware — функция, которая имеет доступ к req, res и next. Выполняется между получением запроса и отправкой ответа.
function middleware(req, res, next) {
// Логика
next(); // Передать управление следующему middleware
}Порядок выполнения
const app = express();
app.use((req, res, next) => {
console.log('1. Первый middleware');
next();
});
app.use((req, res, next) => {
console.log('2. Второй middleware');
next();
});
app.get('/', (req, res) => {
console.log('3. Обработчик маршрута');
res.send('OK');
});
// Порядок: 1 → 2 → 3Важно
Порядок app.use() имеет значение! Middleware выполняются в порядке объявления.
Встроенные Middleware
// Парсинг JSON
app.use(express.json({ limit: '10mb' }));
// Парсинг URL-encoded
app.use(express.urlencoded({ extended: true }));
// Статические файлы
app.use(express.static('public'));
app.use('/uploads', express.static('uploads'));Популярные сторонние Middleware
cors
npm install corsconst cors = require('cors');
// Разрешить все origins
app.use(cors());
// С настройками
app.use(cors({
origin: 'http://localhost:3000',
methods: ['GET', 'POST'],
credentials: true
}));helmet
npm install helmetconst helmet = require('helmet');
// Защита HTTP заголовков
app.use(helmet());morgan
npm install morganconst morgan = require('morgan');
// Логирование запросов
app.use(morgan('dev')); // Краткий формат
app.use(morgan('combined')); // Полный форматcompression
npm install compressionconst compression = require('compression');
// Сжатие ответов
app.use(compression());Создание Middleware
Логгер
function logger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} ${res.statusCode} - ${duration}ms`);
});
next();
}
app.use(logger);Аутентификация
function auth(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token required' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
}
// Использование
app.get('/profile', auth, (req, res) => {
res.json({ user: req.user });
});Валидация
function validate(schema) {
return (req, res, next) => {
const { error } = schema.validate(req.body);
if (error) {
return res.status(400).json({
error: error.details[0].message
});
}
next();
};
}
// Использование с Joi
const Joi = require('joi');
const userSchema = Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required()
});
app.post('/users', validate(userSchema), (req, res) => {
res.json(req.body);
});Rate Limiting
function rateLimit(windowMs, max) {
const requests = new Map();
return (req, res, next) => {
const ip = req.ip;
const now = Date.now();
if (!requests.has(ip)) {
requests.set(ip, { count: 1, start: now });
return next();
}
const data = requests.get(ip);
if (now - data.start > windowMs) {
requests.set(ip, { count: 1, start: now });
return next();
}
if (data.count >= max) {
return res.status(429).json({ error: 'Too many requests' });
}
data.count++;
next();
};
}
// 100 запросов в минуту
app.use('/api', rateLimit(60000, 100));Middleware для конкретных маршрутов
// Для одного маршрута
app.get('/admin', auth, adminOnly, (req, res) => {
res.json({ admin: true });
});
// Для группы маршрутов
app.use('/api/admin', auth, adminOnly);
// Для роутера
const adminRouter = express.Router();
adminRouter.use(auth);
adminRouter.use(adminOnly);Async Middleware
// Обёртка для async функций
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Использование
app.get('/users', asyncHandler(async (req, res) => {
const users = await User.find();
res.json(users);
}));Практика
Задание 1: Логгер с временем
Задача: Создай middleware, который логирует время запроса.
Решение:
function requestTime(req, res, next) {
req.requestTime = new Date().toISOString();
console.log(`[${req.requestTime}] ${req.method} ${req.url}`);
next();
}
app.use(requestTime);Задание 2: API Key
Задача: Создай middleware для проверки API ключа.
Решение:
function apiKey(req, res, next) {
const key = req.headers['x-api-key'];
if (key !== process.env.API_KEY) {
return res.status(403).json({ error: 'Invalid API key' });
}
next();
}
app.use('/api', apiKey);Проверь себя
- Что делает функция
next()? - Как применить middleware к конкретному маршруту?
- Как обработать ошибки в async middleware?