NestJS 项目中如何使用 class-validator 进行数据验证

前言

在现代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;
}

3. 组合验证装饰器

有时可能需要将多个验证规则组合在一起,这时可以使用 @ValidatorConstraint() 来创建自定义验证装饰器。例如:

  1. 判断数据库中是否已经存在用户名
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;
}
  1. 验证密码强度
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;
}
  1. 条件验证
    根据条件进行验证,可以使用 @ValidateIf 装饰器。
import { ValidateIf, IsNotEmpty, IsEmail } from 'class-validator';
export class UpdateUserDto {@IsEmail()email: string;@ValidateIf(o => o.email)@IsNotEmpty({ message: '新邮件地址不能为空' })newEmail: string;
}
  1. 使用 @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;
}
  1. 全局验证选项
    全局启用验证管道时,可以配置全局验证选项,比如剥离非白名单字段、自动转换类型等。
// 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();
  1. 动态验证消息
    有时可能需要根据具体的验证条件动态生成错误消息,可以使用 ValidationArguments 来实现。
import { IsString, MinLength, ValidationArguments } from 'class-validator';export class CreateUserDto {@IsString()@MinLength(4, {message: (args: ValidationArguments) => {return `用户名太短了,至少需要 ${args.constraints[0]} 个字符`;},})username: string;
}
  1. @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;
}
  1. 属性分组验证
    通过分组,可以在不同情境下验证不同的字段。比如在创建和更新时可能需要验证不同的字段。
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 };}
}
  1. 仅执行部分属性验证
    有时可能需要只验证对象的一部分属性,可以使用 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 中的所有属性,但这些属性都是可选的。这在更新操作中非常有用,因为我们可能只想提供那些需要更新的字段,而不是所有字段。

  1. 验证消息的国际化
    通过使用自定义验证装饰器和消息生成函数,可以实现验证消息的国际化。
import { IsString, IsNotEmpty, Length, ValidationArguments } from 'class-validator';
import { i18n } from 'i18next'; // 假设在项目中使用 i18nexport 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 进行数据验证,大家都在项目中实践起来吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/9234.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

GenAI 用于客户支持 — 第 5 部分:可观察性

作者:来自 Elastic Andy James 本系列将带你深入了解我们如何在客户支持中使用生成式人工智能。加入我们,实时分享我们的历程,本篇文章重点介绍支持助理的可观察性。 本博客系列揭示了我们的现场工程团队如何使用 Elastic stack 和生成式 AI …

python安装selenium,geckodriver,chromedriver,Selenium IDE

安装浏览器 找到浏览器的版本号 chrome 版本 130.0.6723.92(正式版本) (64 位) firfox 116.0.3 (64 位),但是后面运行的时候又自动更新到了 127.0.0.8923 安装selenium > pip install selenium > pip show …

Docker部署SpringBoot项目(镜像部署)

目录 一、在pom.xml 文件中加入依赖 1.依赖内容 2.依赖说明和解释 3.使用流程 4.示例 5.注意 二、执行打包 1.使用命令打包 2.使用IDEA提供快捷方式 三、将jar包上传到服务器 四、创建相关配置 1.创建一个Dockerfile文件 2.添加配置 3.举例 五、生成Docker镜像 1.…

WPF+MVVM案例实战与特效(二十五)- 3D粒子波浪效果实现

文章目录 1、案例效果2、案例实现1、文件创建2. 功能代码实现3、粒子功能应用1、前端布局与样式2、代码解释2、 后端功能代码1、案例效果 2、案例实现 1、文件创建 打开 Wpf_Examples 项目、Models 文件夹下创建 3D粒子模型类 ParticleWaveEffectModel.cs 文件。在Tools 文件…

设计模式之建造者模式(各项装修物料组合套餐选配场景)

