如何用Class validator实现organisationId与organisation对象二选一校验
实现方案
可以通过class-validator的条件校验、嵌套校验能力,配合自定义互斥校验装饰器实现需求,具体步骤如下:
步骤1:自定义互斥字段校验装饰器
首先实现一个通用的互斥字段校验装饰器,用来保证两个字段必须传且仅传一个:
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'; export function IsMutuallyExclusive(relatedProperty: string, validationOptions?: ValidationOptions) { return function (target: Object, propertyName: string) { registerDecorator({ name: 'isMutuallyExclusive', target: target.constructor, propertyName: propertyName, constraints: [relatedProperty], options: validationOptions, validator: { validate(value: unknown, args: ValidationArguments) { const relatedValue = (args.object as any)[relatedProperty]; // 仅当两个字段恰好有一个存在时校验通过 return (value !== undefined && relatedValue === undefined) || (value === undefined && relatedValue !== undefined); }, defaultMessage(args: ValidationArguments) { return `\`${args.property}\` 和 \`${args.constraints[0]}\` 必须传且仅传其中一个`; }, }, }); }; }
步骤2:调整DTO类定义
修改创建用户的DTO,新增organisation字段,加上对应的校验装饰器,嵌套对象需要配合@Type做类型转换才能触发内部校验:
import { IsString, IsNumber, IsObject, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; // 导入上面写的自定义装饰器 import { IsMutuallyExclusive } from './your-decorator-path'; export class Organisation { @IsString() public name: string; } export class CreateUserDto { @IsString() public name: string; @IsMutuallyExclusive('organisation') @IsNumber() public organisationId?: number; @IsMutuallyExclusive('organisationId') @IsObject() @ValidateNested() @Type(() => Organisation) public organisation?: Organisation; }
校验效果说明
- 两个字段同时传/同时不传:会触发互斥校验的错误提示
- 仅传
organisationId:会自动校验该字段是否为数字类型 - 仅传
organisation:会自动触发Organisation类内部的字段校验,比如name必须为字符串,不需要额外编写规则
注意事项
如果是在NestJS框架中使用,需要确保全局校验管道开启了transform: true配置,否则嵌套的organisation对象不会被转换为Organisation实例,内部校验规则不会生效,管道配置参考:
// main.ts中配置 import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ transform: true, // 开启自动类型转换 })); await app.listen(3000); } bootstrap();
内容的提问来源于stack exchange,提问作者Vikas Kyatannawar
相关产品推荐
相关产品推荐

