NestJS使用class-validator进行数据验证

作者:后端茶话时间:2026-10-31 16:48:18浏览:225

本文详细介绍在NestJS框架中结合class-validator与class-transformer进行数据验证的方法。通过安装依赖、定义DTO、配置管道及全局启用等步骤,掌握基础用法。文章进一步分享了11条实战技巧,包括局部验证、自定义错误、嵌套对象验证、动态消息生成及PartialType部分属性验证等,帮助开发者提升接口数据校验的准确性与安全性。

引言

当代Web开发范畴内,数据校验不可或缺,其作用在于保障数据精确度并强化系统防护能力。依托NestJS框架开展工程构建时,class-validator与class-transformer两大工具库赋予了开发者便捷的数据校验手段。

下文将依托详尽的操作流程与实战经验,引导读者精通在NestJS环境下运用class-validator执行数据校验的方法。阅读本指南后,您将掌握利用class-validator优雅实施数据校验的技术,并习得11项高频实战技巧,从而增强项目的数据校验效能。

操作流程

第一步:部署 class-validator 与 class-transformer

启用 class-validator 的前提是部署 class-validator 及 class-transformer 两个依赖库。

npm install class-validator class-transformer

第二步:构建 DTO(数据传输对象)

在 NestJS 架构中,普遍采用 DTO(Data Transfer Object)来界定请求数据的形态。初始阶段,需构建一个面向用户注册的 DTO 类,并借助 class-validator 提供的装饰器来设定校验规范。

// src/user/dto/create-user.dto.ts
import { IsString, IsEmail, IsNotEmpty, Length } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @IsNotEmpty()
  @Length(4, 20)
  username: string;

  @IsEmail()
  email: string;

  @IsString()
  @IsNotEmpty()
  @Length(8, 40)
  password: string;
}

该 DTO 内部声明了 username、email 以及 password 三个属性,并借由 class-validator 的装饰器明确了各项校验标准。

第三步:借助管道执行数据校验

随后,需在控制器层引入 DTO,并依托 NestJS 提供的管道(Pipes)机制对传入数据进行校验。

// src/user/user.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';

@Controller('user')
export class UserController {
  @Post('register')
  async register(@Body() createUserDto: CreateUserDto) {
    // 处理注册逻辑
    return { message: 'User registered successfully', data: createUserDto };
  }
}

此示例里,在 register 方法内通过 @Body() 装饰器提取请求体,并传入 CreateUserDto。NestJS 会自动对该 DTO 执行校验流程,若校验未通过,则抛出异常并返回对应的错误响应。

第四步:全局激活校验管道

为提升管理便捷性,可全局激活校验管道,使得所有 DTO 的校验操作均能自动执行。

// 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());
  await app.listen(3000);
}

bootstrap();

于 main.ts 文件中,借助 ValidationPipe 全局激活了校验管道。如此一来,无论控制器中如何运用 DTO,NestJS 均会自动执行数据校验。当然,亦可仅针对特定控制器激活校验管道,具体操作请参阅下方实战技巧。

实战应用技巧

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;
}

4. 组合校验装饰器

某些情况下需将多项校验规则整合,此时可运用 @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;
}

2.校验密码强度

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;
}

3.条件校验

依据条件执行校验,可运用 @ValidateIf 装饰器。

import { ValidateIf, IsNotEmpty, IsEmail } from 'class-validator';
export class UpdateUserDto {
  @IsEmail()
  email: string;
  @ValidateIf(o => o.email)
  @IsNotEmpty({ message: '新邮件地址不能为空' })
  newEmail: string;
}

4.使用 @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;
}

5.全局校验选项

全局激活校验管道时,可配置全局校验选项,诸如剥离非白名单字段、自动转换类型等。

// 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();

6.动态校验消息

某些场景下需依据具体校验条件动态生成错误消息,可运用 ValidationArguments 来实现。

import { IsString, MinLength, ValidationArguments } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @MinLength(4, {
    message: (args: ValidationArguments) => {
      return `用户名太短了,至少需要 ${args.constraints[0]} 个字符`;
    },
  })
  username: string;
}

7.@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;
}

8.属性分组校验

通过分组机制,可在不同情境下校验不同字段。例如在创建与更新操作中,可能需要校验不同的字段。

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 };
  }
}

9.仅执行部分属性校验

某些场景下仅需校验对象的部分属性,可运用 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 中的所有属性,但这些属性均为可选。这在更新操作中极具价值,因为我们可能仅需提交需要更新的字段,而非所有字段。

10.校验消息的国际化

通过运用自定义校验装饰器与消息生成函数,能够实现校验消息的国际化处理。

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;
}

总结

将 class-validator 与 NestJS 结合使用,使得在应用中进行数据校验变得轻松便捷,不仅提升了代码可读性,还确保了数据的精确度与安全性。

以上即为NestJS使用class-validator进行数据验证的详细内容,更多关于NestJS数据验证的资料请关注本站其它相关文章!

作者最新文章