天天看點

NestJs學習之旅(4)——中間件

中間件

中間件是在路由處理程式之前調用的函數。中間件函數可以通路請求和響應對象。

使用過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);