Nest框架中对数据处理和验证 => 管道(Pipe)
管道与过滤器极为相似,运用在控制器中,不清楚的同学,可以先看看这两块,有助于更好的理解
管道能干什么
管道有两个典型的应用场景:
- 转换:管道将输入的数据,转换为所需的数据并输出(例如,将字符串转换为整数)
- 验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常
在这两种情况下, 管道参数(arguments)
是由 控制器(controllers)的路由处理程序 来进行处理。
Nest 会在调用控制器方法之前插入一个管道,管道会先拦截方法的调用参数,对参数进行转换或是验证处理,然后将转换好或是验证通过的参数,放入方法中进行逻辑处理,如果转换或验证失败,则会抛出异常。
注意:如果在
Pipe
处理和验证中发生异常,controller
不会继续执行任何方法
Nest 自带九个开箱即用的管道:
ValidationPipe
:验证管道是一个基于class-validator库的管道,它可以验证输入数据的正确性。它可以验证输入数据的类型、格式、长度、范围等,如果输入数据不符合要求,则会抛出异常。ParseIntPipe
:将输入数据转换为整数类型,如果无法转换,则会抛出异常。ParseFloatPipe
:将输入数据转换为浮点数类型,如果无法转换,则会抛出异常。ParseBoolPipe
:将输入数据转换为布尔类型,如果无法转换,则会抛出异常。ParseArrayPipe
:将输入数据转换为数组类型,可以指定分隔符和数组元素的类型。如果无法转换,则会抛出异常。ParseUUIDPipe
:将输入数据转换为UUID(通用唯一标识符)类型,如果无法转换,则会抛出异常。ParseEnumPipe
:将输入数据转换为枚举类型,如果无法转换,则会抛出异常。DefaultValuePipe
:如果输入数据为undefined,则使用默认值。ParseFilePipe
:将输入数据解析为文件对象,可以指定文件大小和文件类型。如果无法解析,则会抛出异常。
创建管道
使用CIL快速创建管道,执行$ nest g pipe user
命令
$ nest g pipe user
管道的转换应用
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@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;
}
}
转换器(PipeTransform)、注入器(Injectable)、参数元数据(ArgumentMetadata)和错误异常(BadRequestException)
配置管道来处理所传参数 id:
@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id) {
return this.catsService.findOne(id);
}
是按 ID 从数据库中选择一个现有的用户实体
@Get(':id')
findOne(@Param('id', UserByIdPipe) userEntity: UserEntity) {
return userEntity;
}
绑定管道
转换 URL 中的数据参数
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
@Param('id', ParseIntPipe)
表示该参数会从请求的URL中获取,并使用ParseIntPipe进行转换
,将其转换为整数类型。
验证查询字符串参数
@Get()
async findOne(@Query('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
@Query
:用于获取请求中的查询参数,即在URL中以“?”后面的参数,例如:http://example.com/api/cats?id=1
,其中id就是查询参数
@Param
:用于获取请求URL中的路径参数,即在URL中以“/”分隔的参数,例如:http://example.com/api/cats/1
,其中1就是路径参数。
管道验证
验证管道,验证通过返回值不变,验证失败抛出异常
使用class-validator
和class-transformer
库,创建一个类
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToInstance } from 'class-transformer';
@Injectable()
export class ValidationPipe implements PipeTransform<any> {
async transform(value: any, { metatype }: ArgumentMetadata) {
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToInstance(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);
}
}
在控制器中,绑定ValidationPipe
并使用
@Post()
async create(
@Body(new ValidationPipe()) createCatDto: CreateCatDto,
) {
this.catsService.create(createCatDto);
}
全局管道
全局管道用于整个应用程序、每个控制器和每个路由处理程序
在入口文件中,使用useGlobalPipes()
方法,新建一个全局通道
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 全局管道
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
从任何模块外部注册的全局管道(即使用了 useGlobalPipes()
, 如上例所示)无法注入依赖,因为它们不属于任何模块
为了能灵活的注入依赖,可以模块中设置通道
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe
}
]
})
export class AppModule {}
管道默认值
当接收到 null
或者 undefined
值时,它们会抛出异常。如果想不抛出异常,就可以给他一个默认值
DefaultValuePipe
提供了默认值这种能力
@Get()
async findAll(
@Query('activeOnly', new DefaultValuePipe(false), ParseBoolPipe) activeOnly: boolean,
@Query('page', new DefaultValuePipe(0), ParseIntPipe) page: number,
) {
return this.catsService.findAll({ activeOnly, page });
}
转载自:https://juejin.cn/post/7244721015371694137