概述

  • 在 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.pswp.cn/pingmian/91464.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/91464.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/91464.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nastool+cpolar:群晖NAS用户的全场景影音自由方案

文章目录前言1. 本地搭建Nastool2. nastool基础设置3. 群晖NAS安装内网穿透工具4. 配置公网地址小结5. 配置固定公网地址**第二版:技术整合与效率提升导向****第二版:技术整合与效率提升导向****第二版:技术整合与效率提升导向**Nastool与cpo…

从零开始:Kaggle 竞赛实战入门指南

一、Kaggle社区概述 Kaggle 是全球最大的数据科学和机器学习社区,由Anthony Goldbloom于2010年创立,2017年被Google收购。平台专注于数据科学竞赛、开源数据集共享、协作编程以及技能学习,吸引了从初学者到专业数据科学家的广泛用户群体。 …

sqli-labs:Less-16关卡详细解析

1. 思路🚀 本关的SQL语句为: $uname".$uname."; $passwd".$passwd."; $sql"SELECT username, password FROM users WHERE username($uname) and password($passwd) LIMIT 0,1";注入类型:字符串型(…

Lipschitz连续函数

Lipschitz function 一、说明 在数学分析中,Lipschitz连续性以德国 数学家 鲁道夫利普希茨 (Rudolf Lipschitz)的名字命名,是函数一致连续性的强形式。直观地说,Lipschitz连续函数的变化速度有限:存在一个实数,使得对于…

Dynamics 365 business central 与Shopify集成

Dynamics 365 Business Central(简称 D365 BC) 与 Shopify 的集成,能帮助企业实现前端电商平台(Shopify)与后端 ERP 系统(Business Central)之间的无缝数据同步,是一种典型的 ERP 与…

TCP RTO 与丢包检测

TCP RTO 是它 40 多年前唯一丢包检测策略,也是当前最后的丢包检测兜底策略,它几乎从没变过。 有个咨询挺有趣,以其案例为背景写篇随笔。大致意思是,嫌 TCP RTO 太大,游戏场景丢包卡顿怎么办?我提供了几行代…

安装php和配置环境变量

为了简单方便,先下载vscode然后下载对应的php安装包,然后配置环境变量,然后点击运行即可下载对应版本的php,这个版本凑合用然后下载完之后解压配置环境变量搜索环境变量将路径添加到环境变量中然后打开vscode添加变量具体看实际路…

Rabbit MQ的消息模式-Java原生代码

一.简单模式1.1.核心逻辑生产者 → 队列 → 单个消费者(1:1 直连),消息被消费后自动从队列删除。1.2.关键特性无交换器(其实使用的是默认交换机不是显示指定),直接指定队列 消息默认自动确认(au…

【lucene】使用docvalues的案例

下面给出一段 可直接跑通 的 Lucene 8.5.0 示例代码,演示如何1. 建索引时为两个字段启用 DocValues(一个 NumericDocValues,一个 SortedDocValues); 2. 用 IndexSearcher 按 DocValues 排序; 3. 用 Facet…

IntelliJ IDEA 配置 Maven 阿里云镜像加速源全流程

1. 为什么要加国内镜像源?国内网络访问 Maven 中央仓库经常超时、依赖下载极慢或失败。配置阿里云等国内镜像后,Java 项目依赖下载飞快,极大提升开发效率,是中国开发者必做优化!2. 添加阿里云镜像源的步骤(…

【worklist】worklist的hl7、dicom是什么关系

HL7和DICOM在Worklist系统中是互补的关系,它们各自承担不同的角色,但协同工作以实现完整的医疗信息系统集成。HL7与DICOM Worklist的关系1. 功能分工DICOM Worklist (Modality Worklist - MWL)主要用于影像设备获取患者和检查信息基于DICOM协议&#xff…

位运算-面试题01.01.判定字符是否唯一-力扣(LeetCode)

一、题目解析1、s[i]仅包含小写字母2、字符串的长度为[0,100]二、算法原理解法1:哈希表用哈希表记录s[i]的字符,如果有重复的,则返回false优化1:由于s[i]中只有小写字母,所以可以创建一个int hash[26]的数组…

wsl /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.28‘ not found

遇到的问题并没有解决,这个 glibc-2.28 应该是安装好了 Ubuntu18 问题描述:Ubuntu18 WSL 无法启动 VS Code ,因为node版本问题 rootUbuntu18:~# code . /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.28 not found (required by /root…

Windows系统ffmpeg.dll丢失怎么办?从错误分析到永久修复的完整流程

您是否遇到过这样的情况:打开心爱的视频编辑软件时,系统突然提示无法启动此程序,因为计算机中丢失ffmpeg.dll?别担心,这个问题比您想象的要常见得多。作为专业的技术支持团队,我们已经帮助数千用户解决了类…

LaTeX 复杂图形绘制教程:从基础到进阶

系列文章目录 第一章:深入了解 LaTeX:科技文档排版的利器 第二章:LaTeX 下载安装保姆级教程 第三章:LaTeX 创建工程并生成完整文档指南 第四章:LaTeX 表格制作全面指南 文章目录系列文章目录前言一、​LaTeX 绘图工具…

用 Go Typed Client 快速上手 Elasticsearch —— 从建索引到聚合的完整实战

1. 准备工作 go get github.com/elastic/go-elasticsearch/v9小贴士 如果你的集群启用了安全特性,需要在 elasticsearch.Config 中配置 Username/Password 或 APIKey。Typed Client 通过 NewTypedClient 创建,内部复用 *http.Client,建议全局…

《义龙棒球科普》棒球是韩国的国球吗·棒球1号位

⚾ Why Baseball is Koreas NATIONAL SPORT? | KBO热血全解析 ⚾⚾ 1. 历史根源 & 情感纽带 Historical Roots & Emotional Bond美军引入 (1945后): 战后美军将棒球带入韩国,迅速扎根!✨1982 KBO成立: 亚洲第二个职业棒球联盟诞生!奥…

三坐标测量机路径规划与补偿技术:如何用算法看见微米级误差?

三坐标测量的微米级精度背后,是精密的路径规划算法与实时补偿技术在保驾护航。三坐标测量机的智能避撞算法保障了测量的安全与高效;温度补偿技术消除了环境的无形干扰;点云智能处理则让海量数据蜕变为精准的工程决策依据。 “智能避让路径”&…

Docker设置容器时间

一、前言前言&#xff1a;容器搭建好之后&#xff0c;容器的默认时区于本地时区不一致&#xff0c;这将导致日志文件中保存的时间为错误时间。二、操作1、进入docker 容器docker exec -it <容器名称> bash2、选择时区tzselect3、配置时区根据跳出来的配置选择Asia -> …

德国威乐集团亚太中东非洲PMO负责人和继明受邀为PMO大会主持人

全国PMO专业人士年度盛会德国威乐集团亚太中东非洲PMO负责人 和继明先生 受邀为“PMO评论”主办的2025第十四届中国PMO大会主持人&#xff0c;敬请关注&#xff01;嘉宾介绍&#xff1a;和继明先生&#xff0c;德国威乐集团亚太中东非洲PMO负责人&#xff0c;项目管理硕士MPM&a…