Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Промежуточные обработчики (middleware)

Промежуточный обработчик (middleware) — функция, которая получает запрос раньше контроллера. Это те же middleware, что в Express: у них есть объекты req, res и функция next(), передающая запрос дальше. Их используют для общих задач — записи запросов в журнал, установки заголовков, подсчёта времени.

Создание

nest g middleware common/middleware/request-log --flat
// src/common/middleware/request-log.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
 
@Injectable()
export class RequestLogMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`${req.method} ${req.originalUrl}`);
    next();
  }
}

Класс реализует интерфейс NestMiddleware с методом use(). Вызов next() обязателен: без него запрос так и останется в обработчике.

Подключение

У middleware нет места в декораторе @Module(). Его подключают в методе configure() модуля:

// src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { RequestLogMiddleware } from './common/middleware/request-log.middleware';
import { PostsModule } from './posts/posts.module';
 
@Module({
  imports: [PostsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(RequestLogMiddleware).forRoutes('posts');
  }
}

apply() называет обработчик, forRoutes() — адреса, для которых он работает. Откройте /posts и /posts/1 — в терминале сервера появятся строки GET /posts и GET /posts/1.

Выбор маршрутов

// src/app.module.ts
import { MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { PostsController } from './posts/posts.controller';
 
configure(consumer: MiddlewareConsumer) {
  consumer
    .apply(RequestLogMiddleware)
    .exclude({ path: 'posts/popular', method: RequestMethod.GET })
    .forRoutes(PostsController);
}

В forRoutes() можно передать контроллер — тогда обработчик сработает для всех его маршрутов. exclude() убирает отдельные адреса. Несколько обработчиков перечисляют через запятую: apply(First, Second).

Функция вместо класса

Если обработчику не нужны сервисы, достаточно обычной функции:

// src/common/middleware/powered-by.middleware.ts
import { NextFunction, Request, Response } from 'express';
 
export function poweredBy(req: Request, res: Response, next: NextFunction) {
  res.setHeader('X-Powered-By', 'Blog API');
  next();
}

Её подключают так же: consumer.apply(poweredBy).forRoutes('posts'). Для всего приложения готовые Express-обработчики можно включить в src/main.ts через app.use().

Middleware или другие инструменты

Middleware ничего не знает о том, какой контроллер и метод будут вызваны. Поэтому проверку доступа удобнее делать гардами, а изменение ответа — перехватчиками: оба видят, какой именно метод обрабатывает запрос. Эти инструменты описаны в следующих уроках.

Частая ошибка

Забытый вызов next() — запрос «повисает»: клиент ждёт ответа, пока не сработает тайм-аут, а в журнале нет ни одной ошибки. Если после подключения middleware сервер перестал отвечать, первым делом проверьте, что next() вызывается во всех ветках кода.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.