前言: 乱码七糟,我时常怀疑这个成语是来形容程序猿的! 无论承接什么样的需求,是不是身边总有那么几个人代码写的烂,但是却时常有测试小姐姐过来聊天(求改bug)、有产品小伙伴送吃的(求写需求)、有业务小妹妹陪着改代码(…

ffmpeg视频滤镜:组合两个视频为立体视频- framepack

视频描述 framepack 官方网址 > FFmpeg Filters Documentation 这个滤镜会将两个视频进行组合,有个前提是这两个视频的帧率、分别率必须一样。比如输入的是两个852x480 视频,输出可能是1704*480(左右拼接)、852*960&#xf…

【K8S问题系列 | 8】K8S集群资源突然爆满导致 Pod 状态变为 Pending 详细解决方案

在 Kubernetes 集群中,当 CPU 突然爆满时,Pod 可能无法获得所需的资源,从而导致其状态变为 Pending。以下是更详细的解决方案描述,有效应对这一问题。 解决方案 1: 扩展集群资源 描述 当集群资源不足以支撑当前的工作负载时&…

第18篇 :深入剖析systemverilog中 randomize 失败案例启示录(一)

经过前面章节的理论学习,我们对systemverilog中的随机约束,有一定的了解,那么,今天开始,着重讲述一些工作中遇到的困惑。主要通过一些例子,层层递进,举一反三,源于实践,剖…

mac端mumu模拟器adb识别不了问题

1.在终端中输入:system_profiler SPUSBDataType,把0x05e3 (Genesys Logic, Inc.)复制 2. 1.cd ~/.android/ 2.open . 3.找到.android/adb_usb.ini文件 将以上格式的Wendor ID放入该文件 3.依次执行 * adb devices* adb kill-server* adb start-server* adb disco…

Ubuntu版本、ROS版本与Python 版本之间的关系

引言 在机器人开发中,ROS(机器人操作系统)广泛应用于科研和工业领域,支持多个Ubuntu和Python版本。然而,随着不同Ubuntu LTS版本的发布以及Python逐渐从2.x向3.x过渡,ROS的版本选择和兼容性要求也在不断变化…

Linux - 信号

文章目录 一、信号的定义二、查看信号三、产生信号1、指令2、系统调用3、由软件条件产生信号4、异常5、键盘输入 四、保存信号1、补充:信号其他相关概念2、信号保存在哪,怎么保存?3、信号集操作函数 五、捕获信号1、概念2、捕获信号的时机3、…

PMP–知识卡片--项目干系人

项目干系人主要分为两类:参与项目的人和受项目影响的人。按照由近及远,从项目经理、项目团队等逐渐扩充至供应商、客户、监管机构等。 项目往往死在被忽略的干系人手上,作为项目经理,要尽可能地识别出来所有可能影响项目以及受项目…

MATLAB - ROS 2 分析器

系列文章目录 前言 本主题介绍如何连接 ROS 2 网络,分析网络图中所有元素的基本信息(如节点名称和节点之间的信息),以及可视化与 ROS 2 节点相关的参数(如主题、服务和操作)之间的交互。 一、连接并查看 RO…

分组校验在Spring中的应用详解

目录 前言1. 什么是分组校验2. 分组校验的基本原理3. 分组校验的实现步骤3.1 定义分组接口3.2 在校验项中指定分组3.3 校验时指定要校验的分组3.4 默认分组和分组的继承 4. 分组校验的优势和适用场景4.1 优势4.2 适用场景 5. 常见问题与解决方案5.1 校验未生效5.2 无法识别默认…

SDL打开YUV视频

文章目录 问题1:如何控制帧率?问题2:如何触发退出事件?问题3:如何实时调整视频窗口的大小问题4:YUV如何一次读取一帧的数据? 问题1:如何控制帧率? 单独用一个子线程给主线…

[MySQL]索引

索引介绍 索引是帮助数据库高效获取数据的数据结构。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用数据, 这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。 假设我们有…

window 利用Putty免密登录远程服务器

1 在本地电脑用putty-gen生成密钥 参考1 参考2 2 服务器端操作 将公钥上传至Linux服务器。 复制上述公钥到服务器端的authorized_keys文件 mkdir ~/.ssh vi ~/.ssh/authorized_keys在vi编辑器中,按下ShiftInsert键或者右键选择粘贴,即可将剪贴板中的文…

【大数据技术基础 | 实验八】HBase实验:新建HBase表

文章目录 一、实验目的二、实验要求三、实验原理四、实验环境五、实验内容和步骤(一)启动HBase集群(二)编写项目java代码(三)将代码导出jar包 六、实验结果七、实验心得 一、实验目的 掌握HBase数据模型(逻…

密钥管理服务 (KMS) 故障排除指南

企业客户将密钥管理服务 (KMS) 设置为部署流程的一部分,因为通过该服务,他们可以使用简单、直接的过程在其环境中激活 Windows。 通常,一旦设置了 KMS 主机,KMS 客户端就会自动连接到主机并自行激活。 然而,有时该流程…

CSS的配色

目录 1 十六进制2 CSS中的十六进制2.1 十六进制颜色的基本结构2.2 十六进制颜色的范围2.3 简写形式2.4 透明度 3 CSS的命名颜色4 配色4.1 色轮4.2 互补色4.3 类似色4.4 配色工具 日常在开发小程序中,客户总是希望你的配色是美的,但是美如何定义&#xff…