mcpField
C
class-validator

class-validator

github.com/claude-dev-suite开发4645 次安装2 天前更新
代码仓库

关于 class-validator

面向 TypeScript 后端开发者的 class-validator 参考技能,介绍基于装饰器的 DTO 校验,并包含 NestJS 集成及常见配置问题。

主要功能

  1. 01字符串、数字、日期等校验
  2. 02嵌套对象与数组校验
  3. 03NestJS ValidationPipe 集成
  4. 04自定义校验器基础示例
  5. 05装饰器配置与故障排查

适用场景

  1. 01校验 API 请求 DTO
  2. 02在 NestJS 中过滤未知字段
  3. 03检查嵌套对象和数组数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill class-validator

安装后在 Claude Code 中输入/class-validator确认已加载。

附带文件advanced.md

详细介绍

Class-validator - 快速参考

完整参考:自定义验证器、跨字段验证、验证组、错误格式化和 class-transformer 集成,请参阅 advanced.md。

深入了解:使用 mcp__documentation__fetch_docs,并将 technology 设为:class-validator,获取全面的文档。

设置

npm install class-validator class-transformer
// tsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

基本用法

import {
  validate,
  IsString,
  IsEmail,
  IsInt,
  Min,
  Max,
  Length,
  IsOptional,
} from 'class-validator';
import { plainToInstance } from 'class-transformer';

class CreateUserDto {
  @IsString()
  @Length(2, 50)
  name: string;

  @IsEmail()
  email: string;

  @IsInt()
  @Min(18)
  @Max(120)
  age: number;

  @IsOptional()
  @IsString()
  bio?: string;
}

// Validate
async function validateUser(data: unknown) {
  const user = plainToInstance(CreateUserDto, data);
  const errors = await validate(user);

  if (errors.length > 0) {
    throw new Error(errors.map((e) => Object.values(e.constraints || {})).flat().join(', '));
  }

  return user;
}

常用装饰器

字符串验证器
import {
  IsString, IsNotEmpty, Length, MinLength, MaxLength,
  Matches, IsUUID, IsEmail, IsUrl, IsIP,
} from 'class-validator';

class StringValidationDto {
  @IsString()
  @IsNotEmpty()
  required: string;

  @Length(5, 20)
  username: string;

  @MinLength(8)
  @Matches(/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/, {
    message: 'Password must contain letters and numbers',
  })
  password: string;

  @IsEmail()
  email: string;

  @IsUrl({ require_protocol: true })
  website: string;

  @IsUUID('4')
  id: string;
}
数字验证器
import { IsNumber, IsInt, IsPositive, Min, Max, IsDivisibleBy } from 'class-validator';

class NumberValidationDto {
  @IsNumber()
  decimal: number;

  @IsInt()
  integer: number;

  @IsPositive()
  positive: number;

  @Min(0)
  @Max(100)
  percentage: number;
}
日期验证器
import { IsDate, MinDate, MaxDate, IsISO8601 } from 'class-validator';
import { Type } from 'class-transformer';

class DateValidationDto {
  @IsDate()
  @Type(() => Date)
  date: Date;

  @MinDate(new Date('2020-01-01'))
  @Type(() => Date)
  afterDate: Date;

  @IsISO8601()
  isoString: string;
}
数组验证器
import {
  IsArray, ArrayMinSize, ArrayMaxSize, ArrayUnique,
  ArrayNotEmpty, ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';

class ItemDto {
  @IsString()
  name: string;
}

class ArrayValidationDto {
  @IsArray()
  @ArrayNotEmpty()
  @ArrayMinSize(1)
  @ArrayMaxSize(10)
  @IsString({ each: true })  // Validate each item
  tags: string[];

  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => ItemDto)
  items: ItemDto[];
}
嵌套对象验证
import { IsObject, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';

class AddressDto {
  @IsString()
  street: string;

  @IsString()
  city: string;
}

class UserWithAddressDto {
  @IsString()
  name: string;

  @ValidateNested()
  @Type(() => AddressDto)
  address: AddressDto;
}
其他验证器
import {
  IsBoolean, IsEnum, IsIn, IsOptional,
  IsCreditCard, IsPhoneNumber,
} from 'class-validator';

enum UserRole {
  ADMIN = 'admin',
  USER = 'user',
  GUEST = 'guest',
}

class MixedValidationDto {
  @IsBoolean()
  active: boolean;

  @IsEnum(UserRole)
  role: UserRole;

  @IsIn(['draft', 'published', 'archived'])
  status: string;

  @IsOptional()
  @IsCreditCard()
  creditCard?: string;
}

NestJS 集成

// main.ts
import { ValidationPipe } from '@nestjs/common';

app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true,           // Strip non-decorated properties
    forbidNonWhitelisted: true, // Throw on unknown properties
    transform: true,            // Auto-transform payloads to DTO instances
    transformOptions: {
      enableImplicitConversion: true,
    },
  })
);
// users.controller.ts
@Controller('users')
export class UsersController {
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    // createUserDto is already validated and transformed
    return this.usersService.create(createUserDto);
  }

  @Get()
  findAll(@Query() query: PaginationQueryDto) {
    // Query params also validated
    return this.usersService.findAll(query);
  }

  @Get(':id')
  findOne(@Param('id', ParseUUIDPipe) id: string) {
    return this.usersService.findOne(id);
  }
}
部分 DTO(用于更新)
import { PartialType, OmitType, PickType, IntersectionType } from '@nestjs/mapped-types';

