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.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…