如何在NestJS DTO中基于其他字段管控必填与可选字段?
解决方案
方案一:使用 @ValidateIf 装饰器实现条件必填
这种方式适合简单的条件关联校验,直接在字段上根据loginType的值动态启用必填规则。
修改后的LoginDto代码:
import { LOGIN_TYPE } from "src/utils/constants"; import { IsIn, IsNotEmpty, IsOptional, ValidateIf } from "class-validator"; export class LoginDto { // 当登录类型为手机号OTP时,手机号必填 @ValidateIf((obj) => obj.loginType === LOGIN_TYPE.PHONE_NUMBER_OTP) @IsNotEmpty({ message: "手机号不能为空" }) phoneNumber?: string; @IsOptional() fcmToken?: string; // 当登录类型为第三方平台时,accessToken必填 @ValidateIf((obj) => [LOGIN_TYPE.GOOGLE, LOGIN_TYPE.APPLE, LOGIN_TYPE.FACEBOOK].includes(obj.loginType)) @IsNotEmpty({ message: "第三方登录令牌不能为空" }) accessToken?: string; @IsIn([LOGIN_TYPE.APPLE, LOGIN_TYPE.GOOGLE, LOGIN_TYPE.FACEBOOK, LOGIN_TYPE.PHONE_NUMBER_OTP]) loginType: string = LOGIN_TYPE.PHONE_NUMBER_OTP; }
说明:
@ValidateIf接收回调函数,返回true时才会执行后续的校验规则- 移除了
loginType的@IsOptional,因为登录类型是核心参数,即使前端不传也有默认值兜底 - 字段后加
?标记为可选类型,配合条件校验实现动态必填逻辑
方案二:自定义类级验证器(适配复杂场景)
如果需要更复杂的多字段关联校验逻辑,可以使用类级别的自定义验证器,对整个DTO实例进行校验。
步骤1:创建自定义验证器
// src/validators/login-validation.validator.ts import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from "class-validator"; import { LOGIN_TYPE } from "../utils/constants"; @ValidatorConstraint({ name: "loginValidation", async: false }) export class LoginValidation implements ValidatorConstraintInterface { validate(dto: any) { const { loginType, phoneNumber, accessToken } = dto; switch (loginType) { case LOGIN_TYPE.PHONE_NUMBER_OTP: return !!phoneNumber; case LOGIN_TYPE.GOOGLE: case LOGIN_TYPE.APPLE: case LOGIN_TYPE.FACEBOOK: return !!accessToken; default: return false; } } defaultMessage(args: ValidationArguments) { const { loginType } = args.object as any; return loginType === LOGIN_TYPE.PHONE_NUMBER_OTP ? "手机号不能为空" : "第三方登录令牌不能为空"; } }
步骤2:在DTO中应用验证器
import { LOGIN_TYPE } from "src/utils/constants"; import { IsIn, IsOptional, Validate } from "class-validator"; import { LoginValidation } from "./validators/login-validation.validator"; @Validate(LoginValidation) export class LoginDto { @IsOptional() phoneNumber?: string; @IsOptional() fcmToken?: string; @IsOptional() accessToken?: string; @IsIn([LOGIN_TYPE.APPLE, LOGIN_TYPE.GOOGLE, LOGIN_TYPE.FACEBOOK, LOGIN_TYPE.PHONE_NUMBER_OTP]) loginType: string = LOGIN_TYPE.PHONE_NUMBER_OTP; }
说明:
- 类级验证器会对整个DTO实例做校验,适合多字段联动的复杂规则
- 可以在
validate方法中编写任意自定义校验逻辑 defaultMessage可根据校验场景返回针对性错误提示
注意事项
- 确保NestJS全局管道已启用
ValidationPipe并配置transform: true(你已声明使用该配置) - 两种方案均无需在服务层处理校验逻辑,完全在DTO内部完成校验
内容的提问来源于stack exchange,提问作者Yogesh Rao
相关产品推荐
相关产品推荐

