中間件
中間件是在路由處理程式之前調用的函數。中間件函數可以通路請求和響應對象。
使用過koa和express的朋友應該知道,中間件是一個很核心的功能,尤其是koa,核心就是中間件,連路由功能都是由中間件提供的。
中間件可以提供以下功能:
- 運作過程中執行任意代碼
- 對請求和響應進行更改
- 結束本次請求的響應
- 繼續調用下一個中間件
示例
NestJs使用@Injectable()來裝飾中間件,被裝飾的對象應該實作NestMiddleware接口。
以下是一個日志中間件的實作:
// log.middleware.ts
import {Injectable, NestMiddleware} from '@nestjs/common';
import {Request, Response} from 'express';
@Injectable()
export class LogMiddleware implements NestMiddleware {
use(req: Request, resp: Response, next: Function) {
console.log(`${req.method} ${req.path}`)
next();
}
}
// app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LogMiddleware } from './common/middleware/log.middleware';
import { UserModule } from './user/user.module';
@Module({
imports: [UserModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LogMiddleware)
.forRoutes('users');
}
}
針對請求方法應用中間件
上面的簡單示例中會對所有的users路由應用中間件,如果需要隻對特定的請求方法,比如GET請求才應用中間件,可以使用以下方式:
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LogMiddleware } from './common/middleware/log.middleware';
import { UserModule } from './user/user.module';
@Module({
imports: [UserModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LogMiddleware)
.forRoutes({ path: 'users', method: RequestMethod.GET });
}
}
應用多個中間件
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LogMiddleware } from './common/middleware/log.middleware';
import { UserModule } from './user/user.module';
@Module({
imports: [UserModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LogMiddleware, OtherMiddleware)
.forRoutes({ path: 'users', method: RequestMethod.GET });
}
}
基于控制器名稱應用中間件
上述代碼都是針對固定的路由位址應用中間件,在NestJs中路由位址是通過裝飾器定義的,如果控制器的路由位址有變化,而中間件這裡沒有跟着改掉,就會導緻問題。
NestJs在使用中間件的時候提供了基于控制器來注冊的方式:
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LogMiddleware } from './common/middleware/log.middleware';
import { UserModule } from './user/user.module';
@Module({
imports: [UserModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LogMiddleware)
.forRoutes(UserController);
}
}
排除指定路由
有些場景下對控制器應用了中間件之後需要繞過其中幾個方法,比如登入驗證中間件應該放行登入路由,否則沒有人能夠登入成功。
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LogMiddleware } from './common/middleware/log.middleware';
import { UserModule } from './user/user.module';
@Module({
imports: [UserModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LogMiddleware)
.exclude(
{path:'users/login',method:RequestMethod.GET}
)
.forRoutes(UserController);
}
}
全局中間件
類似于全局子產品,中間件也可以全局注冊,對每一個路由都生效。
// main.ts
const app = await NestFactory.create(AppModule);
app.use(LogMiddleware);
await app.listen(3000);