// All fields optional
export class UpdateUserDto extends PartialType(CreateUserDto) {}

// Omit specific fields
export class CreateUserWithoutPasswordDto extends OmitType(CreateUserDto, ['password']) {}

// Pick specific fields
export class LoginDto extends PickType(CreateUserDto, ['email', 'password']) {}

// Combine DTOs
export class ExtendedUserDto extends IntersectionType(CreateUserDto, AdditionalFieldsDto) {}

自定义验证器(基础)

import {
  registerDecorator,
  ValidationOptions,
  ValidatorConstraint,
  ValidatorConstraintInterface,
  ValidationArguments,
} from 'class-validator';

@ValidatorConstraint({ async: false })
export class IsStrongPasswordConstraint implements ValidatorConstraintInterface {
  validate(password: string, args: ValidationArguments) {
    const hasUppercase = /[A-Z]/.test(password);
    const hasLowercase = /[a-z]/.test(password);
    const hasNumber = /\d/.test(password);
    const minLength = password.length >= 8;

    return hasUppercase && hasLowercase && hasNumber && minLength;
  }

  defaultMessage(args: ValidationArguments) {
    return 'Password must contain uppercase, lowercase, and number';
  }
}

export function IsStrongPassword(validationOptions?: ValidationOptions) {
  return function (object: Object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [],
      validator: IsStrongPasswordConstraint,
    });
  };
}

// Usage
class RegisterDto {
  @IsStrongPassword()
  password: string;
}

不适用此技能的场景

  • 客户端验证 - 使用 Zod 和 React Hook Form
  • 非 NestJS 后端 - Zod 与框架的耦合更少
  • 函数式编程模式 - 装饰器需要类
  • 简单验证 - Zod 的 API 更简单

对比:class-validator 与 Zod

特性class-validatorZod
风格装饰器函数式
框架NestJS 标准与框架无关
转换class-transformer内置
包体积更大更小
类型推断手动自动

反模式

反模式不良之处正确做法
缺少 @Type() 装饰器嵌套验证会失败对嵌套对象使用 @Type()
ValidationPipe 中未启用 whitelist会接受未知属性设置 whitelist: true
在 DTO 中使用 any绕过验证使用适当的类型
在控制器中手动验证代码重复全局使用 ValidationPipe
缺少 experimentalDecorators装饰器无法工作在 tsconfig.json 中启用
未使用 class-transformer不会验证普通对象使用 plainToInstance()

快速故障排除

问题原因解决方案
装饰器无法工作tsconfig 配置错误启用 experimentalDecorators: true
嵌套验证失败缺少 @Type()添加 @Type(() => NestedClass)
未运行验证未设置 ValidationPipe在 main.ts 中全局添加 ValidationPipe
接受未知属性未启用 whitelist在 ValidationPipe 中设置 whitelist: true
转换不起作用缺少 transform 选项在 ValidationPipe 中设置 transform: true
异步验证器失败设置错误在 @ValidatorConstraint 中使用 async: true

检查清单

  • 在 tsconfig 中启用装饰器
  • 使用 class-transformer 进行嵌套验证/转换
  • 在 NestJS 中全局设置 ValidationPipe
  • 设置 whitelist 和 forbidNonWhitelisted
  • 针对特定逻辑编写自定义验证器
  • 为 API 响应格式化错误

参考

深入了解:使用 mcp__documentation__fetch_docs,并将 technology 设为:class-validator

评论

支持 Markdown