Menu

Middleware в Express: как работает next(), с примерами

Middleware в Express это функция (req, res, next), которая выполняется до обработчика маршрута. Она может прочитать или изменить запрос, завершить его ответом или вызвать next(), чтобы передать его дальше.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Middleware в Express это функция с сигнатурой (req, res, next), которая выполняется между моментом, когда приходит запрос, и моментом, когда отправляется ответ. Каждый middleware может прочитать или изменить req и res, завершить запрос, отправив ответ, или вызвать next(), чтобы передать запрос следующей функции в очереди. app.use(fn) добавляет middleware для каждого запроса, и Express выполняет всё в том порядке, в котором вы это зарегистрировали.

Для каждого запроса Console по порядку показывает три шага, а маршрут читает req.startedAt, свойство, которое добавил логгер. Отправьте GET /missing: оба middleware всё равно пишут в лог, потому что app.use() подходит для любого пути, а затем Express отвечает 404, так как ни один маршрут не совпал.

Забытый next()

Middleware, который не отправляет ответ и не вызывает next(), оставляет запрос висеть. Клиент ждёт до таймаута, а в логах ничего не появляется, поэтому это частая ошибка с middleware. Удалите next(); из logger выше и снова отправьте GET /: запрос так и не получит ответа.

Правило простое: каждый путь через middleware заканчивается ровно одним из этого:

  • next(), чтобы продолжить,
  • next(err), чтобы перейти к обработчику ошибок (см. Обработка ошибок в Express),
  • ответ, например res.json() или res.status(401).send().

Вызов next() после отправки ответа тоже ошибка. Следующий обработчик пытается отправить второй ответ, и Express выбрасывает ERR_HTTP_HEADERS_SENT. Пишите return res.status(401).json(...), чтобы функция на этом остановилась.

Проверка авторизации, которая останавливает запрос

Middleware, который отклоняет запрос, сам отправляет ответ и никогда не вызывает next(). Передайте его маршруту дополнительным аргументом, чтобы защитить только этот маршрут:

Первый запрос получает 401 с {"error":"Missing or wrong API key"}. Добавьте в строке запроса заголовок x-api-key: secret123 и отправьте снова: теперь ответ {"users":42,"caller":"reporting-app"}. На /public проверка не выполняется никогда.

Где выполняется middleware

Вы пишетеВыполняется для
app.use(fn)Каждого запроса, любого метода
app.use('/api', fn)Путей, которые начинаются с /api
app.get('/admin', fn, handler)Только GET /admin, до handler
router.use(fn)Каждого маршрута этого router'а

Порядок входит в смысл. Логгер, зарегистрированный после маршрутов, никогда не запишет запросы, на которые эти маршруты ответили, потому что маршрут, отправивший ответ, не вызывает next(). Сначала регистрируйте middleware для всего приложения, такие как логирование, разбор тела и CORS, затем маршруты, затем обработчики 404 и ошибок.

Встроенные middleware

В Express есть несколько функций middleware, которые подключаются через app.use(). Парсеры тела заполняют req.body; без них там undefined.

MiddlewareЧто делает
express.json()Разбирает JSON-тела (Content-Type: application/json)
express.urlencoded()Разбирает отправку HTML-форм (name=Ada&age=36)
express.text()Кладёт текстовое тело в req.body строкой
express.static('public')Отдаёт файлы из папки

Отправьте POST /echo с телом {"name": "Ada"}, и в ответе будет "body":{"name":"Ada"}. Удалите строку с express.json(), отправьте снова, и body в ответе пропадёт: req.body равен undefined, а JSON.stringify выбрасывает значения undefined. Пакеты cors, helmet и morgan тоже middleware: вы ставите их через npm install и так же передаёте в app.use(). Отдачу файлов описывает страница про express.static.

Частые ошибки

Вызов middleware вместо передачи. app.use(logger()) выполняет logger один раз при запуске и регистрирует то, что он вернул. Пишите app.use(logger). (У express.json() есть скобки, потому что он возвращает функцию middleware.)

Парсер тела добавлен после маршрутов. Маршруты выполняются первыми и видят req.body как undefined.

Ожидание, что app.use('/admin', fn) совпадает с одним путём. Он срабатывает и для /admin/users, и для любого другого пути под /admin, для всех методов. Чтобы защитить один маршрут, передайте middleware в app.get('/admin', fn, handler).

Часто задаваемые вопросы

Чем middleware отличается от обработчика маршрута?

Технически ничем: и то и другое функции (req, res, next). По соглашению обработчик маршрута завершает запрос ответом, а middleware обычно делает общую работу и вызывает next().

Может ли middleware быть async?

Да. В Express 5 async middleware, который выбрасывает ошибку или возвращает отклонённый промис, автоматически отправляет ошибку в ваш обработчик ошибок. Дождитесь через await всего, что нужно, а потом вызовите next().

Что делает next('route')?

Пропускает оставшиеся обработчики текущего маршрута и переходит к следующему подходящему маршруту. Работает только в обработчиках, добавленных через app.get(), app.post() и другие функции методов, но не в app.use().

Как пропустить middleware для одного маршрута?

Зарегистрируйте этот маршрут до middleware, чтобы он ответил первым, или проверяйте путь внутри middleware (if (req.path === '/health') return next();). Часто проблема исчезает, если подключать middleware к отдельному router'у или маршруту, а не ко всему приложению.

Иллюстрация языков программирования Coddy

Учитесь программировать с Coddy

Начать