1. 局部验证管道
可以为特定的路由或控制器方法配置验证管道,而无需全局启用。这样可以在不同的场景下灵活使用不同的验证规则。
import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
@Controller('user')
export class UserController {
@Post('register')
@UsePipes(new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}))
async register(@Body() createUserDto: CreateUserDto) {
// 处理注册逻辑
return { message: 'User registered successfully', data: createUserDto };
}
}
2. 自定义错误消息
class-validator 允许为每个验证规则定义自定义错误消息。例如:
import { IsString, IsNotEmpty, Length, IsEmail } from 'class-validator';
export class CreateUserDto {
@IsString({ message: '用户名必须是字符串' })
@IsNotEmpty({ message: '用户名不能为空' })
@Length(4, 20, { message: '用户名长度必须在4到20个字符之间' })
username: string;
@IsEmail({}, { message: '邮箱格式不正确' })
email: string;
@IsString({ message: '密码必须是字符串' })
@IsNotEmpty({ message: '密码不能为空' })
@Length(8, 40, { message: '密码长度必须在8到40个字符之间' })
password: string;
}
3. 嵌套对象验证
如果 DTO 中包含嵌套对象,可以使用 @ValidateNested() 装饰器进行验证。例如:
import { Type } from 'class-transformer';
import { ValidateNested, IsString, IsNotEmpty } from 'class-validator';
class AddressDto {
@IsString()
@IsNotEmpty()
street: string;
@IsString()
@IsNotEmpty()
city: string;
}
export class CreateUserDto {
@IsString()
@IsNotEmpty()
username: string;
@ValidateNested()
@Type(() => AddressDto)
address: AddressDto;
}
3. 组合验证装饰器
有时可能需要将多个验证规则组合在一起,这时可以使用 @ValidatorConstraint() 来创建自定义验证装饰器。例如:
- 判断数据库中是否已经存在用户名
import { registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator';
@ValidatorConstraint({ async: false })
export class IsUsernameUniqueConstraint implements ValidatorConstraintInterface {
validate(username: any) {
// 这里可以添加验证逻辑,例如查询数据库
return true; // 如果验证通过返回 true
}
}
export function IsUsernameUnique(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [],
validator: IsUsernameUniqueConstraint,
});
};
}
// 使用自定义装饰器
export class CreateUserDto {
@IsUsernameUnique({ message: '用户名已存在' })
username: string;
}
- 验证密码强度
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator';
export function IsStrongPassword(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isStrongPassword',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
return /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}/.test(value);
},
defaultMessage(args: ValidationArguments) {
return '密码必须包含大小写字母、数字和特殊字符,并且至少8个字符长';
},
},
});
};
}
export class ChangePasswordDto {
@IsStrongPassword({ message: '密码不符合强度要求' })
newPassword: string;
}
- 条件验证
根据条件进行验证,可以使用 @ValidateIf 装饰器。
import { ValidateIf, IsNotEmpty, IsEmail } from 'class-validator';
export class UpdateUserDto {
@IsEmail()
email: string;
@ValidateIf(o => o.email)
@IsNotEmpty({ message: '新邮件地址不能为空' })
newEmail: string;
}
- 使用 @Matches 进行正则表达式验证
使用 @Matches 装饰器,可以验证字符串是否与指定的正则表达式匹配。
import { Matches } from 'class-validator';
export class ChangePasswordDto {
@Matches(/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}/, { message: '密码必须包含大小写字母、数字和特殊字符,并且至少8个字符长' })
newPassword: string;
}
- 全局验证选项
全局启用验证管道时,可以配置全局验证选项,比如剥离非白名单字段、自动转换类型等。
// src/main.ts
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();
- 动态验证消息
有时可能需要根据具体的验证条件动态生成错误消息,可以使用 ValidationArguments 来实现。
import { IsString, MinLength, ValidationArguments } from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(4, {
message: (args: ValidationArguments) => {
return `用户名太短了,至少需要 ${args.constraints[0]} 个字符`;
},
})
username: string;
}
- @Validate 自定义验证逻辑
如果内置装饰器无法满足需求,可以使用 @Validate 装饰器添加自定义验证逻辑。
import { Validate, ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
@ValidatorConstraint({ name: 'customText', async: false })
class CustomTextConstraint implements ValidatorConstraintInterface {
validate(text: string, args: ValidationArguments) {
return text.startsWith('prefix_'); // 任何自定义逻辑
}
defaultMessage(args: ValidationArguments) {
return '文本 ($value) 必须以 "prefix_" 开头';
}
}
export class CustomTextDto {
@Validate(CustomTextConstraint)
customText: string;
}
- 属性分组验证
通过分组,可以在不同情境下验证不同的字段。比如在创建和更新时可能需要验证不同的字段。
import { IsString, IsNotEmpty } from 'class-validator';
export class CreateUserDto {
@IsString()
@IsNotEmpty({ groups: ['create'] })
username: string;
@IsString()
@IsNotEmpty({ groups: ['create', 'update'] })
password: string;
}
// 使用时指定组
import { ValidationPipe } from '@nestjs/common';
const createUserValidationPipe = new ValidationPipe({ groups: ['create'] });
const updateUserValidationPipe = new ValidationPipe({ groups: ['update'] });
在控制器中使用不同的管道进行验证:
import { Controller, Post, Put, Body, UsePipes } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { createUserValidationPipe, updateUserValidationPipe } from './validation-pipes';
@Controller('user')
export class UserController {
@Post('create')
@UsePipes(createUserValidationPipe)
async createUser(@Body() createUserDto: CreateUserDto) {
// 处理创建用户逻辑
return { message: 'User created successfully', data: createUserDto };
}
@Put('update')
@UsePipes(updateUserValidationPipe)
async updateUser(@Body() updateUserDto: CreateUserDto) {
// 处理更新用户逻辑
return { message: 'User updated successfully', data: updateUserDto };
}
}
- 仅执行部分属性验证
有时可能需要只验证对象的一部分属性,可以使用 PartialType 来实现。
import { PartialType } from '@nestjs/mapped-types';
export class UpdateUserDto extends PartialType(CreateUserDto) {}
以下是如何在控制器中使用 UpdateUserDto。
// src/user/user.controller.ts
import { Controller, Post, Put, Body, Param } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('user')
export class UserController {
@Post('create')
async createUser(@Body() createUserDto: CreateUserDto) {
// 处理创建用户逻辑
return { message: 'User created successfully', data: createUserDto };
}
@Put('update/:id')
async updateUser(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
// 处理更新用户逻辑
return { message: 'User updated successfully', data: updateUserDto };
}
}
在这个示例中,UpdateUserDto 继承自 PartialType(CreateUserDto),这意味着 UpdateUserDto 包含 CreateUserDto 中的所有属性,但这些属性都是可选的。这在更新操作中非常有用,因为我们可能只想提供那些需要更新的字段,而不是所有字段。
- 验证消息的国际化
通过使用自定义验证装饰器和消息生成函数,可以实现验证消息的国际化。
import { IsString, IsNotEmpty, Length, ValidationArguments } from 'class-validator';
import { i18n } from 'i18next'; // 假设在项目中使用 i18n
export class CreateUserDto {
@IsString()
@IsNotEmpty({ message: (args: ValidationArguments) => i18n.t('validation.usernameRequired') })
@Length(4, 20, { message: (args: ValidationArguments) => i18n.t('validation.usernameLength', { min: 4, max: 20 }) })
username: string;
}