NestJs 中使用 验证功能

NestJS 的 管道 学习文章中我们已经接触过 NestJs 中开箱即用的管道。而这篇文章主要是使用ValidationPipe实现更高级的定制功能。

使用内置的 ValidationPipe

因为内置的ValidationPipe管道内置使用了class-validatorclass-transformer所以我们首先需要安装这两个库。

$ npm i --save class-validator class-transformer

ValidationPipe依赖class-validatorclass-transformer库,所以当我们创建此管道后可以设置如下配置参数,来做一些管道初始化的操作:

选项类型描述
enableDebugMessagesboolean如果设置为 true,当出现问题时,验证器将向控制台打印额外的警告消息
skipUndefinedPropertiesboolean如果设置为 true,则验证器将跳过验证对象中未定义的所有属性的验证
skipNullPropertiesboolean如果设置为 true,则验证器将跳过验证对象中所有为 null 的属性的验证
skipMissingPropertiesboolean如果设置为 true,则验证器将跳过验证对象中所有为 null 或未定义的属性的验证
whitelistboolean如果设置为 true,验证器将删除已验证(返回)对象的任何不使用任何验证装饰器的属性
forbidNonWhitelistedboolean如果设置为 true,验证器将抛出异常,而不是剥离非白名单属性
forbidUnknownValuesboolean如果设置为 true,尝试验证未知对象会立即失败
disableErrorMessagesboolean如果设置为 true,验证错误将不会返回给客户端
errorHttpStatusCodenumber此设置允许您指定在发生错误时将使用哪种异常类型。默认情况下它会抛出 BadRequestException
exceptionFactoryFunction获取验证错误数组并返回要抛出的异常对象
groupsstring[]验证对象期间要使用的组
alwaysboolean设置 always 装饰器选项的默认值。可以在装饰器选项中覆盖默认值
strictGroupsboolean如果 groups 未给出或为空,则忽略至少一组的装饰器
dismissDefaultMessagesboolean如果设置为 true,验证将不会使用默认消息。undefined 如果没有明确设置,错误消息总是会出现
validationError.targetboolean指示目标是否应暴露在 ValidationError
validationError.valueboolean指示是否应在 中公开经过验证的值 ValidationError
stopAtFirstErrorboolean当设置为 true 时,给定属性的验证将在遇到第一个错误后停止。默认为 false

自动验证

我们将从 ValidationPipe 应用程序级别的绑定开始,从而确保所有端点都免受接收错误数据的影响。具体代码如下:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

我们添加了全局的校验器后,我们就可以在控制器中使用校验器,具体实例如下:

// 校验类
export class CreateDemoDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;

  @IsNotEmpty()
  password: string;

  @IsNumber()
  count: number;
}

// 控制器
@Post('/checkParams')
checkParams(@Body() createDemo: CreateDemoDto) {
  return 'This action adds a new user';
}

当我们的请求参数为:

{
  "name": "",
  "email": "",
  "password": "",
  "count": 1
}

接口返回结果如下:

{
  "statusCode": 400,
  "message": ["email must be an email", "password should not be empty"],
  "error": "Bad Request"
}

禁用详细错误

错误消息有助于解释请求中的错误内容。但是,某些生产环境更喜欢禁用详细错误。通过将选项对象传递给 ValidationPipe:

app.useGlobalPipes(
  new ValidationPipe({
    // 设置为true后,错误信息就不会在响应正文中显示
    disableErrorMessages: true,
  })
);

当我们发送上述的 JSON 请求参数后,接口返回的错误信息如下:

{
  "statusCode": 400,
  "message": "Bad Request"
}

剔除不需要检测的属性

ValidationPipe还可以过滤掉不必要的属性,主要我们在ValidationPipe中开启白名单就可以实现过滤属性功能。具体实例代码如下:

export class CreateDemoDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;

  @IsNotEmpty()
  password: string;

  @IsNumber()
  count: number;
}

请求参数为:

{
  "name": "Tom",
  "email": "Tom@qq.com",
  "password": "123456",
  "count": 1,
  "age": 12 // DTO中不存在该属性,参数接受不会显示该属性
}

上述我们只是开启了白名单,只是简单让不需要的属性过滤掉,我们还可以在存在非白名单属性时停止处理请求,并返回对应的错误信息,要开启此功能只要把forbidNonWhitelisted属性设置为 true 即可。具体实例如下:

