当前位置: 首页 > news >正文

Nestjs框架: 请求生命周期与应用生命周期

概述

  • 在 NestJS 框架中,中间件(Middleware)、管道(Pipes)、过滤器(Filters)、拦截器(Interceptors) 均属于请求处理流程的核心组件,它们共同构成了 NestJS 的 请求生命周期(Request Lifecycle)
  • 中间件、管道、过滤器、拦截器属于 请求生命周期,用于管理单次请求的处理流程;而 onModuleInit 等钩子属于应用生命周期,用于管理应用状态
  • 两者的区别
    类型请求生命周期组件应用程序生命周期钩子
    作用对象单次 HTTP 请求整个应用启动/关闭过程
    典型方法middleware.use(), @UseInterceptors()onModuleInit(), onApplicationShutdown()
    关注点请求处理流程控制资源初始化、数据库连接释放等
  • 中间件、管道、过滤器、拦截器属于 请求生命周期,用于管理单次请求的处理流程;而 onModuleInit 等钩子属于 应用生命周期,用于管理应用状态

请求生命周期


1 )请求生命周期中的角色与执行顺序

当客户端发起请求时,NestJS 会按以下顺序处理

  1. 中间件(Middleware)

    • 定位:请求的第一道关卡,处理全局或路由级别的预处理(如日志、CORS)。
    • 特点:可访问请求/响应对象,需调用 next() 传递控制权。
    • 示例:日志记录、请求头校验
  2. 守卫(Guards)

    • 定位:第二道关卡,负责权限验证(如角色校验、JWT 认证)
    • 特点:决定请求是否进入控制器(Controller)
    • :用户问题未提及守卫,但因其在流程中紧接中间件,需补充说明
  3. 拦截器(Interceptors)

    • 定位:第三道关卡,包裹控制器逻辑,处理请求前后逻辑。
    • 功能:
      • 请求前:修改请求数据;
      • 响应后:统一封装响应格式、记录执行时间
    • 示例:全局响应包装({ code: 200, data: ... }
  4. 管道(Pipes)

    • 定位:第四道关卡,聚焦数据转换与验证。
    • 功能:
      • 转换:将输入数据转为目标类型(如字符串转数字);
      • 验证:校验参数有效性,失败时抛出异常
    • 示例@Param('id', ParseIntPipe) 自动转换路径参数
  5. 控制器(Controller)

    • 定位:执行业务逻辑,返回响应。
  6. 拦截器 (Post)

    • 响应前的程序拦截
  7. 异常过滤器(Exception Filters)

    • 定位:请求流程的“安全网”,捕获全局或局部异常
    • 功能:统一处理错误响应(如转换技术异常为友好提示)

2 ) 代码示例:实现请求周期钩子

基于您提供的NestJS生命周期结构图,我已全面补充了每个组件的完整代码实现,确保所有子类(全局/控制器/路由级别)均有完整示例。以下是按执行顺序重新生成的代码:

src/
├── middleware/
│    └── global.middleware.ts
├── guards/
│    ├── global.guard.ts
│    ├── controller.guard.ts
│    └── route.guard.ts
├──interceptors/
│    ├── global.interceptor.ts
│    ├── controller.interceptor.ts
│    └── route.interceptor.ts
├──pipes/
│    ├── global.pipe.ts
│    ├── controller.pipe.ts
│    └── route.pipe.ts
├── controllers/
│    └── example.controller.ts
├── filters/
│    ├── global.filter.ts
│    └── route.filter.ts

2.1 中间件(Middleware)

// 全局中间件
@Injectable()
export class GlobalMiddleware implements NestMiddleware {use(req: Request, res: Response, next: NextFunction) {console.log('全局中间件执行');next();}
}// 模块中间件(在模块内注册)
@Module({})
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {consumer.apply(ModuleMiddleware).forRoutes('*');}
}

2.2 守卫(Guards)

// 全局守卫
@Injectable()
export class GlobalGuard implements CanActivate {canActivate(context: ExecutionContext): boolean {console.log('全局守卫验证');return true;}
}// 控制器守卫 
@Controller('users')
@UseGuards(ControllerGuard)
export class UserController {}// 路由守卫 
@Get('profile')
@UseGuards(RouteGuard)
getProfile() { /*...*/ }

2.3 拦截器(Interceptors) PRE处理阶段(请求到达控制器前):

// 全局拦截器pre 
@Injectable()
export class GlobalPreInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler) {console.log('全局拦截器pre');return next.handle();}
}// 控制器拦截器pre
@UseInterceptors(ControllerPreInterceptor)
@Controller('posts')
export class PostController {}// 路由拦截器pre
@Get(':id')
@UseInterceptors(RoutePreInterceptor)
getPost() { /*...*/ }

2.4 管道(Pipes)

// 全局管道(main.ts注册)
app.useGlobalPipes(new ValidationPipe());// 控制器管道
@UsePipes(ControllerPipe)
@Controller('products')
export class ProductController {}// 路由参数管道
@Get(':id')
getProduct(@Param('id', ParseIntPipe) id: number) { /*...*/ }

