中間件是在路由處理程序 之前 調(diào)用的函數(shù)。 中間件函數(shù)可以訪問(wèn)請(qǐng)求和響應(yīng)對(duì)象,以及應(yīng)用程序請(qǐng)求響應(yīng)周期中的 next() 中間件函數(shù)。 next() 中間件函數(shù)通常由名為 next 的變量表示。
Nest 中間件實(shí)際上等價(jià)于 express 中間件。 下面是Express官方文檔中所述的中間件功能:
中間件函數(shù)可以執(zhí)行以下任務(wù):
您可以在函數(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 )。
中間件不能在 @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*)。
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è)路由除外。
我們使用的 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ù)式中間件。
如前所述,為了綁定順序執(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);
更多建議: