请求生命周期

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 之前执行。

info 注意 当讨论全局绑定与控制器或本地绑定的区别时,关键在于守卫(或其他组件)绑定的位置。如果使用 app.useGlobalGuard() 或通过模块提供该组件,则为全局绑定。否则,装饰器位于控制器类前时绑定到控制器,位于路由声明前时则绑定到路由。

拦截器

拦截器在很大程度上遵循与守卫相同的模式,但有一点不同:由于拦截器返回 RxJS 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(遵循同样的顺序)。如果存在任何参数特定的管道,它们将在控制器和路由级别的管道之后运行(同样是从最后一个参数到第一个参数)。

过滤器

过滤器是唯一不优先解析全局组件的部分。相反,过滤器会从最低层级开始解析,这意味着执行首先从路由绑定的过滤器开始,然后是控制器级别,最后才是全局过滤器。需要注意的是异常无法在过滤器之间传递;如果路由级别的过滤器捕获了异常,控制器或全局级别的过滤器就无法捕获同一个异常。要实现类似效果的唯一方法是使用过滤器之间的继承关系。

info 提示 过滤器仅在请求过程中发生未捕获异常时才会执行。已捕获的异常(例如通过 try/catch 捕获的异常)不会触发异常过滤器。一旦遇到未捕获异常,请求将跳过剩余生命周期直接进入过滤器处理阶段。

概述

通常情况下,请求生命周期遵循以下流程:

  1. 传入请求
  2. 中间件
    • 2.1 全局绑定的中间件
    • 2.2 模块绑定的中间件
  3. 守卫
    • 3.1 全局守卫
    • 3.2 控制器守卫
    • 3.3 路由守卫
  4. 拦截器(控制器前)
    • 4.1 全局拦截器
    • 4.2 控制器拦截器
    • 4.3 路由拦截器
  5. 管道
    • 5.1 全局管道
    • 5.2 控制器管道
    • 5.3 路由管道
    • 5.4 路由参数管道
  6. 控制器(方法处理器)
  7. 服务(如存在)
  8. 拦截器(请求后)
    • 8.1 路由拦截器
    • 8.2 控制器拦截器
    • 8.3 全局拦截器
  9. 异常过滤器
    • 9.1 路由
    • 9.2 控制器
    • 9.3 全局
  10. 服务器响应