Nest应用程序处理请求并生成回应的过程被称为请求生命周期。使用中间件、管道、守卫和拦截器时,要在请求生命周期中追踪特定的代码片段的执行很困难,尤其是在全局、控制器或者路由的部件中。一般来说,一个请求流经中间件、守卫与拦截器,然后到达管道,并最终回到拦截器中的返回路径中(从而产生响应)。
中间件
中间件以特殊的顺序执行。首先,Nest运行全局绑定的中间件(例如app.use中绑定的中间件),然后运行在路径中指定的模块绑定的中间件。中间件以他们绑定的次序顺序执行,这和在Express中的中间件工作原理是类似的。
守卫
守卫的执行首先从全局守卫开始,然后处理控制器守卫,最后是路径守卫。和中间件一样,守卫的执行也和他们的绑定顺序一致。例如:
@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.useGlobalGuard()或者通过模块提供一个部件,它就是全局绑定的。否则,当一个装饰器在控制器类之前时,它就是绑定在控制器上的,当装饰器在路径声明之前时它就是绑定在路径上的。
拦截器
拦截器在大部分情况下和守卫类似。只有一种情况例外:当拦截器返回的是一个RxJS Observables时,observables是以先进后出的顺序执行的。因此,入站请求是按照标准的全局、控制器和路由层次执行的,但请求的响应侧(例如,当从一个控制器方法的处理器返回时)则是从路由到控制器再到全局。另外,由管道、控制器或者服务抛出的任何错误都可以在拦截器的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语句不会触发过滤器。一旦遇到未处理的异常,请求接下来的生命周期会被忽略并直接跳转到过滤器。
总结
一般来说,请求生命周期大致如下:
- 收到请求
- 全局绑定的中间件
- 模块绑定的中间件
- 全局守卫
- 控制层守卫
- 路由守卫
- 全局拦截器(控制器之前)
- 控制器层拦截器 (控制器之前)
- 路由拦截器 (控制器之前)
- 全局管道
- 控制器管道
- 路由管道
- 路由参数管道
- 控制器(方法处理器)
15。服务(如果有) - 路由拦截器(请求之后)
- 控制器拦截器 (请求之后)
- 全局拦截器 (请求之后)
- 异常过滤器 (路由,之后是控制器,之后是全局)
- 服务器响应
