请求生命周期
Nest 应用在处理请求并生成响应时,会经历一个我们称为请求生命周期的顺序流程。由于中间件、管道、守卫和拦截器都会参与其中,尤其当全局级、控制器级和路由级组件同时存在时,往往不容易判断某段代码究竟是在生命周期的哪个阶段执行的。总体来说,一个请求会依次经过中间件、守卫、拦截器、管道,然后在返回路径上再次经过拦截器,最终生成响应。
中间件
中间件按特定顺序执行。首先,Nest 会运行全局绑定的中间件(例如通过 app.use() 绑定的中间件),然后再运行按路径匹配的模块级中间件。中间件会按照它们被绑定的顺序依次执行,这一点与 Express 中间件的行为一致。如果中间件分布在不同模块中,则绑定在根模块上的中间件会先执行,随后再按模块在 imports 数组中的顺序依次执行。
守卫
守卫的执行顺序是:先执行全局守卫,再执行控制器守卫,最后执行路由守卫。与中间件类似,守卫也是按绑定顺序执行。例如:
@UseGuards(Guard1, Guard2)
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@UseGuards(Guard3)
@Get()
getCats(): Cats[] {
return this.catsService.getCats();
}
}Guard1 会先于 Guard2 执行,而它们二者又都会先于 Guard3 执行。
提示
所谓“全局绑定”与“控制器级 / 本地绑定”的区别,本质上在于组件是在哪里被绑定的。如果你使用的是 app.useGlobalGuards(),或者通过模块提供该组件,那么它就是全局绑定。否则,如果装饰器作用在控制器类上,它就是控制器级绑定;如果装饰器作用在某个路由处理方法上,它就是路由级绑定。
拦截器
拦截器在大部分情况下遵循与守卫相同的模式,但有一个关键区别:由于拦截器返回的是 RxJS Observable,因此这些 Observable 会以“后进先出”的方式解析。也就是说,请求进入时会按全局、控制器、路由的顺序依次经过拦截器;但响应返回时(即从控制器方法处理器返回之后),则会按路由、控制器、全局的顺序反向经过拦截器。
另外,管道、控制器或服务中抛出的任何错误,都可以在拦截器的 catchError 操作符中被读取到。
管道
管道遵循标准的“全局 -> 控制器 -> 路由”绑定顺序,并且对于 @UsePipes() 传入的多个管道,同样按先进先出的顺序执行。不过,在路由参数级别,如果你有多个参数管道,它们会按照“从最后一个带管道的参数到第一个参数”的顺序执行。这个规则同样会影响路由级和控制器级管道。比如下面这个控制器:
@UsePipes(GeneralValidationPipe)
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@UsePipes(RouteSpecificPipe)
@Patch(':id')
updateCat(
@Body() body: UpdateCatDTO,
@Param() params: UpdateCatParams,
@Query() query: UpdateCatQuery,
) {
return this.catsService.updateCat(body, params, query);
}
}那么 GeneralValidationPipe 会先作用于 query,再作用于 params,最后作用于 body,然后才轮到 RouteSpecificPipe,它也会按照同样的参数顺序执行。如果还定义了参数级别的管道,那么它们会在控制器级和路由级管道之后执行,并且同样遵循“从最后一个参数到第一个参数”的顺序。
过滤器
过滤器是唯一一个不是从全局优先开始解析的组件。相反,它总是从最底层开始解析,也就是说先执行路由级过滤器,再执行控制器级过滤器,最后才是全局过滤器。
需要注意的是,异常不会在过滤器之间继续传递。如果一个路由级过滤器已经捕获了该异常,那么控制器级或全局过滤器就无法再次捕获同一个异常。若你确实需要实现类似“逐层处理”的效果,唯一办法是让过滤器之间通过继承来复用行为。
提示
过滤器只会在请求过程中出现未捕获异常时执行。像 try/catch 这样已经被捕获的异常,不会触发异常过滤器。一旦遇到未捕获异常,请求生命周期中剩余的其他步骤都会被忽略,流程会直接跳转到过滤器。
总结
总体来说,一个请求的生命周期如下:
- 进入请求
- 中间件
- 全局中间件
- 模块级中间件
- 守卫
- 全局守卫
- 控制器守卫
- 路由守卫
- 拦截器(控制器前)
- 全局拦截器
- 控制器拦截器
- 路由拦截器
- 管道
- 全局管道
- 控制器管道
- 路由管道
- 路由参数管道
- 控制器(方法处理器)
- 服务(如果存在)
- 拦截器(请求后)
- 路由拦截器
- 控制器拦截器
- 全局拦截器
- 异常过滤器
- 路由级过滤器
- 控制器级过滤器
- 全局过滤器
- 服务器响应