请求生命周期概览
一、完整链路
HTTP 请求到达
│
▼
Middleware(中间件)
│ Express/Fastify 级别
│ 可修改 req/res,调用 next()
│ 无法访问 NestJS 路由元数据
▼
Guard(守卫)
│ 返回 true/false 决定是否放行
│ 可访问路由元数据(@Roles 等装饰器)
│ 返回 false → 自动抛出 403 ForbiddenException
▼
Interceptor(拦截器)—— 进入阶段
│ 基于 RxJS,在 Handler 前后均可插入逻辑
│ 日志、耗时统计、缓存、响应转换
▼
Pipe(管道)
│ 转换和校验参数(DTO 验证、类型转换)
│ 抛出 BadRequestException → 跳过 Handler
▼
Handler(控制器方法)
│ 业务逻辑处理
│ 抛出异常 → 跳过 Interceptor 返回阶段
▼
Interceptor(拦截器)—— 返回阶段
│ 统一包装响应格式、序列化
▼
ExceptionFilter(异常过滤器)
│ 捕获整个链路中抛出的所有异常
│ 统一格式化错误响应
▼
HTTP 响应返回二、每个环节的职责边界
| 环节 | 执行时机 | 核心能力 | 典型用途 |
|---|---|---|---|
| Middleware | Guard 之前 | 访问原始 req/res | 日志、CORS、压缩、限流 |
| Guard | Pipe 之前 | 读取路由元数据、决定放行 | 认证(JWT 验证)、授权(角色检查) |
| Interceptor(进入) | Handler 之前 | 包装 Observable、读上下文 | 耗时开始、缓存命中检测 |
| Pipe | Handler 参数绑定时 | 转换/验证参数值 | DTO 验证、类型转换、参数清洗 |
| Handler | — | 业务逻辑 | 调用 Service、返回数据 |
| Interceptor(返回) | Handler 之后 | 变换 Observable 结果 | 统一响应格式、序列化 |
| ExceptionFilter | 抛出异常时 | 捕获任何异常 | 统一错误格式、记录错误日志 |
三、如何选择用哪个环节
场景判断树:
需要修改原始 req/res 对象(如添加 traceId)?
→ Middleware
需要读取路由装饰器上的元数据(@Roles、@Public 等)?
→ Guard(守卫)或 Interceptor(不能是 Middleware)
需要在 Handler 执行前拦截并可能阻止执行?
→ Guard(返回 false)或 Pipe(抛出异常)
需要转换或验证 Handler 的输入参数?
→ Pipe
需要包装 Handler 的返回值?
→ Interceptor(返回阶段 map 操作符)
需要在执行前后都有逻辑(如计时)?
→ Interceptor(进入 + 返回两个阶段)
需要统一处理异常?
→ ExceptionFilter
需要同时保护多个路由(认证)?
→ Guard(全局注册)按常见需求对照:
| 需求 | 推荐环节 | 原因 |
|---|---|---|
| 记录请求日志 | Middleware | 需要在路由解析前就记录 |
| CORS / 请求压缩 | Middleware | Express/Fastify 级别处理 |
| JWT Token 验证 | Guard | 需要读取路由 @Public() 装饰器 |
| 角色权限检查 | Guard | 需要读取 @Roles() 装饰器 |
| 接口耗时统计 | Interceptor | 需要包裹整个 Handler 执行 |
统一响应格式 {code, data, message} | Interceptor | map 转换返回值 |
| 响应缓存 | Interceptor | 可短路 Handler,直接返回 |
| DTO 校验(字段格式、必填) | Pipe | ValidationPipe + class-validator |
| 路径参数转 number | Pipe | ParseIntPipe |
| 统一错误响应格式 | ExceptionFilter | 捕获所有未处理异常 |
四、绑定范围与优先级
每种机制都支持三个层级绑定,执行顺序:全局 → 控制器 → 路由:
typescript
// 1. 全局(main.ts)— 作用于所有路由
app.useGlobalGuards(new JwtAuthGuard());
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
app.useGlobalInterceptors(new ResponseInterceptor());
app.useGlobalFilters(new AllExceptionsFilter());
// 全局注册的缺点:无法注入 NestJS Provider(如 Reflector)
// 解决:通过 APP_GUARD 等令牌注册(可以注入依赖)
providers: [
{ provide: APP_GUARD, useClass: JwtAuthGuard },
{ provide: APP_INTERCEPTOR, useClass: ResponseInterceptor },
{ provide: APP_PIPE, useClass: ValidationPipe },
{ provide: APP_FILTER, useClass: AllExceptionsFilter },
]
// 2. 控制器级别
@UseGuards(JwtAuthGuard)
@UseInterceptors(LoggingInterceptor)
@Controller('users')
export class UsersController {}
// 3. 路由级别
@UseGuards(RolesGuard)
@UseFilters(HttpExceptionFilter)
@Get(':id')
findOne(@Param('id') id: string) {}同一层级多个绑定时,按声明顺序(从左到右/从上到下)执行:
typescript
@UseGuards(AuthGuard, RolesGuard)
// AuthGuard 先执行,通过后 RolesGuard 再执行五、异常传播规则
异常在链路中的传播:
Handler 抛出 NotFoundException
↓
跳过 Interceptor 返回阶段(pipe 操作符)
↓
被 ExceptionFilter 捕获
↓
返回 404 响应
Guard 抛出/返回 false
↓
跳过 Interceptor 进入阶段后的所有环节
↓
被 ExceptionFilter 捕获(ForbiddenException)
Interceptor 进入阶段抛出异常
↓
跳过 Handler
↓
被 ExceptionFilter 捕获六、调试技巧:追踪请求经过的每个环节
typescript
// 在每个环节打印日志,验证执行顺序
@Injectable()
export class DebugGuard implements CanActivate {
canActivate() {
console.log('[Guard] 执行');
return true;
}
}
@Injectable()
export class DebugInterceptor implements NestInterceptor {
intercept(ctx: ExecutionContext, next: CallHandler) {
console.log('[Interceptor] 进入');
return next.handle().pipe(
tap(() => console.log('[Interceptor] 返回')),
);
}
}
// 预期输出:
// [Middleware] 执行
// [Guard] 执行
// [Interceptor] 进入
// [Handler] 执行
// [Interceptor] 返回可运行 Demo:
practice/02-request-lifecycle— Middleware/Guard/Interceptor/Pipe 全链路追踪 Demo
常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
| 中间件不执行 | configure() 中路由写错或未 apply | 用 forRoutes('*') 先确认全局生效,再收窄范围 |
| Guard 在中间件之前报错 | 误解执行顺序 | 顺序:Middleware → Guard → Interceptor → Pipe → Handler |
| 异常过滤器捕获不到某个异常 | 过滤器注册层级低于抛出点 | 全局过滤器用 app.useGlobalFilters(),或在 Controller 级别注册 |