Express / 38 минут
Порядок middleware
Подготовьте данные перед обработчиком и передайте управление.
- Подготовьте данные перед обработчиком и передайте управление.
- Применить тему в задаче «Порядок middleware» и объяснить граничные случаи.
Около 8 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.
Цепочка обработки одного запроса
Express-приложение можно представить как упорядоченную цепочку функций. Каждая получает запрос, ответ и способ передать управление дальше. Middleware может добавить контекст, проверить условие, разобрать тело или завершить ответ. Конкретное место регистрации определяет, какие действия уже произошли и какие ещё доступны. Это не произвольный набор независимых плагинов.
Обычный middleware обязан выбрать понятный путь: закончить ответ, передать запрос через next либо передать ошибку. Если функция просто завершилась без ответа и next, клиент будет ждать. Если функция отправила ответ, но продолжила цепочку без необходимости, следующий слой может попытаться ответить второй раз. Контроль потока здесь является частью корректности HTTP-программы.
Порядок парсера и маршрута
express.json разбирает подходящее JSON-тело и делает результат доступным через req.body. Он должен сработать до маршрута, который использует body. При этом парсер проверяет синтаксис и размер, а не бизнес-форму объекта. Если Content-Type не подходит или тела нет, обработчик не должен безусловно ожидать объект с нужными полями.
app.use(express.json({ limit: '16kb' }));
app.post('/notes', (req, res) => {
if (typeof req.body?.title !== 'string') return res.sendStatus(400);
res.status(201).json({ title: req.body.title });
});Ограничение размера — часть внешней границы. Не увеличивайте его без необходимости ради одного неожиданного запроса. Разные типы загрузки могут требовать отдельных маршрутов и специализированного парсера. Нельзя считать все входные данные безопасными только потому, что один JSON-parser уже зарегистрирован глобально.
Контекст ответа и res.locals
res.locals предназначен для данных конкретного цикла запроса и ответа. Он удобен для промежуточной информации, которую следующий обработчик должен получить от предыдущего. В отличие от глобальной переменной, такой контекст не предназначен для случайного обмена текущим пользователем между независимыми запросами.
app.use((req, res, next) => {
res.locals.requestStartedAt = Date.now();
next();
});
app.get('/time', (_req, res) => {
res.json({ startedAt: res.locals.requestStartedAt });
});Выбирайте минимальные данные контекста и понятные имена. Не копируйте в locals весь объект credentials или большой body «на всякий случай». Серверные логи и публичный ответ также имеют разные назначения. Данные, доступные следующему слою приложения, не обязаны автоматически попадать клиенту.
Область действия middleware
app.use может ограничить слой префиксом пути. Router позволяет сгруппировать связанные маршруты и их локальные middleware. Проверка прав только на одном endpoint не защищает соседние операции, поэтому область регистрации должна соответствовать набору действительно защищённых действий. При этом не нужно выполнять дорогую проверку для всех статических ресурсов без требований.
const api = express.Router();
api.use((req, res, next) => {
res.locals.apiVersion = 1;
next();
});
api.get('/info', (_req, res) => res.json({ version: res.locals.apiVersion }));
app.use('/api', api);Вложенность префиксов должна оставаться читаемой. Если router уже смонтирован на /api, не повторяйте этот же префикс внутри каждого его пути. При переносе маршрутов проверяйте итоговый URL и порядок локальных проверок через HTTP-тест, а не только визуально по именам файлов.
Ошибки и асинхронная работа
Error middleware отличается четырьмя параметрами err, req, res, next и обычно ставится после обычных маршрутов. В Express 5 отклонённый Promise возвращённого async-handler передаётся в ошибочную цепочку. Но асинхронная работа, оторванная от возвращаемого Promise, не становится автоматически частью этой обработки.
app.use((err, req, res, next) => {
if (res.headersSent) return next(err);
res.status(500).json({ error: 'internal' });
});Не удаляйте формально неиспользуемый четвёртый параметр без понимания: сигнатура участвует в распознавании error middleware. Публичный ответ должен оставаться безопасным, а подробная диагностика — в контролируемых логах. Если заголовки уже отправлены, новая попытка res.json не является корректным восстановлением ответа.
Практика передачи tag
По условию первый middleware записывает переданный tag в res.locals, вызывает next, а GET /tag читает это значение и возвращает JSON. Нельзя использовать один глобальный tag для всех запросов или пропустить HTTP-слой, просто вернув {tag} из solve. Проверяется именно передача локального контекста по цепочке.
Проверьте несколько независимых значений и пустую строку, если она допустима. Затем поменяйте порядок middleware и маршрута и объясните получившееся отсутствие данных. Для самостоятельной тренировки добавьте ранний отказ на неверный заголовок и убедитесь, что основной handler действительно не вызывается после завершения ошибочного ответа.