请求生命周期
Nest 应用程序按照我们称之为请求生命周期的顺序处理请求并生成响应。由于使用了中间件、管道、守卫和拦截器,要追踪特定代码在请求生命周期中的执行位置可能颇具挑战性,尤其是当全局、控制器级别和路由级别的组件共同作用时。总的来说,请求依次流经中间件、守卫、拦截器、管道,最后在返回路径上(生成响应时)再次经过拦截器。
中间件
中间件按特定顺序执行。首先,Nest 运行全局绑定的中间件(例如通过 app.use 绑定的中间件),然后运行基于路径确定的 module bound middleware。中间件按照绑定的顺序依次执行,这与 Express 中中间件的工作方式类似。对于跨不同模块绑定的中间件,绑定到根模块的中间件会首先运行,然后中间件按照模块添加到 imports 数组中的顺序依次运行。
守卫
守卫的执行从全局守卫开始,然后是控制器守卫,最后是路由守卫。与中间件一样,守卫按照绑定的顺序执行。例如:
Guard1 将在 Guard2 之前执行,而这两者都将在 Guard3 之前执行。
info 提示 关于全局绑定与控制器或局部绑定的区别,关键在于守卫(或其他组件)的绑定位置。如果您使用
app.useGlobalGuards()或通过模块提供该组件,则它是全局绑定的。否则,如果装饰器位于控制器类之前,则绑定到控制器;如果装饰器位于路由声明之前,则绑定到路由。
拦截器
拦截器在很大程度上遵循与守卫相同的模式,但有一个区别:由于拦截器返回 RxJS Observables,Observable 将按照先进后出的方式解析。因此,入站请求将经过标准的全局、控制器、路由级别的解析,但请求的响应侧(即从控制器方法处理器返回之后)将从路由到控制器再到全局进行解析。此外,管道、控制器或服务抛出的任何错误都可以在拦截器的 catchError 操作符中读取。
管道
管道遵循标准的全局到控制器再到路由绑定的顺序,对于 @UsePipes() 参数同样采用先进先出的方式。然而,在路由参数级别,如果有多个管道运行,它们将按照从最后一个带管道的参数到第一个参数的顺序执行。这也适用于路由级别和控制器级别的管道。例如,如果我们有以下控制器:
那么 GeneralValidationPipe 将针对 query 运行,然后是 params,接着是 body 对象,然后才进入 RouteSpecificPipe,后者遵循相同的顺序。如果存在任何参数特定的管道,它们将在控制器和路由级别的管道之后运行(同样是从最后一个参数到第一个参数)。
过滤器
过滤器是唯一不先解析全局组件的组件。相反,过滤器从最低级别开始解析,这意味着执行从任何路由绑定的过滤器开始,然后依次是控制器级别,最后是全局过滤器。请注意,异常不能在过滤器之间传递;如果路由级别的过滤器捕获了异常,控制器或全局级别的过滤器就无法捕获同一个异常。要实现类似效果,唯一的方法是在过滤器之间使用继承。
info 提示 过滤器仅在请求过程中发生任何未捕获异常时执行。已捕获的异常(例如通过
try/catch捕获的异常)不会触发异常过滤器。一旦遇到未捕获的异常,生命周期的其余部分将被忽略,请求直接跳到过滤器。从 middleware 抛出的异常也由异常层处理,但只有全局异常过滤器适用,因为中间件在路由处理器被选中之前运行。
总结
总的来说,请求生命周期如下所示:
- 传入请求
- 中间件
- 2.1. 全局绑定的中间件
- 2.2. 模块绑定的中间件
- 守卫
- 3.1 全局守卫
- 3.2 控制器守卫
- 3.3 路由守卫
- 拦截器(控制器之前)
- 4.1 全局拦截器
- 4.2 控制器拦截器
- 4.3 路由拦截器
- 管道
- 5.1 全局管道
- 5.2 控制器管道
- 5.3 路由管道
- 5.4 路由参数管道
- 控制器(方法处理器)
- 服务(如果存在)
- 拦截器(请求之后)
- 8.1 路由拦截器
- 8.2 控制器拦截器
- 8.3 全局拦截器
- 异常过滤器
- 9.1 路由
- 9.2 控制器
- 9.3 全局
- 服务器响应

