前言
用過express
與koa
的同學,對中間件這個概念應該非常熟悉了,中間件可以拿到Request
、Response
對象和next
函數.
一般來講中間件有以下作用:
- 執行任何代碼
- 對請求與響應攔截並改造
- 結束
request-response
週期 - 通過
next()
調用下一個中間件 - 如果當前中間件沒有結束當前
request-response
週期,必須調用next()
函數,否則請求會處於掛起狀態,阻塞整個應用
中間件一般有兩種:類中間件、函數中間件
類中間件
創建類中間件
使用@Injectable()
裝飾器,並且需要實現NestMiddleware
接口(use
方法)
// Logger.middleware.ts
import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response } from "express";
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: () => void) {
console.log('logger middleware', `url: ${req.url}`);
next();
}
}
使用類中間件
類中間創建完之後,需要在模塊中進行掛載,但@Module
裝飾器並沒有中間件的相關配置,我們需要讓module
類實現NestModule
接口,實現裏面configure方法來進行掛載
// user.module.ts
import { Module, NestModule } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { LoggerMiddleware } from '../middleware/Logger.middleware';
@Module({
controllers: [UserController],
providers: [UserService]
})
export class UserModule implements NestModule {
configure(consumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(UserController);
}
}
apply
方法表示掛載的是哪個中間件forRoutes
方法表示對哪個請求路徑起作用,這種方式與app.use(path, middleware)
作用是一樣,只針對部分路徑起作用- 當給
forRoutes
方法傳遞的是一個controller
控制器時,那麼該中間件則對整個控制器下的路徑生效
比如這裏傳遞的是UserController
控制器,那麼針對該控制器下的路徑都會生效
forRootes
方法還能做更詳細的配置,比如可以針對特定的請求方法、請求路徑可以使用正則匹配(需要注意的是使用fastify
驅動不能使用)
export class UserModule implements NestModule {
configure(consumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'user', method: RequestMethod.GET});
}
}
apply
可以同時掛載多箇中間件
export class UserModule implements NestModule {
configure(consumer) {
consumer
.apply(LoggerMiddleware, aaaMiddleware, ...)
.forRoutes({ path: 'user', method: RequestMethod.GET});
}
}
forRoutes
可以使用單個string
路徑,多個string
路徑,RouteInfo
對象,單個Controller
,多個Controller
export class AppModule implements NestModule {
configure(consumer) {
consumer
.apply(LoggerMiddleware, NjMiddleware, ...)
.forRoutes(UserController, NjController, ...);
}
}
exclude
可以用來排除不使用中間件的路徑
export class UserModule implements NestModule {
configure(consumer) {
consumer
.apply(LoggerMiddleware)
.exclude({ path: '/user/a', method: RequestMethod.GET})
.forRoutes(UserController);
}
}
需要注意的是forRoutes
需要最後調用
函數中間件
這種方式較爲簡單,使用起來與類中間件一致
創建函數中間件
export function LoggerMiddleware(req: Request, res: Response, next: () => void) {
console.log('logger middleware', `url: ${req.url}`);
next();
}
使用函數中間件
export class UserModule implements NestModule {
configure(consumer) {
consumer
.apply(LoggerMiddleware)
.exclude({ path: '/user/a', method: RequestMethod.GET})
.forRoutes(UserController);
}
}
全局中間件
可以直接在入口文件main.ts
中使用app.use
來掛載中間件,這樣掛載的中間件將全局生效
app.use(LoggerMiddleware) // 日誌中間件
中間件其實可以用來實現很多功能,比如:日誌系統、cors跨域處理、圖片防盜等...
對圖片防盜感興趣的可以看我這篇文章:你不知道的 HTTP Referer