Nest.js 从零到壹详细系列(六):中间件1. 基本概念 中间件是Express很强大的一个功能,提供了丰富的扩展性
1. 基本概念
中间件是Express很强大的一个功能,提供了丰富的扩展性,所有的逻辑处理都通过中间件实现。任何一个中间件都可以对request和response对象访问和存取,再用next()函数将控制权交给下一个中间件。
NestJS 使用 Express.js 作为其默认的 HTTP 服务器实现。这意味着 NestJS 应用程序底层仍然可以访问所有 Express.js 提供的功能,例如中间件。
之前介绍的过滤器和拦截器的底层还是express的中间件概念,只不过在Nest中,根据使用场景对中间件进行概念拆分:拦截器注重请求的全生命周期控制,例如更改返回的数据格式;过滤器只关注异常情况,例如异常日志记录;中间件原则不控制请求源到目标的数据,只做翻译、转换。例如某个通用的逻辑,被很多API用到,那就可以为这一批API设置中间件。类似于hooks,都是逻辑抽离与复用;
2. 基本使用
1. 命令行快捷创建中间件
nest g mi core/middleware/Logger --no-spec --flat
2. 定义中间件,修改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();
}
}
req和res分别对应请求和响应对象,next()函数是将控制权交给下一个。
3. 注册,修改app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from 'src/core/middleware/logger.middleware';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('/');
}
}
中间件不能在 @Module() 装饰器中列出。我们必须使用模块类的 configure() 方法来设置它们
configure 方法接受一个 MiddlewareConsumer 类型的参数。MiddlewareConsumer 是一个可以用来注册中间件的对象
apply方法注册要应用的中间件
forRoutes方法指定了中间件应该应用于哪些路由
在forRoutes中,除了定义中间件应用的路由,还可以进一步将中间件限制为特定的请求方法。修改app.module.ts
//...省略
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: '/', method: RequestMethod.GET });
}
}
3. 路由通配符
路由同样支持模式匹配。例如,星号被用作通配符,将匹配任何字符组合。
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
以上路由地址将匹配 abcd 、 ab_cd 、 abecd 等。字符 ? 、 + 、 * 以及 () 是它们的正则表达式对应项的子集。连字符 (-) 和点 (.) 按字符串路径解析。
4. 排除某些路由
forRoutes可以定义中间件应用哪些路由,exclude可以定义中间件不能应用哪些路由
consumer
.apply(LoggerMiddleware)
.exclude('cats/(.*)')
.forRoutes('/');
5. 应用控制器
forRoutes可接受一个字符串、多个字符串、对象、一个控制器类或多个控制器类。在大多数情况下,只需传递一个由逗号分隔的控制器列表。
//...
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(AppController,CatsController);
}
}
6. 多个中间件
1. 如果多个中间件有相同的作用域,则可将多个中间件一同注册到模块中,使用逗号分隔。
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware,ReqMiddleware)
.forRoutes('/');
}
}
2. 如果中间件有不同的作用域,可通过链式调用,分别注册。
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({path:'/',method:RequestMethod.GET})
.apply(ReqMiddleware)
.forRoutes({path:'/user',method:RequestMethod.GET});
}
}
7. 函数中间件
除了以类方式声明的中间件,Nest也支持以函数方式定义中间件,logger.middleware.ts的内容等同于
export function logger(req, res, next) {
console.log(`Request...`);
next();
};
当中间件没有任何依赖关系时,可以考虑使用函数式中间件。这也是Express中最常用的中间件定义形式。
结尾
后续会继续更新Nest系列文章,感兴趣的可先关注我。
转载自:https://juejin.cn/post/7414157132972851236