中间件与管道
Nest 中间件
中间件就是一个函数,在路由处理器之前调用。这就表示中间件函数可以访问到请求和响应对象以及应用的请求响应周期中的

- 执行任意的代码
- 对请求
/ 响应做操作 - 终结请求
- 响应周期 - 调用下一个栈中的中间件函数
- 如果当前的中间间函数没有终结请求响应周期,那么它必须调用
next() 方法将控制权传递给下一个中间件函数。否则请求将被挂起
中间件定义
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();
}
}
或者我们也可以使用函数式中间件,函数式的中间件可以用一个简单无依赖函数来实现:
export function logger(req, res, next) {
console.log(`Request...`);
next();
}
应用中间件
@Module({
imports: [CatsModule]
})
export class ApplicationModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes("cats");
}
}
上面的代码
.forRoutes({ path: 'cats', method: RequestMethod.GET });
.forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
当然,你也可以指定不包括某些路由规则:
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: "cats", method: RequestMethod.GET },
{ path: "cats", method: RequestMethod.POST }
)
.forRoutes(CatsController);
不过请注意
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
管道
管道(Pipes)是一个用

从官方的示意图中我们可以看出来管道
- 转换
/ 变形:转换输入数据为目标格式 - 验证:对输入数据时行验证,如果合法让数据通过管道,否则抛出异常。
管道会处理控制器路由的参数,
@Injectable()
export class ValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
return value;
}
}
类验证器
因为我们前面已经在控制器参数上使用了
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
要使用类验证器,你需要先安装
import { IsString, IsInt } from "class-validator";
export class CreateCatDto {
@IsString()
readonly name: string;
@IsInt()
readonly age: number;
@IsString()
readonly breed: string;
}
不过管道验证器中的代码也需要适配一下:
@Injectable()
export class ValidationPipe implements PipeTransform<any> {
async transform(value: any, { metatype }: ArgumentMetadata) {
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToClass(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException("Validation failed");
}
return value;
}
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
注意这次的
- 参数作用域
@Post()
async create(
@Body(new ValidationPipe()) createCatDto: CreateCatDto,
) {
this.catsService.create(createCatDto);
}
- 方法作用域
@Post()
@UsePipes(new ValidationPipe())
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
管道修饰器入参可以是类而不必是管道实例:
@Post()
@UsePipes(ValidationPipe)
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
这样以来将实例化过程留给框架去做并肝启用依赖注入。由于
async function bootstrap() {
const app = await NestFactory.create(ApplicationModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
转换
我们还可以用管道来进行数据转换,比如说上面的例子中
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException("Validation failed");
}
return val;
}
}
上面这个管道的功能就是强制转换成
@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id) {
return await this.catsService.findOne(id);
}