Что такое middleware в Express.js и как он работает?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Middleware в Express.js: полное руководство
Middleware — это функция, которая имеет доступ к объектам request (req), response (res) и следующему middleware в цепочке обработки запроса. Это один из самых важных концептов Express.js.
Основное определение
Middleware — это функция с сигнатурой:
(req, res, next) => { ... }
req— объект HTTP запросаres— объект HTTP ответаnext— функция для передачи управления следующему middleware
Как работает middleware?
Middleware работают в виде конвейера (pipeline). Каждый middleware может:
- Обработать запрос
- Модифицировать req или res
- Отправить ответ (завершить конвейер)
- Передать управление следующему middleware через
next()
Request
↓
┌─────────────────┐
│ Middleware 1 │
│ (Authentication)│ ← Проверка токена
└─────────────────┘
↓ next()
┌─────────────────┐
│ Middleware 2 │
│ (Logging) │ ← Логирование
└─────────────────┘
↓ next()
┌─────────────────┐
│ Middleware 3 │
│ (Validation) │ ← Валидация
└─────────────────┘
↓ next()
┌─────────────────┐
│ Route Handler │
│ (Controller) │ ← Основная логика
└─────────────────┘
↓ res.send()
Response
Пример 1: Простой middleware
const express = require('express');
const app = express();
// Middleware функция
const loggingMiddleware = (req, res, next) => {
console.log(`${req.method} ${req.path}`);
next(); // Передаём управление дальше
};
// Регистрируем middleware ДЛЯ ВСЕХ маршрутов
app.use(loggingMiddleware);
// Route handler
app.get('/users', (req, res) => {
res.json({ users: [] });
});
app.listen(3000);
// При запросе GET /users:
// 1. Вызывается loggingMiddleware → выводит "GET /users"
// 2. Вызывается next() → переходит к обработчику маршрута
// 3. Отправляется ответ { users: [] }
Пример 2: Middleware с модификацией запроса
// Middleware для добавления информации о пользователе
const authMiddleware = (req, res, next) => {
// Проверяем токен
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
// Парсим токен (без проверки подписи для примера)
try {
const decoded = { userId: 123, username: 'john' }; // В реальности парсим JWT
req.user = decoded; // Добавляем в объект req
next();
} catch (err) {
res.status(401).json({ error: 'Invalid token' });
}
};
app.use(authMiddleware);
app.get('/profile', (req, res) => {
// req.user доступен благодаря middleware
res.json({ user: req.user });
});
Пример 3: Middleware для отдельных маршрутов
// Применяем middleware только к конкретному маршруту
const adminOnlyMiddleware = (req, res, next) => {
if (req.user?.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
// Без middleware
app.get('/public', (req, res) => {
res.json({ message: 'Public page' });
});
// С middleware
app.get('/admin', adminOnlyMiddleware, (req, res) => {
res.json({ message: 'Admin page' });
});
// С несколькими middleware
app.post(
'/admin/users',
authMiddleware, // Проверка аутентификации
adminOnlyMiddleware, // Проверка прав
(req, res) => { // Основной обработчик
res.json({ message: 'Create user' });
}
);
Типы middleware
1. Application-level middleware (для всех маршрутов)
app.use((req, res, next) => {
console.log('Request received');
next();
});
2. Router-level middleware (для маршрутов роутера)
const router = express.Router();
router.use((req, res, next) => {
console.log('Router middleware');
next();
});
router.get('/users', (req, res) => {
res.json({ users: [] });
});
app.use('/api', router);
3. Error-handling middleware (4 параметра!)
// ОБЯЗАТЕЛЬНО 4 параметра для ошибок!
app.use((err, req, res, next) => {
console.error('Error:', err.message);
res.status(500).json({ error: err.message });
});
4. Built-in middleware
// Express встроенные middleware
app.use(express.json()); // Парсинг JSON тела
app.use(express.urlencoded()); // Парсинг форм
app.use(express.static('public')); // Статические файлы
5. Third-party middleware
const cors = require('cors');
const helmet = require('helmet');
app.use(cors()); // CORS
app.use(helmet()); // Безопасность
app.use(morgan('combined')); // Логирование
Пример 4: Цепочка middleware
const express = require('express');
const app = express();
// Middleware 1: логирование
const logger = (req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
next();
};
// Middleware 2: парсинг JSON
const jsonParser = express.json();
// Middleware 3: валидация
const validateUser = (req, res, next) => {
if (!req.body.name || !req.body.email) {
return res.status(400).json({ error: 'Missing fields' });
}
next();
};
// Регистрируем middleware по порядку
app.use(logger); // Вызывается первым
app.use(jsonParser); // Вызывается вторым
app.post('/users', validateUser, (req, res) => {
// Вызывается третьим (validateUser)
// Затем обработчик маршрута
res.status(201).json({ id: 1, ...req.body });
});
app.listen(3000);
Пример 5: Middleware с асинхронностью
// ✓ Правильно: используем async/await
const fetchUserMiddleware = async (req, res, next) => {
try {
const user = await database.users.findById(req.params.userId);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
req.user = user;
next();
} catch (err) {
next(err); // Передаём ошибку в error-handling middleware
}
};
app.get('/users/:userId', fetchUserMiddleware, (req, res) => {
res.json(req.user);
});
// ✓ Error handling
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
});
Пример 6: Реальное приложение
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const morgan = require('morgan');
const jwt = require('jsonwebtoken');
const app = express();
// Security middleware
app.use(helmet());
app.use(cors());
// Logging middleware
app.use(morgan('combined'));
// Body parsing middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Authentication middleware
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'No token' });
try {
req.user = jwt.verify(token, 'secret');
next();
} catch (err) {
res.status(401).json({ error: 'Invalid token' });
}
};
// Public route (БЕЗ middleware)
app.post('/auth/login', (req, res) => {
const token = jwt.sign({ id: 1, username: 'john' }, 'secret');
res.json({ token });
});
// Protected route (С middleware)
app.get('/users', authMiddleware, (req, res) => {
res.json({ user: req.user });
});
// Error handling middleware (ПОСЛЕДНИЙ!)
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Важные правила
- Порядок имеет значение — middleware выполняются в том порядке, в котором зарегистрированы
- ОБЯЗАТЕЛЬНО вызывай next() (если не отправляешь ответ)
- Ошибки в middleware → передавай в следующий middleware через
next(err) - Error-handling middleware с 4 параметрами должен быть ПОСЛЕДНИМ
- Не вызывай next() дважды — это вызовет ошибку
- Для асинхронности используй async/await и обрабатывай ошибки
Распространённые ошибки
// ✗ ОШИБКА: забыли вызвать next()
app.use((req, res) => {
console.log('Request');
// Запрос зависнет!
});
// ✓ ПРАВИЛЬНО
app.use((req, res, next) => {
console.log('Request');
next(); // Передаём управление
});
// ✗ ОШИБКА: вызвали next() дважды
app.get('/users', (req, res, next) => {
res.send('Users');
next(); // Ошибка!
});
// ✓ ПРАВИЛЬНО: отправляем ответ ИЛИ вызываем next(), не оба
app.get('/users', (req, res, next) => {
res.send('Users');
// Не вызываем next()
});
Заключение
Middleware — это фундаментальный концепт Express.js, который позволяет обрабатывать запрос на разных этапах. Они используются для аутентификации, валидации, логирования, обработки ошибок и многого другого. Ключ к правильной работе — понимание порядка выполнения и обязательный вызов next() для передачи управления следующему middleware.