You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS中如何在嵌套DTO中实现条件验证?

NestJS 嵌套DTO跨层级条件验证实现

问题背景

开发NestJS项目时需实现以下条件验证:
请求Payload结构:

{
  "user_id": "123",
  "user_type": "M", // 仅支持M/F
  "inner_details": {
     "name": {
             "firstname":"akshay",
             "lastname":"nikte"
           },
     "email": "ak@test.com"
  },
  "user_co": "Tesla"
}

验证规则:

  • user_type = M:firstname、lastname均不能为空
  • user_type = F:仅firstname不能为空,lastname可选

已拆分三个DTO文件,但嵌套的NameDTO无法直接获取上层CreateUserDTO的user_type属性完成条件验证。

现有DTO代码:

create-user.dto.ts

import { IsNotEmpty, ValidateNested } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { InnerDetailsDTO } from './inner-details.dto';

export class CreateUserDTO {
    
    @IsNotEmpty()
    @ApiProperty({ required: true })
    user_id: string
    
    @IsNotEmpty()
    @ApiProperty({ required: true })
    user_type: string
    
    @ValidateNested({ each: true })
    @Type(() => InnerDetailsDTO)
    inner_details: InnerDetailsDTO
    
    @IsNotEmpty()
    user_co: string
}

inner-details.dto.ts

import { IsNotEmpty, ValidateNested } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { NameDTO } from './name.dto';

export class InnerDetailsDTO {
    
    @ValidateNested({ each: true })
    @Type(() => NameDTO)
    name: NameDTO

    @IsNotEmpty()
    @ApiProperty({ required: true })
    email: string
}

name.dto.ts

import { IsNotEmpty, ValidateIf } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class NameDTO {
    
    @IsNotEmpty()
    @ApiProperty({ required: true })
    firstname: string

    // 需根据上层user_type做条件验证
    @ValidateIf(/* 如何获取user_type? */)
    @IsNotEmpty()
    lastname: string  
}

解决方案

方法1:自定义验证器(推荐)

利用class-validator的ValidationArguments获取顶层对象,实现跨层级属性访问:

  1. 创建自定义验证器文件lastname.validator.ts:
import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
import { CreateUserDTO } from './create-user.dto';

@ValidatorConstraint({ name: 'lastnameRequired', async: false })
export class LastnameRequiredConstraint implements ValidatorConstraintInterface {
  validate(lastname: string, args: ValidationArguments) {
    // 通过args.root直接获取顶层CreateUserDTO实例
    const userType = (args.root as CreateUserDTO).user_type;
    
    // 仅当user_type为M时验证lastname非空
    if (userType === 'M') {
      return !!lastname && lastname.trim() !== '';
    }
    return true;
  }

  defaultMessage() {
    return '当user_type为M时,lastname不能为空';
  }
}
  1. 在name.dto.ts中引入并使用该验证器:
import { IsNotEmpty, Validate } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { LastnameRequiredConstraint } from './lastname.validator';

export class NameDTO {
    
    @IsNotEmpty()
    @ApiProperty({ required: true })
    firstname: string

    @Validate(LastnameRequiredConstraint)
    lastname: string  
}

方法2:顶层DTO统一验证

将验证逻辑集中在CreateUserDTO,避免跨层级访问:

  1. 定义顶层验证约束:
import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
import { CreateUserDTO } from './create-user.dto';

@ValidatorConstraint({ name: 'userNameValidation', async: false })
export class UserNameValidationConstraint implements ValidatorConstraintInterface {
  validate(user: CreateUserDTO) {
    const { user_type, inner_details } = user;
    const { firstname, lastname } = inner_details.name;

    // 验证firstname始终非空
    if (!firstname?.trim()) return false;
    // user_type为M时验证lastname非空
    if (user_type === 'M' && !lastname?.trim()) return false;
    
    return true;
  }

  defaultMessage(args: ValidationArguments) {
    const user = args.object as CreateUserDTO;
    if (!user.inner_details.name.firstname?.trim()) {
      return 'firstname不能为空';
    }
    if (user.user_type === 'M' && !user.inner_details.name.lastname?.trim()) {
      return '当user_type为M时,lastname不能为空';
    }
    return '用户信息验证失败';
  }
}
  1. 在CreateUserDTO上添加验证:
import { Validate } from 'class-validator';
import { UserNameValidationConstraint } from './user-name.validator';

@Validate(UserNameValidationConstraint)
export class CreateUserDTO {
    // 原有属性代码...
}

额外优化

  • 为user_type添加可选值验证,提前拦截非法输入:
// 在CreateUserDTO的user_type字段添加
@IsIn(['M', 'F'])
user_type: string;
  • 确保NestJS全局启用验证管道(main.ts):
import { ValidationPipe } from '@nestjs/common';

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

内容的提问来源于stack exchange,提问作者Akki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 05:24:12