中间件
中间件
中间件是在路由处理程序之前
调用的函数。中间件函数可以访问请求和响应对象,以及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 })
上述路线路径匹配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