1. 请求生命周期概述
NestJS请求处理的生命周期按顺序包含以下组件:
- 中间件 (Middleware)
- 守卫 (Guards)
- 拦截器 (Interceptors) - 请求阶段
- 管道 (Pipes)
- 路由处理器 (Controller Method)
- 拦截器 (Interceptors) - 响应阶段(逆序执行)
- 异常过滤器 (Exception Filters) - 在任何阶段出现异常时触发
2. 中间件 (Middleware)
中间件是请求处理的第一道关卡,可以访问请求和响应对象,适合处理日志、认证等通用功能。
全局注册
方法1: 在main.ts中使用app.use()
// main.ts
const app = await NestFactory.create(AppModule);
app.use(loggerMiddleware);
注意: 通过app.use()注册的中间件总是先于在模块中通过configure()注册的中间件执行。
方法2: 在AppModule中实现NestModule接口
// app.module.ts
@Module({})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('*'); // 应用到所有路由
}
}
局部注册
// app.module.ts
@Module({})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats'); // 特定路由
// 或者
// .forRoutes({ path: 'cats', method: RequestMethod.GET });
// 或者
// .forRoutes(CatsController);
}
}
函数式中间件
// 函数式中间件
export function loggerMiddleware(req: Request, res: Response, next: NextFunction) {
console.log(`请求路径: ${req.path}`);
next();
}
多个中间件链式应用
consumer
.apply(cors(), helmet(), logger)
.forRoutes('*');
排除特定路由
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'health', method: RequestMethod.GET },
{ path: 'api/docs', method: RequestMethod.ALL }
)
.forRoutes('*');
3. 守卫 (Guards)
守卫主要负责授权决策,决定请求是否可以继续处理。
全局注册
方法1: 在main.ts中使用useGlobalGuards()
// main.ts
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());
方法2: 使用APP_GUARD令牌(支持依赖注入)
// 可以在任何模块中注册,不必只在AppModule
import { APP_GUARD } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AppModule {}
局部注册
控制器级别:
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {}
方法级别:
@Post()
@UseGuards(RolesGuard)
create(@Body() createCatDto: CreateCatDto) {}
使用反射器传递元数据
// roles.decorator.ts
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
// 控制器中使用
@Post()
@Roles('admin')
create() {}
// 守卫中获取元数据
@Injectable()
export class RolesGuard {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// 从方法获取元数据
const methodRoles = this.reflector.get<string[]>('roles', context.getHandler());
// 从控制器获取元数据
const controllerRoles = this.reflector.get<string[]>('roles', context.getClass());
// 合并控制器和方法的元数据(优先使用方法的元数据)
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return roles.some((role) => user.roles?.includes(role));
}
}
不同上下文中的守卫
// 支持不同类型的应用(HTTP、微服务、WebSocket)
@Injectable()
export class AuthGuard {
canActivate(context: ExecutionContext): boolean {
const contextType = context.getType();
if (contextType === 'http') {
// HTTP处理逻辑
const request = context.switchToHttp().getRequest();
return this.validateRequest(request);
}
else if (contextType === 'rpc') {
// 微服务处理逻辑
const data = context.switchToRpc().getData();
return this.validateRpcData(data);
}
else if (contextType === 'ws') {
// WebSocket处理逻辑
const client = context.switchToWs().getClient();
return this.validateWsConnection(client);
}
}
}
4. 拦截器 (Interceptors)
拦截器可以在请求和响应的处理前后添加逻辑,适合用于日志、转换响应等场景。
全局注册
方法1: 在main.ts中使用useGlobalInterceptors()
// main.ts
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
方法2: 使用APP_INTERCEPTOR令牌(支持依赖注入)
// 可以在任何模块中注册,不必只在AppModule
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}
使用APP_INTERCEPTOR令牌注册的拦截器只需要在一个模块中注册一次即可。可以在主模块或专门的拦截器模块中注册:
// interceptors.module.ts
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from './logging.interceptor';
import { TimeoutInterceptor } from './timeout.interceptor';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
{
provide: APP_INTERCEPTOR,
useClass: TimeoutInterceptor, // 可以注册多个拦截器
},
],
})
export class InterceptorsModule {}
注意: 当注册多个拦截器时,请求阶段按注册顺序执行,响应阶段按相反顺序执行。这是NestJS的特殊设计,以便拦截器可以按"洋葱模型"工作。
局部注册
控制器级别:
@Controller('cats')
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
方法级别:
@Get()
@UseInterceptors(LoggingInterceptor)
findAll() {}
响应映射与转换
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map(data => ({
statusCode: context.switchToHttp().getResponse().statusCode,
data,
timestamp: new Date().toISOString(),
})),
);
}
}
缓存实现
@Injectable()
export class CacheInterceptor implements NestInterceptor {
private readonly cacheMap = new Map();
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const key = this.buildKey(context);
if (this.cacheMap.has(key)) {
return of(this.cacheMap.get(key));
}
return next.handle().pipe(
tap(response => this.cacheMap.set(key, response)),
);
}
private buildKey(context: ExecutionContext): string {
const request = context.switchToHttp().getRequest();
return `${request.method}-${request.url}`;
}
}
超时拦截器
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
timeout(5000),
catchError(err => {
if (err instanceof TimeoutError) {
return throwError(() => new RequestTimeoutException());
}
return throwError(() => err);
}),
);
}
}
5. 管道 (Pipes)
管道用于数据转换和验证,在控制器方法执行前处理参数。
全局注册
方法1: 在main.ts中使用useGlobalPipes()
// main.ts
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
方法2: 使用APP_PIPE令牌(支持依赖注入)
// 可以在任何模块中注册,不必只在AppModule
import { APP_PIPE } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe,
},
],
})
export class AppModule {}
局部注册
控制器级别:
@Controller('cats')
@UsePipes(ValidationPipe)
export class CatsController {}
方法级别:
@Post()
@UsePipes(ValidationPipe)
create(@Body() createCatDto: CreateCatDto) {}
参数级别:
@Post()
create(@Body(ValidationPipe) createCatDto: CreateCatDto) {}
自定义验证管道(支持异步)
@Injectable()
export class CustomValidationPipe implements PipeTransform {
// 支持同步方式
transform(value: any, metadata: ArgumentMetadata) {
const { metatype } = metadata;
if (!metatype || !this.toValidate(metatype)) {
return value;
}
// 自定义验证逻辑
if (!value || Object.keys(value).length === 0) {
throw new BadRequestException('值不能为空');
}
return value;
}
// 也支持异步方式
async transformAsync(value: any, metadata: ArgumentMetadata) {
const { metatype } = metadata;
// 异步验证
const result = await this.validator.validate(value);
if (result.errors.length > 0) {
throw new BadRequestException('验证失败');
}
return value;
}
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
全局启用验证器转换
// main.ts
app.useGlobalPipes(
new ValidationPipe({
transform: true, // 自动转换类型
whitelist: true, // 自动移除非DTO中定义的属性
forbidNonWhitelisted: true, // 当存在非白名单属性时抛出错误
transformOptions: {
enableImplicitConversion: true, // 启用隐式类型转换
},
}),
);
模式验证管道
import Joi from 'joi';
@Injectable()
export class JoiValidationPipe implements PipeTransform {
constructor(private schema: Joi.Schema) {}
transform(value: any) {
const { error, value: validatedValue } = this.schema.validate(value);
if (error) {
throw new BadRequestException('验证失败');
}
return validatedValue;
}
}
// 使用
@Post()
create(
@Body(new JoiValidationPipe(createCatSchema)) createCatDto: CreateCatDto,
) {}
6. 异常过滤器 (Exception Filters)
异常过滤器捕获应用程序中抛出的异常,并返回自定义响应。
全局注册
方法1: 在main.ts中使用useGlobalFilters()
// main.ts
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
方法2: 使用APP_FILTER令牌(支持依赖注入)
// 可以在任何模块中注册,不必只在AppModule
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
局部注册
控制器级别:
@Controller('cats')
@UseFilters(HttpExceptionFilter)
export class CatsController {}
方法级别:
@Post()
@UseFilters(HttpExceptionFilter)
create(@Body() createCatDto: CreateCatDto) {}
注意: 异常过滤器的执行顺序与其他组件不同。首先执行方法级过滤器,然后是控制器级,最后是全局过滤器。这样设计是为了让更具体的过滤器有机会先处理异常。
全局异常处理并记录日志
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
constructor(private logger: LoggerService) {}
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const errorResponse = {
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
method: request.method,
message: exception.message || '服务器内部错误',
};
this.logger.error(
`${request.method} ${request.url}`,
exception.stack,
'ExceptionFilter',
);
response.status(status).json(errorResponse);
}
}
特定异常过滤器
@Catch(QueryFailedError, TypeORMError)
export class DatabaseExceptionFilter implements ExceptionFilter {
catch(exception: QueryFailedError | TypeORMError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
response.status(HttpStatus.BAD_REQUEST).json({
statusCode: HttpStatus.BAD_REQUEST,
message: '数据库操作错误',
error: exception.message,
});
}
}
WebSocket异常过滤器
@Catch()
export class WsExceptionFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const client = host.switchToWs().getClient();
const data = host.switchToWs().getData();
const error = {
statusCode: HttpStatus.BAD_REQUEST,
message: exception.message || '错误',
data,
};
client.emit('exception', error);
}
}
7. 执行顺序与性能考虑
执行顺序
NestJS请求生命周期的执行顺序:
-
中间件
- 先执行
main.ts中通过app.use()注册的全局中间件 - 然后执行模块中通过
configure()注册的中间件
- 先执行
-
守卫
- 全局守卫
- 控制器级守卫
- 路由级守卫
-
拦截器(请求阶段)
- 全局拦截器(按注册顺序)
- 控制器级拦截器
- 路由级拦截器
-
管道
- 全局管道
- 控制器级管道
- 路由级管道
- 参数级管道
-
路由处理器(控制器方法)
-
拦截器(响应阶段,以相反顺序)
- 路由级拦截器
- 控制器级拦截器
- 全局拦截器(按注册的相反顺序)
-
异常过滤器(如果有异常抛出)
- 方法级过滤器
- 控制器级过滤器
- 全局过滤器
注意:在同一类别中(如全局守卫、全局拦截器等),组件的执行顺序通常按照它们的注册顺序。
性能考虑
对于性能敏感的应用,应该考虑在哪个生命周期阶段执行特定逻辑:
- 中间件适合处理认证、日志等所有请求通用的操作
- 守卫适合处理授权逻辑
- 拦截器适合处理响应转换和日志记录
- 管道适合处理输入验证和转换
依赖注入注意事项
在main.ts中使用useGlobal*方法注册的组件不支持依赖注入,因为它们在NestJS IoC容器外部实例化。
要启用依赖注入,必须使用以下方法之一:
- 使用APP_*令牌在模块的providers中注册(推荐方式)
- 在main.ts中使用应用程序上下文手动获取实例:
const app = await NestFactory.create(AppModule);
const authGuard = app.get(AuthGuard);
app.useGlobalGuards(authGuard);
特别说明:使用APP_*令牌注册时,如果同一类型的令牌注册了多个实现(如多个APP_INTERCEPTOR),它们都会被注册为全局组件。这是NestJS的特殊设计,方便模块化开发。
8. 综合案例
下面是一个综合使用各种生命周期组件的案例:
// app.module.ts
@Module({
imports: [],
providers: [
// 配置全局验证管道
{
provide: APP_PIPE,
useValue: new ValidationPipe({
transform: true,
whitelist: true,
}),
},
// 配置全局异常过滤器
{
provide: APP_FILTER,
useClass: GlobalExceptionFilter,
},
// 配置全局守卫
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
// 配置全局拦截器
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
{
provide: APP_INTERCEPTOR,
useClass: TransformInterceptor,
},
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// 配置全局中间件
consumer
.apply(LoggerMiddleware, HelmetMiddleware)
.forRoutes('*');
// 为特定路由配置中间件
consumer
.apply(AuthMiddleware)
.exclude('auth/login', 'auth/register')
.forRoutes('*');
}
}
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 配置全局中间件
app.use(cors());
app.use(helmet());
await app.listen(3000);
}
bootstrap();
在这个例子中,我们:
- 使用全局中间件处理跨域和安全头
- 添加全局JWT守卫进行认证
- 添加全局验证管道处理输入验证
- 添加全局异常过滤器统一处理错误
- 添加全局日志拦截器和响应转换拦截器