2.5 控制器(Controller) & 服务(Service)

@Controller('orders')
export class OrderController {constructor(private orderService: OrderService) {}@Post()createOrder(@Body() data: CreateOrderDto) {return this.orderService.process(data); // 服务处理业务逻辑 }
}

2.6 拦截器(Post 处理阶段) 响应返回客户端前:

// 路由拦截器post
@Injectable()
export class RoutePostInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler) {return next.handle().pipe(map(data => ({ status: 'success', data })) // 统一响应格式 );}
}
// 全局/控制器级post拦截器同理

2.7 过滤器(Exception Filters)

// 全局过滤器
@Catch(HttpException)
export class GlobalFilter implements ExceptionFilter {catch(exception: HttpException, host: ArgumentsHost) {const ctx = host.switchToHttp();ctx.getResponse().status(500).json({ error: exception.message });}
}// 控制器过滤器
@UseFilters(ControllerFilter)
@Controller('payments')
export class PaymentController {}// 路由过滤器 
@Post()
@UseFilters(RouteFilter)
makePayment() { /*...*/ }

3 ) 各组件核心功能总结

组件核心职责典型场景注册方式
中间件预处理请求/响应日志记录、请求头设置全局 app.use() 或模块绑定
守卫访问控制角色验证、API 权限控制器类或方法装饰器
拦截器拦截请求/响应响应包装、性能监控全局或控制器级 @UseInterceptors()
管道数据转换与验证参数类型转换、DTO 校验控制器方法参数装饰器
过滤器统一异常处理捕获 HttpException 返回友好错误全局 app.useGlobalFilters()

4 ) 关键注册方式对比

组件类型全局注册控制器注册路由注册
中间件app.use(GlobalMiddleware)Module.configure()❌ 不支持
守卫app.useGlobalGuards()@UseGuards()@UseGuards()
拦截器app.useGlobalInterceptors()@UseInterceptors()@UseInterceptors()
管道app.useGlobalPipes()@UsePipes()@Param(key, Pipe)
过滤器app.useGlobalFilters()@UseFilters()@UseFilters()

5 ) 请求生命周期执行顺序总结

阶段组件类型执行顺序
请求进入中间件(pre)1
认证/授权守卫2
数据预处理拦截器(pre)3
数据验证管道4
控制器处理控制器5
数据处理服务6
数据后处理拦截器(post)7
异常处理过滤器8
响应发送响应体9

6 )可扩展性建议

  • 日志追踪:可在中间件或拦截器中加入请求 ID,实现全链路日志追踪。
  • 权限统一管理:将守卫和拦截器结合 JWT 实现 RBAC 权限系统。
  • 响应结构统一:通过拦截器 post 统一返回格式。
  • 自定义管道:实现自定义数据校验逻辑,如手机号格式校验。
  • 异常处理增强:区分客户端错误与服务器错误,返回不同响应。

7 )技术建议

  1. 链路追踪:在全局中间件中添加请求ID,贯穿所有生命周期组件 [1]
  2. 响应标准化:通过POST拦截器统一返回格式 { code: 200, data: T }
  3. 管道进阶:自定义验证管道(如手机号格式校验)[9]
  4. 错误分层:使用过滤器区分客户端错误(4xx)和服务端错误(5xx) [5]

完整可运行项目代码可通过此模板快速初始化

应用生命周期

NestJS的生命周期钩子允许开发者在应用启动、运行和终止的关键阶段注入逻辑,是实现初始化、资源清理和异常管理的核心机制,下面看下生命周期全流程图示

初始化阶段
onModuleInit
onApplicationBootstrap
运行阶段
请求处理
终止阶段
onModuleDestroy
beforeApplicationShutdown
onApplicationShutdown

1 )生命周期核心阶段概览

NestJS生命周期分为三大阶段,共6个核心钩子

  1. 初始化阶段
    • onModuleInit():模块依赖初始化后触发,适合服务预热(如数据库连接池初始化)
    • onApplicationBootstrap():所有模块和服务就绪后触发,HTTP服务器已启动
  2. 运行阶段
    • 无专用生命周期钩子,由请求级控制器和服务处理业务逻辑
  3. 终止阶段(需手动启用enableShutdownHooks()
    • onModuleDestroy():收到终止信号(如SIGTERM)时触发
    • beforeApplicationShutdown():关闭前执行异步清理任务(如保存状态)
    • onApplicationShutdown():资源释放完成后触发(如关闭数据库连接)
  4. 钩子函数特性与约束
    • 异步支持:所有钩子可返回 Promise,Nest 将等待其解决后再继续生命周期
    • 作用域限制:钩子仅作用于 模块/提供者/控制器,不适用于请求作用域实例
    • 平台差异:终止钩子在Windows系统对SIGTERM信号支持有限,建议优先使用SIGINTSIGBREAK

2 ) 核心钩子对比表

