JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Middleware: концепция, порядок выполнения

Middleware: концепция, порядок выполнения

Модуль 4: Node.js, Next.js и Angular
6 уровень , 0 лекция
Открыта

1. Как работает middleware в Express.js

Middleware (по-русски часто называют "промежуточное ПО", "промежуточный обработчик" или просто "мидлварь") — это функция, которая стоит между запросом пользователя и вашим конечным обработчиком маршрута. Она может что-то сделать с запросом, изменить его, добавить данные, проверить права, записать лог, обработать ошибку — и только потом передать управление дальше.

В Express.js middleware — это обычная функция, которая принимает три (а иногда четыре) аргумента:

function myMiddleware(req, res, next) {
  // что-то делаем с req или res
  next(); // передаём управление дальше
}
  • req — объект запроса (Request).
  • res — объект ответа (Response).
  • next — специальная функция, которую нужно вызвать, чтобы передать управление следующему middleware или обработчику маршрута.

Если next() не вызвать — цепочка прервётся, и пользователь будет вечно ждать ответа (или получит таймаут). Это частая ошибка новичков.

Минимальный пример

const express = require('express');
const app = express();

function logger(req, res, next) {
  console.log(`${req.method} ${req.url}`);
  next(); // обязательно!
}

app.use(logger);

app.get('/', (req, res) => {
  res.send('Hello, Middleware!');
});

Здесь каждый запрос будет проходить через logger, который просто пишет в консоль метод и URL.

2. Порядок выполнения middleware: цепочка обработки

Порядок важен!
Express строит цепочку middleware в том порядке, в каком вы их объявили в коде.

  1. Запрос поступает в приложение.
  2. Первый middleware (app.use(...) или маршрутный) получает управление.
  3. Если он вызывает next(), управление передаётся следующему middleware.
  4. Так продолжается до тех пор, пока не будет отправлен ответ (res.send, res.json и т.д.) или не закончится цепочка.

Если какой-то middleware не вызовет next() и не отправит ответ — всё зависнет. Это как если бы гардеробщик в ресторане не отдал вам номерок и не пустил дальше.

Визуальная схема

[Запрос] → [Middleware 1] → [Middleware 2] → [Маршрут/обработчик] → [Ответ]

Пример с несколькими middleware

app.use((req, res, next) => {
  console.log('Первый мидлварь');
  next();
});

app.use((req, res, next) => {
  console.log('Второй мидлварь');
  next();
});

app.get('/', (req, res) => {
  res.send('Финальный ответ');
});

В консоли:

Первый мидлварь
Второй мидлварь

3. Типы middleware в Express

В Express.js есть несколько основных видов middleware:

  • Глобальные (application-level) — работают для всех маршрутов:
    app.use(middlewareFunction)
  • Маршрутные (router-level) — работают только для определённых маршрутов:
    app.use('/admin', adminMiddleware)
  • Встроенные (built-in) — например, express.json(), express.static()
  • Сторонние (third-party) — например, morgan, cors, helmet
  • Обработчики ошибок — специальные middleware с 4 аргументами (err, req, res, next)

Пример глобального и маршрутного middleware

// Глобальный — для всех маршрутов
app.use((req, res, next) => {
  console.log('Глобальный мидлварь');
  next();
});

// Только для /admin
app.use('/admin', (req, res, next) => {
  console.log('Мидлварь только для /admin');
  next();
});

app.get('/', (req, res) => res.send('Главная'));
app.get('/admin', (req, res) => res.send('Админка'));

4. Практические примеры: для чего нужен middleware

Логирование всех запросов

app.use((req, res, next) => {
  console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
  next();
});

Проверка авторизации

function checkAuth(req, res, next) {
  if (req.query.token === 'secret') {
    next();
  } else {
    res.status(401).send('Не авторизован');
  }
}

app.use('/private', checkAuth);

app.get('/private/data', (req, res) => {
  res.send('Секретные данные!');
});

Добавление данных в объект запроса

