logologo
文档仓库
文档仓库
logologo
开始

概述

第一步
控制器
提供者
模块
中间件
异常过滤器
管道
守卫
拦截器
自定义装饰器

基础

自定义提供程序
异步提供者
动态模块
注入作用域
循环依赖
模块引用
懒加载模块
执行上下文
生命周期事件
发现服务
平台无关
单元测试

技术

配置
SQL
Mongo
验证
缓存
序列化
版本控制
任务调度
队列
日志
Cookies
事件
压缩
文件上传
文件流
HTTP 模块
Session
MVC
性能(Fastify)
SSE

安全

认证
授权
加密与哈希
Helmet
CORS
CSRF
速率限制

GraphQL

快速开始
解析器
变更
订阅
标量
指令
接口
联合与枚举
字段中间件
类型映射
插件
复杂度
扩展
CLI 插件
生成SDL
共享模型
其他功能
联邦

WebSocket

网关
异常过滤器
管道
守卫
拦截器
适配器

微服务

基础
Redis
MQTT
NATS
RabbitMQ
Kafka
gRPC
自定义传输
异常过滤器
管道
守卫
拦截器
部署
独立应用程序

CLI

概述
工作区
库
用法
脚本

OpenAPI

介绍
装饰器
类型映射
操作
其他特性
安全
类型与参数
CLI 插件

实用示例

REPL
CRUD生成器
SWC
Passport(认证)
热重载
MikroORM
TypeORM
Mongoose
Sequelize
路由模块
Swagger
健康检查
CQRS
Compodoc
Prisma
Sentry
静态资源
Commander
异步本地存储
Necord
套件(原Automock)

常见问题

Serverless
HTTP 适配器
长连接
全局前缀
原始请求体
混合应用
HTTPS & 多服务器
请求生命周期
错误

开发工具

概述
CI/CD
迁移指南
API参考(官方)

生态与案例

谁在用
精彩资源

支持

支持

社区

贡献者

最后更新于: 2025/11/6 03:08:06

上一页HTTPS & 多服务器
下一页错误

#请求生命周期

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.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(遵循同样的顺序)。如果存在任何参数特定的管道,它们将在控制器和路由级别的管道之后运行(同样是从最后一个参数到第一个参数)。

#过滤器

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

提示

过滤器仅在请求过程中发生未捕获异常时才会执行。已捕获的异常(例如通过 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. 服务器响应