Nest 中间件可以是一个函数,也可以是一个带有 @Injectable() 装饰器的类,且该类应该实现 NestMiddleware 接口,而函数没有任何特殊要求。
如下是一个日志中间件的简单示例:
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log('Request...');
next();
}
}
与提供者(Provider)和控制器(Controller)一样,他能够通过构造函数注入属于同一模块的依赖项:
import { Injectable, Inject, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';
@Injectable()
export class SomeMiddleware implements NestMiddleware {
constructor(@Inject(SomeService) private readonly someService: SomeService) {}
use(req: Request, res: Response, next: Function) {
// do some logic...
this.someService.method();
console.log('Request...');
next();
}
}
中间件是请求发出者和路由处理器之间的桥梁,我们只需要在模块类中实现 NestModule 接口:
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');
}
}
在上面的例子中,我们为 /cats 路由处理器(@CatsController('/cats'))设置了日志中间件。
如果只需要给 /cats 路由中的某几个请求方法设置这个中间件,那只需要改变一下 forRoutes() 方法中的参数即可:forRoutes({ path: 'cats', method: RequestMethod.GET }),此时,只有 GET 请求才会被中间件拦截。
Nest 提供了路由通配符的功能(与 Controller 中的路由通配符一样)
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL })
forRoutes() 方法中还可以传入一个控制器类,如:forRoutes(CatsController),他会将 CatsController 中的所有路由拦截并使用中间件。如果需要传入多个控制器类,只需要使用 , 分割,如: forRoutes(CatsController, UserController)。
不仅如此,apply() 方法同样可以传入一个或多个(用 , 分割)中间件,如:apply(LoggerMiddleware, OtherMiddleware)。
Nest 中的中间件可以是类,也可以是一个函数,上述都在讲关于类的中间件,这里使用函数来声明一个中间件:
export function logger(req, res, next) {
console.log(`Request...`);
next();
};
为了将中间件一次绑定到每个注册的路由,我们可以使用 INestApplication 实例中的 use() 方法:
const app = await NestFactory.create(ApplicationModule);
// 这里必须使用函数中间件
app.use(logger);
await app.listen(3000);
Nest 内置异常层负责处理整个应用程序中抛出的所有异常。当捕获到未处理的异常时,用户最终将收到适当的友好响应。
每个出现的异常都由全局异常过滤器处理,当无法识别时(既不是HttpException,也不是从HttpException继承的类),用户会收到以下JSON响应:
{
"statusCode": 500,
"message": "Internal server error"
}
当程序抛出一个 HttpException 对象时,它会被异常处理程序捕获,然后转换成相关的 JSON 响应。
在讲异常过滤器前,我们先熟悉框架的基础异常类:HttpException,HttpException 类来自于 @nestjs/common。正如你所想的,当程序抛出一个 HttpException 对象时,它会被异常处理程序捕获,然后转换成相关的 JSON 响应。
如何在控制器层抛出一个 HttpException 呢?
@Post()
async create(@Body() createCatDto: CreateCatDto) {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
此时,客户端会接收到如下 JSON 相应:
{
"statusCode": 403,
"message": "Forbidden"
}
HttpException 类的构造器中第一个参数可以是 string 或 object,当你将异常改写成如下时:
@Post()
async create(@Body() createCatDto: CreateCatDto) {
throw new HttpException({
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
}, 403);
}
客户端会收到:
{
"status": 403,
"error": "This is a custom message"
}
这种通常用来做自定义的异常消息返回
守卫是一个使用 @Injectable() 装饰器的类。 守卫应该实现 CanActivate 接口。
守卫有一个单独的责任。它们根据运行时出现的某些条件(例如权限,角色,访问控制列表等)来确定给定的请求是否由路由处理程序处理。
守卫在每个中间件之后执行,但在任何拦截器或管道之前执行。
管道是具有 @Injectable() 装饰器的类。管道应实现 PipeTransform 接口。
管道有两个类型:
转换:管道将输入数据转换为所需的数据输出
验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常;
拦截器是使用 @Injectable() 装饰器注解的类。拦截器应该实现 NestInterceptor 接口。
拦截器具有一系列有用的功能,这些功能受面向切面编程(AOP)技术的启发。它们可以:
在函数执行之前/之后绑定额外的逻辑
转换从函数返回的结果
转换从函数抛出的异常
扩展基本函数行为
根据所选条件完全重写函数 (例如, 缓存目的)
每个拦截器都有 intercept() 方法,它接收2个参数。 第一个是 ExecutionContext 实例(与守卫完全相同的对象),第二个参数是 CallHandler。
当我们要记录与应用程序的交互时,它很有用,例如 存储用户调用,异步调度事件或计算时间戳。
// logging.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
为了设置拦截器, 我们使用从 @nestjs/common 包导入的 @UseInterceptors() 装饰器。与守卫一样, 拦截器可以是控制器范围内的, 方法范围内的或者全局范围内的。
// cats.controller.ts
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
管道:数据处理与转换,数据验证
守卫:验证用户登陆,保护路由
拦截器:统一响应内容
过滤器:异常捕获
中间件:日志打印
参考阅读
代码已经上传到github中,欢迎大家star,持续更新,如有任何问题可以联系我v:sky201208(注明来意)
https://github.com/fozero/cloud-collect-nestjs
大家好,我是阿健Kerry,一个有趣且乐于分享的人,前小鹏汽车、货拉拉高级前端工程师,长期专注前端开发,如果你对前端&Node.js 学习进阶感兴趣的话(后续有计划也可以),可以关注我,加我微信【sky201208】,拉你进交流群一起交流、学习共同进步,群内氛围特别好,定期会组织技术分享~