在线文档教程

中间件

中间件

中间件是在路由处理程序之前调用的函数。中间件函数可以访问请求和响应对象,以及next应用程序请求 - 响应周期中的中间件功能。在接下来的中间件功能通常是由一个名为变量来表示next

默认情况下,Nest中间件等同于Express中间件。以下是从官方Express文档中复制的中间件功能的绝佳列表:

中间件功能可以执行以下任务:

  • 执行任何代码。

Nest中间件是一个函数,或者是一个带有@Injectable()装饰器的类。该类应该实现NestMiddleware接口,而函数没有任何特殊要求。让我们从LoggerMiddleware示例开始。

logger.middleware.ts

JS

import { Injectable, NestMiddleware, MiddlewareFunction } from '@nestjs/common'; @Injectable() export class LoggerMiddleware implements NestMiddleware { resolve(...args: any[]): MiddlewareFunction { return (req, res, next) => { console.log('Request...' next( }; } }

该resolve()方法必须返回常规的库特定中间件(req, res, next) => any。

依赖注入

对于中间件,没有例外。与提供者和控制器相同,他们能够注入属于同一模块的依赖关系(通过constructor)。

应用中间件

@Module()装饰器中没有中间件的地方。我们必须使用configure()模块类的方法来设置它们。包含中间件的模块必须实现该NestModule接口。我们LoggerMiddleware在这个ApplicationModule级别设置。

app.module.ts

JS

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { LoggerMiddleware } from './common/middlewares/logger.middleware'; import { CatsModule } from './cats/cats.module'; @Module{ imports: [CatsModule], }) export class ApplicationModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .forRoutes('cats' } }

在上面的例子中,我们已经建立了LoggerMiddleware用于/cats我们先前内部的定义路径处理程序CatsController。此外,我们可能会将中间件限制为特定的请求方法。

app.module.ts

JS

import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common'; import { LoggerMiddleware } from './common/middlewares/logger.middleware'; import { CatsModule } from './cats/cats.module'; @Module{ imports: [CatsModule], }) export class ApplicationModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .forRoutes{ path: 'cats', method: RequestMethod.GET } } }

路由通配符

也支持基于模式的路由。例如,星号用作通配符,并且将匹配任何字符组合。

forRoutes{ path: '*', method: RequestMethod.ALL })

上述路线路径匹配abcdab_cdabecd,等等。字符?+*,和()是他们的正则表达式的对应的子集。连字符(-)和点(.)按字面顺序由基于字符串的路径解释。

中间件消费者

MiddlewareConsumer是一个帮助类。它提供了几种内置方法来管理中间件。所有这些都可以简单地链接。在forRoutes()可采取单个字符串,多个字符串,RouteInfo对象,控制器类和甚至多个控制器类。在大多数情况下,您可能只是传递控制器并用逗号分隔它们。以下是单个控制器的示例:

app.module.ts

JS

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { LoggerMiddleware } from './common/middlewares/logger.middleware'; import { CatsModule } from './cats/cats.module'; @Module{ imports: [CatsModule], }) export class ApplicationModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .forRoutes(CatsController } }

提示apply()方法可以采用单个中间件,也可以采用中间件阵列

在使用课程时,我们通常可能想要排除某些路线。由于该exclude()方法,这非常直观。

app.module.ts

JS

import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common'; import { LoggerMiddleware } from './common/middlewares/logger.middleware'; import { CatsModule } from './cats/cats.module'; @Module{ imports: [CatsModule], }) export class ApplicationModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .exclude( { path: 'cats', method: RequestMethod.GET }, { path: 'cats', method: RequestMethod.POST }, ) .forRoutes(CatsController } }

因此,LoggerMiddleware将限制在内部定义的所有路由,CatsController除了这两个传递给exclude()函数。请注意,该exclude()方法不适用于您的功能中间件。此外,此功能不排除来自更通用路由(例如通配符)的路径。在这种情况下,您应该将路径限制逻辑直接放在中间件上,例如,比较请求的URL。

可配置的中间件

有时,中间件的行为取决于自定义值,例如用户角色数组,选项对象等。我们可以resolve()使用该with()方法应用其他参数。请参阅以下示例:

app.module.ts

JS

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { LoggerMiddleware } from './common/middlewares/logger.middleware'; import { CatsModule } from './cats/cats.module'; import { CatsController } from './cats/cats.controller'; @Module{ imports: [CatsModule], }) export class ApplicationModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .with('ApplicationModule') .forRoutes(CatsController } }

我们已通过普通字符串- ApplicationModulewith()方法。此后,我们要调整resolve()方法了LoggerMiddleware

logger.middleware.ts

JS

import { Injectable, NestMiddleware, MiddlewareFunction } from '@nestjs/common'; @Injectable() export class LoggerMiddleware implements NestMiddleware { resolve(name: string): MiddlewareFunction { return (req, res, next) => { console.log(`[${name}] Request...` // [ApplicationModule] Request... next( }; } }

在这种情况下,name属性的值将是'ApplicationModule'

异步中间件

没有禁忌症会妨碍我们asyncresolve()方法中返回功能。此外,也可以制作该resolve()方法async。这种常见模式称为deferred middleware

logger.middleware.ts

JS

import { Injectable, NestMiddleware, MiddlewareFunction } from '@nestjs/common'; @Injectable() export class LoggerMiddleware implements NestMiddleware { async resolve(name: string): Promise<MiddlewareFunction> { await someAsyncJob( return async (req, res, next) => { await someAsyncJob( console.log(`[${name}] Request...` // [ApplicationModule] Request... next( }; } }

功能中间件

LoggerMiddleware很短。它没有成员,没有其他方法,没有依赖。为什么我们不能只使用一个简单的功能?这是一个很好的问题,事实上 - 我们可以。这种类型的中间件称为功能中间件。让我们将记录器转换为函数。

logger.middleware.ts

JS

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

并在以下内容中使用它ApplicationModule

app.module.ts

JS

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { logger } from './common/middlewares/logger.middleware'; import { CatsModule } from './cats/cats.module'; import { CatsController } from './cats/cats.controller'; @Module{ imports: [CatsModule], }) export class ApplicationModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(logger) .forRoutes(CatsController } }

提示每当中间件不需要任何依赖项时,我们就考虑使用功能中间件

多个中间件

如前所述,为了绑定顺序执行的多个中间件,我们可以在apply()方法内部用逗号分隔它们。

JS

export class ApplicationModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(cors(), helmet(), logger) .forRoutes(CatsController } }

全局中间件

为了将中间件同时绑定到每个注册路由,我们可以利用实例use()提供的方法INestApplication

const app = await NestFactory.create(ApplicationModule app.use(logger await app.listen(3000