app.use((req, res, next) => {
  req.startTime = Date.now();
  next();
});

app.get('/', (req, res) => {
  const duration = Date.now() - req.startTime;
  res.send(`Время обработки: ${duration} мс`);
});

Использование стороннего middleware

const morgan = require('morgan');
app.use(morgan('dev'));

5. Как Express выбирает, какой middleware вызвать

Express идёт по цепочке middleware сверху вниз.
Если вы используете app.use(middleware), он будет вызван для любого запроса.
Если вы используете app.use('/path', middleware), он будет вызван только для маршрутов, начинающихся с /path.

Важно:
- Если путь совпал, middleware вызовется, и после next() цепочка продолжается.
- Если путь не совпал — middleware пропускается.
- Если вы отправили ответ (res.send и т.д.) — цепочка прерывается, остальные middleware не вызываются.

6. Реализация цепочки middleware в мини-приложении

Давайте доработаем наше учебное приложение — TODO-лист на Express (или любой другой пример из прошлых лекций).
Добавим логирование, проверку авторизации и измерение времени запроса через middleware.

const express = require('express');
const app = express();

// 1. Логирование
app.use((req, res, next) => {
  console.log(`[${new Date().toLocaleTimeString()}] ${req.method} ${req.url}`);
  next();
});

// 2. Авторизация для приватных маршрутов
app.use('/api/private', (req, res, next) => {
  if (req.query.token === '12345') {
    next();
  } else {
    res.status(401).json({ error: 'Unauthorized' });
  }
});

// 3. Засекаем время обработки
app.use((req, res, next) => {
  req.requestTime = Date.now();
  // Хитрость: слушаем событие "finish" на res, чтобы узнать, когда ответ отправлен
  res.on('finish', () => {
    const duration = Date.now() - req.requestTime;
    console.log(`Время обработки: ${duration} мс`);
  });
  next();
});

// Обычный маршрут
app.get('/api/todos', (req, res) => {
  res.json([
    { id: 1, text: 'Выучить middleware', done: false },
    { id: 2, text: 'Порадоваться успехам', done: false },
  ]);
});

// Приватный маршрут
app.get('/api/private/secret', (req, res) => {
  res.json({ secret: '42' });
});

app.listen(3000, () => console.log('Сервер запущен на http://localhost:3000'));

Как это работает?

  • Все запросы логируются.
  • Для маршрутов, начинающихся с /api/private, проверяется токен.
  • Для всех маршрутов измеряется время обработки.

7. Типичные ошибки при работе с middleware

Ошибка №1: забыли вызвать next()
Ваш middleware сделал свою работу, но не вызвал next() и не отправил ответ. В результате браузер будет вечно ждать ответа, а вы — искать баг.

Ошибка №2: отправили ответ, но вызвали next()
Если вы уже отправили ответ (res.send, res.json), не нужно вызывать next(), иначе следующий middleware может попытаться отправить ещё один ответ — Express ругнётся ошибкой "Can't set headers after they are sent".

Ошибка №3: неправильный порядок
Если вы подключили middleware после маршрутов, он не будет работать для уже обработанных запросов. Всегда объявляйте глобальные middleware до маршрутов!

Ошибка №4: забыли обработать ошибки
Обычные middleware не ловят ошибки, выброшенные внутри себя. Для этого нужен специальный error-handling middleware (о нём — в отдельной лекции).

Ошибка №5: не учитываете путь
Если вы хотите, чтобы middleware работал только для определённых маршрутов, не забудьте про второй аргумент в app.use('/path', middleware).

1
Задача
Модуль 4: Node.js, Next.js и Angular, 6 уровень, 0 лекция
Недоступна
Создание простого middleware и подключение к приложению
Создание простого middleware и подключение к приложению
1
Задача
Модуль 4: Node.js, Next.js и Angular, 6 уровень, 0 лекция
Недоступна
Порядок выполнения нескольких middleware
Порядок выполнения нескольких middleware
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