概述
- 在 NestJS 框架中,中間件(Middleware)、管道(Pipes)、過濾器(Filters)、攔截器(Interceptors) 均屬于請求處理流程的核心組件,它們共同構成了 NestJS 的 請求生命周期(Request Lifecycle)
- 中間件、管道、過濾器、攔截器屬于 請求生命周期,用于管理單次請求的處理流程;而 onModuleInit 等鉤子屬于應用生命周期,用于管理應用狀態
- 兩者的區別
類型 請求生命周期組件 應用程序生命周期鉤子 作用對象 單次 HTTP 請求 整個應用啟動/關閉過程 典型方法 middleware.use()
,@UseInterceptors()
onModuleInit()
,onApplicationShutdown()
關注點 請求處理流程控制 資源初始化、數據庫連接釋放等 - 中間件、管道、過濾器、攔截器屬于 請求生命周期,用于管理單次請求的處理流程;而
onModuleInit
等鉤子屬于 應用生命周期,用于管理應用狀態
請求生命周期
1 )請求生命周期中的角色與執行順序

當客戶端發起請求時,NestJS 會按以下順序處理
-
中間件(Middleware)
- 定位:請求的第一道關卡,處理全局或路由級別的預處理(如日志、CORS)。
- 特點:可訪問請求/響應對象,需調用
next()
傳遞控制權。 - 示例:日志記錄、請求頭校驗
-
守衛(Guards)
- 定位:第二道關卡,負責權限驗證(如角色校驗、JWT 認證)
- 特點:決定請求是否進入控制器(Controller)
- 注:用戶問題未提及守衛,但因其在流程中緊接中間件,需補充說明
-
攔截器(Interceptors)
- 定位:第三道關卡,包裹控制器邏輯,處理請求前后邏輯。
- 功能:
- 請求前:修改請求數據;
- 響應后:統一封裝響應格式、記錄執行時間
- 示例:全局響應包裝(
{ code: 200, data: ... }
)
-
管道(Pipes)
- 定位:第四道關卡,聚焦數據轉換與驗證。
- 功能:
- 轉換:將輸入數據轉為目標類型(如字符串轉數字);
- 驗證:校驗參數有效性,失敗時拋出異常
- 示例:
@Param('id', ParseIntPipe)
自動轉換路徑參數
-
控制器(Controller)
- 定位:執行業務邏輯,返回響應。
-
攔截器 (Post)
- 響應前的程序攔截
-
異常過濾器(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 )技術建議
- 鏈路追蹤:在全局中間件中添加請求ID,貫穿所有生命周期組件 [1]
- 響應標準化:通過POST攔截器統一返回格式
{ code: 200, data: T }
- 管道進階:自定義驗證管道(如手機號格式校驗)[9]
- 錯誤分層:使用過濾器區分客戶端錯誤(4xx)和服務端錯誤(5xx) [5]
完整可運行項目代碼可通過此模板快速初始化
應用生命周期
NestJS的生命周期鉤子允許開發者在應用啟動、運行和終止的關鍵階段注入邏輯,是實現初始化、資源清理和異常管理的核心機制,下面看下生命周期全流程圖示
1 )生命周期核心階段概覽
NestJS生命周期分為三大階段,共6個核心鉤子
- 初始化階段
onModuleInit()
:模塊依賴初始化后觸發,適合服務預熱(如數據庫連接池初始化)onApplicationBootstrap()
:所有模塊和服務就緒后觸發,HTTP服務器已啟動
- 運行階段
- 無專用生命周期鉤子,由請求級控制器和服務處理業務邏輯
- 終止階段(需手動啟用
enableShutdownHooks()
)onModuleDestroy()
:收到終止信號(如SIGTERM
)時觸發beforeApplicationShutdown()
:關閉前執行異步清理任務(如保存狀態)onApplicationShutdown()
:資源釋放完成后觸發(如關閉數據庫連接)
- 鉤子函數特性與約束
- 異步支持:所有鉤子可返回 Promise,Nest 將等待其解決后再繼續生命周期
- 作用域限制:鉤子僅作用于 模塊/提供者/控制器,不適用于請求作用域實例
- 平臺差異:終止鉤子在Windows系統對
SIGTERM
信號支持有限,建議優先使用SIGINT
或SIGBREAK
2 ) 核心鉤子對比表
鉤子函數 | 觸發時機 | 典型用途 |
---|---|---|
onModuleInit | 模塊依賴解析完成 | 初始化模塊級全局服務 |
onApplicationBootstrap | HTTP 服務器啟動前 | 加載動態配置/預熱緩存 |
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 )常見陷阱
-
依賴順序問題
- 若服務A依賴服務B,需將兩者拆分到不同模塊,利用模塊初始化順序保證依賴可用性
- 錯誤示例:同一模塊內,消息發布者可能在Kafka客戶端未就緒時發送消息
-
異步操作處理
鉤子返回Promise
時,NestJS會等待其完成再進入下一階段async onApplicationBootstrap() {await this.loadCache(); // 阻塞直至緩存加載完成 }
-
終止信號管理 ,鉤子未觸發
在main.ts
中顯式啟用關閉鉤子const app = await NestFactory.create(AppModule); app.enableShutdownHooks(); // 啟用終止信號監聽
-
資源泄露:在
beforeApplicationShutdown
中關閉數據庫連接池、停止消息隊列消費者 -
競態條件:避免在并行鉤子中訪問共享資源,優先使用協調器同步
6 )參考資料
- NestJS 生命周期事件詳解 - 官方執行機制解析
- 鉤子執行順序深度優化 - 模塊依賴拓撲管理方案
- 生產環境終止信號處理 - SIGTERM/SIGINT 最佳實踐
- 提示
- 實際開發中建議結合
@nestjs/cli
的--debug
標志輸出生命周期日志 - 實時驗證鉤子觸發時序
- 實際開發中建議結合
7 )總結
理解NestJS生命周期是構建健壯應用的基礎。通過合理使用鉤子,開發者能
- 確保資源按需初始化
- 實現應用優雅關閉
- 避免競態條件和資源泄漏