乐闻世界logo
搜索文章和话题

How to alidate nested objects using class validator in nestjs?

7 个月前提问
3 个月前修改
浏览次数153

4个答案

1
2
3
4

在 NestJS 中,可以通过使用 class-validator 包和 class-transformer 包来实现类验证。以下是如何使用这些工具来校验一个类的属性以及嵌套对象的步骤:

安装必需的包

首先,需要安装 class-validatorclass-transformer。可以使用 npm 或 yarn 来安装。

bash
npm install class-validator class-transformer # 或者 yarn add class-validator class-transformer

创建 DTO (Data Transfer Object) 类

在 NestJS 中,通常会创建 DTO (数据传输对象) 类,用于定义接收到的数据结构,并在类中应用验证规则。

typescript
import { IsString, IsInt, ValidateNested, IsEmail } from 'class-validator'; import { Type } from 'class-transformer'; class UserDto { @IsString() readonly name: string; @IsEmail() readonly email: string; @IsInt() readonly age: number; } class CreateUserDto { @ValidateNested({ each: true }) @Type(() => UserDto) readonly user: UserDto; }

在这个例子中,CreateUserDto 包含了一个嵌套的 UserDto 对象。使用 @ValidateNested() 装饰器来表明该属性是一个嵌套对象,并且需要被校验。@Type(() => UserDto)class-transformer 的装饰器,它告诉 NestJS 如何将原始数据转换为 UserDto 实例。

在控制器中使用 DTO

在控制器中,我们使用 DTO 类来接收和校验客户端发送的数据。

typescript
import { Body, Controller, Post } from '@nestjs/common'; import { CreateUserDto } from './create-user.dto'; @Controller('users') export class UsersController { @Post() async create(@Body() createUserDto: CreateUserDto) { // createUserDto 在这里已经通过验证,并且是一个 CreateUserDto 的实例 // 可以在这里实现创建用户的逻辑 } }

启用全局验证管道

为了使 class-validator 能够自动进行验证,需要设置 NestJS 全局验证管道。可以在应用的根模块或者 main.ts 中启用它。

typescript
import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, })); await app.listen(3000); } bootstrap();

在这个配置中,whitelist 设置为 true 会自动去除非白名单属性(即 DTO 中未定义的属性),而 forbidNonWhitelisted 设置为 true 会在接收到非白名单属性时抛出错误。transform 选项会自动将客户端的原始数据转换为 DTO 类的实例。

错误处理

如果输入数据不满足 DTO 类中定义的验证规则,NestJS 会抛出一个异常。通常,异常会被全局异常过滤器捕获,并返回一个包含错误信息的响应给客户端。如果需要定制错误信息,可以创建自定义的异常过滤器。

通过上述步骤,可以在 NestJS 应用程序中实现类验证,以及嵌套对象的校验。这种方法使得数据验证变得既简洁又强大,并且保证了在进入业务逻辑之前,输入数据的正确性和有效性。

2024年6月29日 12:07 回复

对我来说,我可以使用以下方法验证嵌套对象'class-transformer'

shell
import { Type } from 'class-transformer';

完整示例:

shell
import { MinLength, MaxLength, IsNotEmpty, ValidateNested, IsDefined, IsNotEmptyObject, IsObject, IsString, } from 'class-validator'; import { Type } from 'class-transformer'; class MultiLanguageDTO { @IsString() @IsNotEmpty() @MinLength(4) @MaxLength(40) en: string; @IsString() @IsNotEmpty() @MinLength(4) @MaxLength(40) ar: string; } export class VideoDTO { @IsDefined() @IsNotEmptyObject() @IsObject() @ValidateNested() @Type(() => MultiLanguageDTO) name!: MultiLanguageDTO; }
2024年6月29日 12:07 回复

您预计positions: [1]会抛出 400,但结果被接受了。

根据这个Github 问题,这似乎是类验证器中的一个错误。如果您传入原始类型(booleanstringnumber、...)或 anarray而不是对象,它将接受输入作为有效输入,尽管它不应该接受。


除了创建自定义验证装饰器之外,我没有看到任何标准解决方法:

shell
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'; export function IsNonPrimitiveArray(validationOptions?: ValidationOptions) { return (object: any, propertyName: string) => { registerDecorator({ name: 'IsNonPrimitiveArray', target: object.constructor, propertyName, constraints: [], options: validationOptions, validator: { validate(value: any, args: ValidationArguments) { return Array.isArray(value) && value.reduce((a, b) => a && typeof b === 'object' && !Array.isArray(b), true); }, }, }); }; }

然后在你的 dto 类中使用它:

shell
@ValidateNested({ each: true }) @IsNonPrimitiveArray() @Type(() => PositionDto) positions: PositionDto[];
2024年6月29日 12:07 回复

我遇到了同样的问题,所以我创建了自己的ValidateNested装饰器。

shell
import { ValidationOptions, registerDecorator, ValidationArguments, validateSync, } from 'class-validator'; import { plainToClass } from 'class-transformer'; /** * @decorator * @description A custom decorator to validate a validation-schema within a validation schema upload N levels * @param schema The validation Class */ export function ValidateNested( schema: new () => any, validationOptions?: ValidationOptions ) { return function (object: Object, propertyName: string) { registerDecorator({ name: 'ValidateNested', target: object.constructor, propertyName: propertyName, constraints: [], options: validationOptions, validator: { validate(value: any, args: ValidationArguments) { args.value; if (Array.isArray(value)) { for (let i = 0; i < (<Array<any>>value).length; i++) { if (validateSync(plainToClass(schema, value[i])).length) { return false; } } return true; } else return validateSync(plainToClass(schema, value)).length ? false : true; }, defaultMessage(args) { if (Array.isArray(args.value)) { for (let i = 0; i < (<Array<any>>args.value).length; i++) { return ( `${args.property}::index${i} -> ` + validateSync(plainToClass(schema, args.value[i])) .map((e) => e.constraints) .reduce((acc, next) => acc.concat(Object.values(next)), []) ).toString(); } } else return ( `${args.property}: ` + validateSync(plainToClass(schema, args.value)) .map((e) => e.constraints) .reduce((acc, next) => acc.concat(Object.values(next)), []) ).toString(); }, }, }); }; }

然后你可以像这样使用它 -

shell
class Schema2 { @IsNotEmpty() @IsString() prop1: string; @IsNotEmpty() @IsString() prop2: string; } class Schema1 { @IsNotEmpty() @IsString() prop3: string; @ValidateNested(Schema2) nested_prop: Schema2; }

适用于非原始数组和 JavaScript 对象。

2024年6月29日 12:07 回复

你的答案