Skip to content
On this page

NestJS 中间件 (Middleware)

NestJS 中间件是在路由处理程序之前调用的函数。中间件函数能够访问请求对象(req), 响应对象(res), 和应用请求-响应循环中的下一个中间件函数。下一个中间件函数通常由 next() 函数表示。

基础概念

NestJS 中间件本质上等同于 Express 中间件。中间件函数可以执行以下任务:

  1. 执行任何代码
  2. 修改请求和响应对象
  3. 终止请求-响应循环
  4. 调用堆栈中的下一个中间件函数
  5. 如果当前中间件函数没有终止请求-响应循环,它必须调用 next() 以将控制权传递给下一个中间件函数。否则,请求将被挂起

创建中间件

让我们创建一个简单的日志中间件:

typescript
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...', req.method, req.path);
    next();
  }
}

应用中间件

NestJS 提供了多种方式来应用中间件。

在模块中应用

在模块中使用 configure() 方法来应用中间件:

typescript
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
import { CatsController } from './cats/cats.controller';

@Module({
  imports: [],
  controllers: [CatsController],
  providers: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('cats', 'dogs');
  }
}

应用到所有路由

typescript
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('*'); // 应用到所有路由
  }
}

使用排除规则

typescript
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .exclude(
        { path: 'cats', method: RequestMethod.GET },
        { path: 'cats/:id', method: RequestMethod.GET },
      )
      .forRoutes(CatsController);
  }
}

多个中间件

可以同时应用多个中间件:

typescript
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware, CorsMiddleware, AuthMiddleware)
      .forRoutes('cats');
  }
}

功能中间件

除了类中间件,NestJS 还支持函数中间件:

typescript
import { Request, Response, NextFunction } from 'express';

export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`Request... ${req.method} ${req.path}`);
  next();
}

// 在模块中使用
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(logger)
      .forRoutes('cats');
  }
}

参数化中间件

可以通过工厂函数创建参数化的中间件:

typescript
export function createLoggingMiddleware(prefix: string) {
  return class LoggingMiddleware implements NestMiddleware {
    use(req: Request, res: Response, next: NextFunction) {
      console.log(`${prefix}: ${req.method} ${req.path}`);
      next();
    }
  };
}

// 使用
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    const LoggingMiddleware = createLoggingMiddleware('INFO');
    consumer
      .apply(LoggingMiddleware)
      .forRoutes('cats');
  }
}

内置中间件

NestJS 应用可以使用 Express 提供的任何第三方中间件:

bash
npm install --save compression helmet cors
npm install --save-dev @types/compression @types/helmet @types/cors
typescript
import * as compression from 'compression';
import * as helmet from 'helmet';
import * as cors from 'cors';

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(helmet(), cors(), compression())
      .forRoutes('*');
  }
}

中间件最佳实践

1. 中间件链

中间件按顺序执行,因此要注意中间件的顺序:

typescript
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(AuthMiddleware, ValidationMiddleware, LoggerMiddleware) // 注意顺序
      .forRoutes('cats');
  }
}

2. 异步中间件

如果中间件需要执行异步操作,可以使用 Promise 或 async/await:

typescript
@Injectable()
export class AsyncMiddleware implements NestMiddleware {
  async use(req: Request, res: Response, next: NextFunction) {
    try {
      // 异步操作
      const data = await someAsyncOperation();
      req.customData = data;
      next();
    } catch (error) {
      next(error);
    }
  }
}

3. 错误处理中间件

typescript
@Injectable()
export class ErrorHandlingMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    res.on('finish', () => {
      // 记录响应完成
      console.log(`Response finished: ${res.statusCode}`);
    });

    res.on('close', () => {
      // 连接关闭时
      console.log('Connection closed');
    });

    next();
  }
}

与守卫、拦截器的关系

中间件、守卫、拦截器在请求处理流程中的顺序:

  1. 中间件 - 最先执行,用于通用请求处理
  2. 守卫 - 用于权限控制
  3. 拦截器 - 用于请求/响应转换
  4. 管道 - 用于数据验证
  5. 控制器方法 - 最终处理请求

总结

NestJS 中间件提供了在请求到达控制器之前处理请求的能力。它们对于日志记录、认证、设置 CORS 等通用任务非常有用。中间件是 NestJS 应用程序中处理通用逻辑的重要组成部分。

中间件的主要特点:

  • 在路由处理程序之前执行
  • 可以访问请求和响应对象
  • 可以修改请求和响应对象
  • 可以终止请求-响应循环或传递控制权给下一个中间件
  • 可以应用到特定路由或所有路由