app.useGlobalPipes(
  new ValidationPipe({
    disableErrorMessages: true,
    whitelist: true,
    forbidNonWhitelisted: true,
  })
);

请求对象转为具体的 DTO 对象

通过网络传入的有效负载是纯 JavaScript 对象。可以 ValidationPipe 自动将请求参数转为对应的 DTO 对象。要启动自动转换,只要设置transform属性为 true 即可。具体的代码如下:

// 方法级别开启转换
@Post()
@UsePipes(new ValidationPipe({ transform: true }))
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

// 全局开启转换
app.useGlobalPipes(
  new ValidationPipe({
    transform: true,
  }),
);

启用自动转换选项后,还将 ValidationPipe 执行基本类型的转换。在以下示例中,该 findOne()方法采用一个参数来表示提取的 id 路径参数,但是@Query()是不支持自动转换对象的:

@Get(':id')
findOne(@Param('id') id: number) {
  // 会自动把Id值转为数值
  console.log(typeof id === 'number'); // true
  return 'This action returns a user';
}

显示转换

在上面的例子中,我们开启了自动转换功能来转换对应的参数类型,我们还可以使用管道来帮我们转换数据类型。具体实例如下:

@Get(':id')
findOne(
  @Param('id', ParseIntPipe) id: number,
  @Query('sort', ParseBoolPipe) sort: boolean,
) {
  console.log(typeof id === 'number'); // true
  console.log(typeof sort === 'boolean'); // true
  return 'This action returns a user';
}

解析和验证数组

TypeScript 不存储有关泛型或接口的元数据,因此当您在 DTO 中使用它们时,ValidationPipe 可能无法正确验证传入数据。例如,在以下代码中,createUserDtos 将无法正确验证:

@Post()
createBulk(@Body() createUserDtos: CreateUserDto[]) {
  return 'This action adds new users';
}

要验证数组,请创建一个专用类,其中包含包装数组的属性,或使用 ParseArrayPipe。

@Post()
createBulk(
  @Body(new ParseArrayPipe({ items: CreateUserDto }))
  createUserDtos: CreateUserDto[],
) {
  return 'This action adds new users';
}

此外,ParseArrayPipe 在解析查询参数时可能会派上用场。让我们考虑一种 findByIds()根据作为查询参数传递的标识符返回用户的方法。

@Get()
findByIds(
  // 请求参数为/?ids=1,2,3,通过ParseArrayPipe可以把ids解析成数组
  @Query('ids', new ParseArrayPipe({ items: Number, separator: ',' }))
  ids: number[],
) {
  return 'This action returns users by ids';
}

DTO 编写技巧

构建输入验证类型(也称为 DTO)时,在同一类型上构建创建和更新变体通常很有用。例如,创建变体可能需要所有字段,而更新变体可能使所有字段可选。

  • PartialType() 函数

在 NestJS 中提供了PartialType()的实用函数,使用此函数可以最大程度上减少样板代码和简化代码量。具体实例如下:

export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}

//
export class UpdateCatDto extends PartialType(CreateCatDto) {}

说明: PartialType 函数是需要安装@nestjs/mapped-types插件包。

  • PartialType() 函数

PartialType() 函数主要是在原先的类上选择需要继承的属性。具体实例如下:

export class CreateDemoDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;

  @IsNotEmpty()
  password: string;

  @IsInt()
  age: number;
}

// 选择继承age、name属性
export class UpdateDemoAgeDto extends PickType(CreateDemoDto, [
  "age",
  "name",
] as const) {}
  • OmitType() 函数

OmitType() 函数主要是在原先的类上删除不需要的属性。具体实例如下:

// 在CreateDemoDto原先类的基础上剔除name属性
export class UpdateDemoExDto extends OmitType(CreateDemoDto, [
  "name",
] as const) {}
  • IntersectionType() 函数

IntersectionType() 函数两种类型组合成一种新类型(类)。具体实例如下:

export class AdditionalDemoInfo {
  color: string;
}

// 将CreateDemoDto类和AdditionalDemoInfo类合并成UpdateDemoDto,这样UpdateDemoDto就具有CreateDemoDto和AdditionalDemoInfo的所有属性
export class UpdateDemoDto extends IntersectionType(
  CreateDemoDto,
  AdditionalDemoInfo
) {}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值