钩子函数触发时机典型用途
onModuleInit模块依赖解析完成初始化模块级全局服务
onApplicationBootstrapHTTP 服务器启动前加载动态配置/预热缓存
beforeApplicationShutdown连接关闭前持久化状态/发送终止通知
onApplicationShutdown进程退出前最后时机强制释放占用的硬件资源

3 )最佳代码实践与常见问题

初始化顺序管理

// 方案1:模块拆分(强依赖场景)  
@Module({ imports: [DatabaseModule] }) // 确保 DatabaseModule 先初始化  
export class PublisherModule implements OnModuleInit { /* ... */ }// 方案2:协调器模式(复杂依赖)  
@Injectable()  
class InitializationCoordinator {  async init() {  await db.connect();  await mq.start();  }  
}  

说明:同一模块内提供者的 onModuleInit 并行执行,需通过依赖注入手动控制时序

优雅终止实现示例

@Injectable()  
class TaskService implements OnApplicationShutdown {  async onApplicationShutdown() {  await this.flushPendingTasks(); // 清理未完成任务  await this.releaseResources();  // 释放文件句柄/网络连接  }  
}  

4 )实际应用场景

  • 启动时:预加载配置、建立长连接(如WebSocket)
  • 终止时:
    • 关闭数据库连接(避免数据损坏)
    • 结束未完成的任务队列
    • 发送系统停机通知

5 )常见陷阱

  1. 依赖顺序问题

    • 若服务A依赖服务B,需将两者拆分到不同模块,利用模块初始化顺序保证依赖可用性
    • 错误示例:同一模块内,消息发布者可能在Kafka客户端未就绪时发送消息
  2. 异步操作处理
    钩子返回Promise时,NestJS会等待其完成再进入下一阶段

    async onApplicationBootstrap() {await this.loadCache(); // 阻塞直至缓存加载完成
    }
    
  3. 终止信号管理 ,钩子未触发
    main.ts中显式启用关闭钩子

    const app = await NestFactory.create(AppModule);
    app.enableShutdownHooks(); // 启用终止信号监听
    
  4. 资源泄露:在 beforeApplicationShutdown 中关闭数据库连接池、停止消息队列消费者

  5. 竞态条件:避免在并行钩子中访问共享资源,优先使用协调器同步

6 )参考资料

  • NestJS 生命周期事件详解 - 官方执行机制解析
  • 钩子执行顺序深度优化 - 模块依赖拓扑管理方案
  • 生产环境终止信号处理 - SIGTERM/SIGINT 最佳实践
  • 提示
    • 实际开发中建议结合 @nestjs/cli--debug 标志输出生命周期日志
    • 实时验证钩子触发时序

7 )总结

理解NestJS生命周期是构建健壮应用的基础。通过合理使用钩子,开发者能

  • 确保资源按需初始化
  • 实现应用优雅关闭
  • 避免竞态条件和资源泄漏
http://www.dtcms.com/a/310554.html

相关文章:

  • Vue模板语法详解:从基础到进阶的响应式绑定指南1
  • 工业数采引擎-DTU
  • CSS属性值计算规则:从声明到渲染的精确过程
  • 《C++》STL--list容器详解
  • 【读文献】Capacitor-drop AC-DC
  • 移除 Excel 文件(.xlsx)的工作表保护
  • Ubuntu 系统下使用 lsusb 命令识别 USB 设备及端口类型详解
  • 从“多、老、旧”到“4i焕新”:品牌官方商城(小程序/官网/APP···)的范式跃迁与增长再想象
  • 数据结构与算法——字典(前缀)树的实现
  • Rockchip RK3568J +FPGA边缘智能系统及储能网关
  • 以太网是什么网,什么网是以太网
  • spring cloud alibaba ——sidecar服务异构
  • Vite+React组件库提速方案
  • 区块链概述
  • 嵌入式 C 语言入门:函数封装与参数传递学习笔记 —— 从定义到内存机制
  • Syzkaller实战教程6:[重要]初始种子加载机制剖析第二集
  • 如何理解卷积,和自注意力机制的局限与优势(个人理解)
  • C++中typename基本用法
  • Nastool+cpolar:群晖NAS用户的全场景影音自由方案
  • 理解HTTP协议
  • 网络配置+初始服务器配置
  • Effective C++ 条款15:在资源管理类中提供对原始资源的访问
  • 在 Docker 中启动 Nginx 并挂载配置文件到宿主机目录
  • MyBatis知识点
  • 烽火HG680-KX-海思MV320芯片-2+8G-安卓9.0-强刷卡刷固件包
  • 电子电气架构 --- 加速48V技术应用的平衡之道
  • 机器学习sklearn:处理缺失值
  • 应用分层
  • 菜鸟教程Shell笔记 数组 运算符 echo命令
  • Qwen2 RotaryEmbedding 位置编码仅仅是第一层有吗