NestJS 中間件

2023-09-07 21:51 更新

中間件是在路由處理程序 之前 調(diào)用的函數(shù)。 中間件函數(shù)可以訪問(wèn)請(qǐng)求和響應(yīng)對(duì)象,以及應(yīng)用程序請(qǐng)求響應(yīng)周期中的 next() 中間件函數(shù)。 next() 中間件函數(shù)通常由名為 next 的變量表示。

7

Nest 中間件實(shí)際上等價(jià)于 express 中間件。 下面是Express官方文檔中所述的中間件功能:

中間件函數(shù)可以執(zhí)行以下任務(wù):

  • 執(zhí)行任何代碼。
  • 對(duì)請(qǐng)求和響應(yīng)對(duì)象進(jìn)行更改。
  • 結(jié)束請(qǐng)求-響應(yīng)周期。
  • 調(diào)用堆棧中的下一個(gè)中間件函數(shù)。
  • 如果當(dāng)前的中間件函數(shù)沒(méi)有結(jié)束請(qǐng)求-響應(yīng)周期, 它必須調(diào)用 next() 將控制傳遞給下一個(gè)中間件函數(shù)。否則, 請(qǐng)求將被掛起。

您可以在函數(shù)中或在具有 @Injectable() 裝飾器的類中實(shí)現(xiàn)自定義 Nest中間件。 這個(gè)類應(yīng)該實(shí)現(xiàn) NestMiddleware 接口, 而函數(shù)沒(méi)有任何特殊的要求。 讓我們首先使用類方法實(shí)現(xiàn)一個(gè)簡(jiǎn)單的中間件功能。

logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
  }
}

依賴注入

Nest中間件完全支持依賴注入。 就像提供者和控制器一樣,它們能夠注入屬于同一模塊的依賴項(xiàng)(通過(guò) constructor )。

應(yīng)用中間件

中間件不能在 @Module() 裝飾器中列出。我們必須使用模塊類的 configure() 方法來(lái)設(shè)置它們。包含中間件的模塊必須實(shí)現(xiàn) NestModule 接口。我們將 LoggerMiddleware 設(shè)置在 ApplicationModule 層上。

app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('cats');
  }
}

我們還可以在配置中間件時(shí)將包含路由路徑的對(duì)象和請(qǐng)求方法傳遞給forRoutes()方法。我們?yōu)橹霸贑atsController中定義的/cats路由處理程序設(shè)置了LoggerMiddleware。我們還可以在配置中間件時(shí)將包含路由路徑的對(duì)象和請(qǐng)求方法傳遞給 forRoutes()方法,從而進(jìn)一步將中間件限制為特定的請(qǐng)求方法。在下面的示例中,請(qǐng)注意我們導(dǎo)入了 RequestMethod來(lái)引用所需的請(qǐng)求方法類型。

app.module.ts
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'cats', method: RequestMethod.GET });
  }
}

可以使用 async/await來(lái)實(shí)現(xiàn) configure()方法的異步化(例如,可以在 configure()方法體中等待異步操作的完成)。

路由通配符

路由同樣支持模式匹配。例如,星號(hào)被用作通配符,將匹配任何字符組合。

forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });

以上路由地址將匹配 abcd 、 ab_cd 、 abecd 等。字符 ? 、 + 、 * 以及 () 是它們的正則表達(dá)式對(duì)應(yīng)項(xiàng)的子集。連字符 (-) 和點(diǎn) (.) 按字符串路徑解析。

該 fastify 軟件包使用該軟件包的最新版本,該版本 path-to-regexp 不再支持通配符星號(hào)*。相反,您必須使用參數(shù)(例如(.*),:splat*)。

中間件消費(fèi)者

MiddlewareConsumer 是一個(gè)幫助類。它提供了幾種內(nèi)置方法來(lái)管理中間件。他們都可以被簡(jiǎn)單地鏈接起來(lái)。forRoutes() 可接受一個(gè)字符串、多個(gè)字符串、對(duì)象、一個(gè)控制器類甚至多個(gè)控制器類。在大多數(shù)情況下,您可能只會(huì)傳遞一個(gè)由逗號(hào)分隔的控制器列表。以下是單個(gè)控制器的示例:

app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller.ts';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(CatsController);
  }
}

該 apply() 方法可以使用單個(gè)中間件,也可以使用多個(gè)參數(shù)來(lái)指定多個(gè)多個(gè)中間件。

有時(shí)我們想從應(yīng)用中間件中排除某些路由。我們可以使用該 exclude() 方法輕松排除某些路由。此方法可以采用一個(gè)字符串,多個(gè)字符串或一個(gè) RouteInfo 對(duì)象來(lái)標(biāo)識(shí)要排除的路由,如下所示:

consumer
  .apply(LoggerMiddleware)
  .exclude(
    { path: 'cats', method: RequestMethod.GET },
    { path: 'cats', method: RequestMethod.POST },
    'cats/(.*)',
  )
  .forRoutes(CatsController);

該 exclude() 方法使用 path-to-regexp 包支持通配符參數(shù)。

在上面的示例中,LoggerMiddleware 將綁定到內(nèi)部定義的所有路由,CatsController 但傳遞給 exclude() 方法的三個(gè)路由除外。

函數(shù)式中間件

我們使用的 LoggerMiddleware 類非常簡(jiǎn)單。它沒(méi)有成員,沒(méi)有額外的方法,沒(méi)有依賴關(guān)系。為什么我們不能只使用一個(gè)簡(jiǎn)單的函數(shù)?這是一個(gè)很好的問(wèn)題,因?yàn)槭聦?shí)上 - 我們可以做到。這種類型的中間件稱為函數(shù)式中間件。讓我們把 logger 轉(zhuǎn)換成函數(shù)。

logger.middleware.ts
export function logger(req, res, next) {
  console.log(`Request...`);
  next();
};

現(xiàn)在在 AppModule 中使用它。

app.module.ts
consumer
  .apply(logger)
  .forRoutes(CatsController);

當(dāng)您的中間件沒(méi)有任何依賴關(guān)系時(shí),我們可以考慮使用函數(shù)式中間件。

多個(gè)中間件

如前所述,為了綁定順序執(zhí)行的多個(gè)中間件,我們可以在 apply() 方法內(nèi)用逗號(hào)分隔它們。

consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);

全局中間件

如果我們想一次性將中間件綁定到每個(gè)注冊(cè)路由,我們可以使用由INestApplication實(shí)例提供的 use()方法:

const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)