关于 Next 中间件
Nest 中间件中间件是处于请求和响应周期中间的函数,类似 express 中间件,可以访问请求对象(req
)、响应对象(res
)、和应用程序的下一个中间件函数。
中间件的主要任务是可以执行以下操作:
- 执行任何代码。
- 修改请求和响应对象。
- 结束请求-响应周期。
- 调用堆栈中的下一个中间件函数。
如果当前中间件没有结束请求-响应周期,它必须调用 next()
方法将控制权传递给下一个中间件,否则请求将被挂起。
Nest 中间件应用场景:
- 请求日志记录:记录每个进入应用的请求的详细信息,如请求路径、方法、来源IP等,便于调试和监控。
- 身份验证和授权:在请求继续处理之前验证用户的身份,检查用户是否有权限访问特定的路由或资源。
- 请求数据处理:对请求中的数据进行预处理,如解析、格式化、校验等。
- 设置响应头:为即将发送的响应设置一些通用的HTTP头,如跨域资源共享(CORS)头、安全相关的头等。
- 性能监控:监控请求处理的时间,以便分析和优化性能。
- 缓存:实现缓存逻辑,减少对后端服务或数据库的请求,提高应用性能。
- 限流:控制请求的频率,防止服务被过度使用或遭受拒绝服务攻击(DDoS)。
- 错误处理:捕获请求处理过程中的异常,进行统一的错误处理。
- 国际化:根据请求头或其他指示来设置语言环境,实现内容的国际化。
- API版本管理:根据请求的版本信息(如URL路径、请求头)来路由到不同版本的处理逻辑。
创建中间件
Nest 中间件可以是一个函数,也可以是一个带有 @Injectable()
装饰器,实现 NestMiddleware
接口的类:
创建 Nest 项目:
bash
nest new middleware-test -p npm
创建 middleware:
bash
nest g middleware logger --no-spec --flat
手动标注类型, 并在 next 前后插入一些代码:
或者是一个函数:
它们之间的区别也就在,类可以实现依赖注入。而函数不行。
使用中间件
在 AppModule 模块中,使用 configure
方法来设置中间件。你可以为整个应用、特定路由或一组路由应用中间件。
forRoutes
方法用于指定中间件应用到的路由。它可以接收多种不同类型的参数,以满足不同的路由匹配需求。
- 字符串路径:
可以直接传递一个字符串,表示中间件将应用于该特定路径。
javascript
.forRoutes('users');
- 路径模式(带通配符):
可以使用通配符*
来匹配一组路由。
javascript
.forRoutes('users/*');
- 控制器类:
可以传递一个控制器类,中间件将应用于该控制器中定义的所有路由。
javascript
.forRoutes(UsersController);
- 路由对象:
可以传递一个对象,其中包含path
和method
属性,用于更精确地定义中间件应用的路由和方法。
javascript
.forRoutes({ path: 'users', method: RequestMethod.GET });
- 路由对象数组:
还可以传递一个路由对象数组,以将中间件应用于多个不同的路由和方法。
javascript
.forRoutes(
{ path: 'users', method: RequestMethod.GET },
{ path: 'users', method: RequestMethod.POST },
{ path: 'admin', method: RequestMethod.ALL }
);
- 控制器和方法数组:
你可以传递一个数组,其中包含控制器类和方法,以将中间件应用于特定控制器的特定方法。
javascript
.forRoutes({ path: 'users', method: RequestMethod.ALL }, UsersController);
使用 forRoutes
方法时,可以根据需要混合使用这些配置,以实现对中间件应用的精确控制。
创建并绑定多个中间件
绑定多个中间件,可以在 configure
方法中使用链式调用 .apply()
方法,并传入多个中间件。
我们先创建个中间件:
也可以这样写:
中间件会按照从左到右,从上到下顺序对所有路由生效,我们访问页面,看下打印结果:
创建全局中间件
全局中间件在整个应用程序中的每个路由上都会生效。
除了上面使用的 forRoutes('*')
,还可以在 main.ts
文件中使用 app.use()
方法:
typescript
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './logger.middleware';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(logger); // 将 logger 函数注册为全局中间件
await app.listen(3000);
}
bootstrap();
这种形式不能注入依赖,也不能指定路由,不建议使用。
middleware 和 interceptor 区别
Nest 的 middleware 和 interceptor 都可以在请求前后加入一些逻辑,有什么区别呢?
- interceptor 是能从 context 里拿到目标 class 和 handler,进而通过 reflector 拿到它的 metadata 等信息的,middleware 就不可以。
- interceptor 可以用 rxjs 操作符来组织响应处理流程,middleware 也不可以。
interceptor 更适合处理与具体业务相关的逻辑,而 middleware 适合更通用的处理逻辑。