## 请求生命周期
`Nest`应用程序处理请求并生成回应的过程被称为**请求生命周期**。使用中间件、管道、守卫和拦截器时,要在请求生命周期中追踪特定的代码片段的执行很困难,尤其是在全局、控制器或者路由的部件中。一般来说,一个请求流经中间件、守卫与拦截器,然后到达管道,并最终回到拦截器中的返回路径中(从而产生响应)。
### 中间件
中间件以特殊的顺序执行。首先,`Nest`运行全局绑定的中间件(例如`app.use`中绑定的中间件),然后运行在路径中指定的**模块绑定的中间件**。中间件以他们绑定的次序顺序执行,这和在`Express`中的中间件工作原理是类似的。
### 守卫
守卫的执行首先从全局守卫开始,然后处理控制器守卫,最后是路径守卫。和中间件一样,守卫的执行也和他们的绑定顺序一致。例如:
```typescript
@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()`参数次序顺序执行。然而,在路由参数层次,如果由多个管道在执行,则会按照自后向前的参数顺序执行,这在路由层面和控制器层面的管道中同样如此,例如,我们有如下控制器:
```typescript
@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. 全局绑定的中间件
3. 模块绑定的中间件
4. 全局守卫
5. 控制层守卫
6. 路由守卫
7. 全局拦截器(控制器之前)
8. 控制器层拦截器 (控制器之前)
9. 路由拦截器 (控制器之前)
10. 全局管道
11. 控制器管道
12. 路由管道
13. 路由参数管道
14. 控制器(方法处理器)
15. 服务(如果有)
16. 路由拦截器(请求之后)
17. 控制器拦截器 (请求之后)
18. 全局拦截器 (请求之后)
19. 异常过滤器 (路由,之后是控制器,之后是全局)
20. 服务器响应
- 介绍
- 概述
- 第一步
- 控制器
- 提供者
- 模块
- 中间件
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 自定义装饰器
- 基础知识
- 自定义提供者
- 异步提供者
- 动态模块
- 注入作用域
- 循环依赖
- 模块参考
- 懒加载模块
- 应用上下文
- 生命周期事件
- 跨平台
- 测试
- 技术
- 数据库
- Mongo
- 配置
- 验证
- 缓存
- 序列化
- 版本控制
- 定时任务
- 队列
- 日志
- Cookies
- 事件
- 压缩
- 文件上传
- 流式处理文件
- HTTP模块
- Session(会话)
- MVC
- 性能(Fastify)
- 服务器端事件发送
- 安全
- 认证(Authentication)
- 授权(Authorization)
- 加密和散列
- Helmet
- CORS(跨域请求)
- CSRF保护
- 限速
- GraphQL
- 快速开始
- 解析器(resolvers)
- 变更(Mutations)
- 订阅(Subscriptions)
- 标量(Scalars)
- 指令(directives)
- 接口(Interfaces)
- 联合类型
- 枚举(Enums)
- 字段中间件
- 映射类型
- 插件
- 复杂性
- 扩展
- CLI插件
- 生成SDL
- 其他功能
- 联合服务
- 迁移指南
- Websocket
- 网关
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 适配器
- 微服务
- 概述
- Redis
- MQTT
- NATS
- RabbitMQ
- Kafka
- gRPC
- 自定义传输器
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 独立应用
- Cli
- 概述
- 工作空间
- 库
- 用法
- 脚本
- Openapi
- 介绍
- 类型和参数
- 操作
- 安全
- 映射类型
- 装饰器
- CLI插件
- 其他特性
- 迁移指南
- 秘籍
- CRUD 生成器
- 热重载
- MikroORM
- TypeORM
- Mongoose
- 序列化
- 路由模块
- Swagger
- 健康检查
- CQRS
- 文档
- Prisma
- 静态服务
- Nest Commander
- 问答
- Serverless
- HTTP 适配器
- 全局路由前缀
- 混合应用
- HTTPS 和多服务器
- 请求生命周期
- 常见错误
- 实例
- 迁移指南
- 发现
- 谁在使用Nest?