Appearance
NestJS 中间件 (Middleware)
NestJS 中间件是在路由处理程序之前调用的函数。中间件函数能够访问请求对象(req), 响应对象(res), 和应用请求-响应循环中的下一个中间件函数。下一个中间件函数通常由 next() 函数表示。
基础概念
NestJS 中间件本质上等同于 Express 中间件。中间件函数可以执行以下任务:
- 执行任何代码
- 修改请求和响应对象
- 终止请求-响应循环
- 调用堆栈中的下一个中间件函数
- 如果当前中间件函数没有终止请求-响应循环,它必须调用
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();
}
}
与守卫、拦截器的关系
中间件、守卫、拦截器在请求处理流程中的顺序:
- 中间件 - 最先执行,用于通用请求处理
- 守卫 - 用于权限控制
- 拦截器 - 用于请求/响应转换
- 管道 - 用于数据验证
- 控制器方法 - 最终处理请求
总结
NestJS 中间件提供了在请求到达控制器之前处理请求的能力。它们对于日志记录、认证、设置 CORS 等通用任务非常有用。中间件是 NestJS 应用程序中处理通用逻辑的重要组成部分。
中间件的主要特点:
- 在路由处理程序之前执行
- 可以访问请求和响应对象
- 可以修改请求和响应对象
- 可以终止请求-响应循环或传递控制权给下一个中间件
- 可以应用到特定路由或所有路由