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

默认情况下,Nest中间件等同于Express中间件。以下是从官方Express文档中复制的中间件功能的绝佳列表:
中间件功能可以执行以下任务:
- 执行任何代码。
- 对请求和响应对象进行更改。
- 结束请求 - 响应周期。
- 调用堆栈中的下一个中间件函数。
- 如果当前的中间件函数没有结束请求 - 响应周期,它必须调用
next()将控制权传递给下一个中间件函数。否则,请求将被挂起。
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 })上述路线路径匹配abcd,ab_cd,abecd,等等。字符?,+,*,和()是他们的正则表达式的对应的子集。连字符(-)和点(.)按字面顺序由基于字符串的路径解释。
中间件消费者
这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);
}
}我们已通过普通字符串- ApplicationModule到with()方法。此后,我们要调整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'。
异步中间件
没有禁忌症会妨碍我们async在resolve()方法中返回功能。此外,也可以制作该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);本文档系腾讯云开发者社区成员共同维护,如有问题请联系 cloudcommunity@tencent.com

