基本概念
中间件是在路由处理程序之前调用的函数。中间件函数可以访问请求
和响应
对象。在程序中我们可以让多个中间件串起来一起使用,当多个中间件一起使用时我们可以使用next()
调用下一个中间件。
中间件主要是可以实现如下功能:
- 执行任何代码
- 更改请求和响应对象
- 结束请求-响应周期
- 调用堆栈中的下一个中间件函数
- 如果当前的中间件函数没有结束请求-响应周期,则必须调用 next()将控制权传递给下一个中间件函数。否则,请求将被挂起
在 NestJS 中可以在函数
或者带有@Injectable()
装饰器装饰的类来定义中间件,并且使用类时我们必须实现NestMiddleware
接口,函数不需要做特殊处理。
依赖注入
在 NestJs 中中间件可以使用依赖注入的方式进行注入使用,就像控制器和提供者一样进行注入。
需要使用的时候,我们可以通过constructor
进行实例化,然后进行使用。
在模块中使用中间件
在@Module
装饰器装饰的类中,我们是没有地方进行注入的,我们只能在类中使用configure()
来声明使用中间件,并且类还必须集成NestModule
接口。
接下来我们声明一个日志中间件,主要是简单的把请求对象和响应对象打印一下,具体代码如下:
typescript
// 日志中间件
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("-------");
console.log(req);
console.log(res);
console.log("-------");
next();
}
}
typescript
// App模块
@Module({
imports: [CommodityModule, AccountModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes("commodity");
}
}
在上面这里例子中,我们的中间件主要是针对带有commodity
前缀的所有请求路径进行处理,并且注入的中间件为LoggerMiddleware
。
我们还可以在forRoutes
传入参数对象,从而过滤出符合的请求进入到中间件中进行处理。例如我们限定监听请求路径是commodity
并且是POST
的请求:
typescript
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'commodity', method: RequestMethod.POST });
}
forRoutes
的路由通配符
在上述的例子中,我们在forRoutes
中传入路由参数后筛选出匹配的路由地址,其中path
是匹配的路由地址,我们可以使用通配符来定义匹配的路由,而通配符设置规则已经在NestJS 的 Controller 学习文章中介绍过。
中间件消费者(MiddlewareConsumer 类)
MiddlewareConsumer
是一个辅助类,它提供了几种内置方法来管理中间件。
forRoutes()
方法传入的参数可以是字符串、多个字符串、RouterInfo 对象和控制器类,建议使用控制器类来作为forRoutes
的参数,因为使用控制器类后,我们不需要过多的来维护监听的数据,这样维护效率会比较高并且清晰。
排除路线
有些时候我们希望排除某些路由应用中间件的时候,我们可以使用exclude()
方法来排除某些路由。该方法传入的参数可以是字符串、多个字符串和 RouterInfo。具体的例子如下:
typescript
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.exclude('account/(.*)')
.forRoutes(CommodityController, AccountController);
}
函数中间件
当我们的中间件不需要任何成员、方法和依赖项的时候,可以使用函数中间件来替代类中间件。函数中间件的例子我们还是采用日志中间件为例,具体的例子如下:
typescript
export function MyLogger(req: Request, res: Response, next: NextFunction) {
console.log("-------");
console.log(req);
console.log(res);
console.log("-------");
next();
}
全局中间件
全局中间件可以在main.ts
文件中进行定义使用,具体如下:
typescript
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);