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.mzph.cn/news/885375.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis Plus 集成 PgSQL 指南

“哲学家们只是用不同的方式解释世界,而问题在于改变世界。” ——卡尔马克思 (Karl Marx) 解读:马克思强调了实践的重要性,主张哲学不仅要理解世界,更要致力于改造世界。 本文我们引入 Mybatis Plus 作为 ORM ,并且使…

苍穹外卖day09超出配送范围前端不提示问题

同学们在写苍穹外卖项目day09时调用了百度地图api来判断用户地址是否超出配送范围, 但是在黑马官方的课程或资料中,出现这样的问题时只会向用户端的控制台报错并不会提醒用户 如下图: 解决方法: 其实解决方法很简单只需要找到向…

【Linux系列】命令行中的文本处理:从中划线到下划线与大写转换

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件

文章目录 一、MyBatis介绍1、MyBatis简介2、MyBatis特性3、和其它持久化层技术对比 二、搭建MyBatis基本步骤1、创建Maven工程2、添加log4j2的配置文件3、创建MyBatis的核心配置文件4、创建Mapper接口5、创建MyBatis映射文件(增删改查)6、创建Junit测试功…

推动企业数字化转型的三驾马车:DataOps与DevOps、MLOps的关系及其重要性

在当今快速发展的技术领域,DevOps、DataOps和MLOps成为了推动企业数字化转型的三大核心实践。它们各自关注不同的领域,但又相互关联,共同推动着软件和数据的高效开发与运营。 DevOps:软件开发的加速器 DevOps是一种将开发&#…

下载 AndroidStudio 旧版本方法

1.打开官网: 点击Read release notes 然后就是各个历史版本了: 直接点链接好像也行:https://developer.android.com/studio/archive

ONLYOFFICE 8.2深度测评:集成PDF编辑、数据可视化与AI功能的强大办公套件

本文 一、文档编辑与PDF支持主要功能概述 二、数据可视化和增强的表格工具数据可视化功能亮点 三、AI驱动的摘要功能AI摘要功能优势 四、演示文稿的增强功能主要更新 五、协同办公能力的提升协同功能更新 六、跨平台兼容与开放文档格式跨平台与兼容性 七、安全性与隐私保护安全…

彻底理解ARXML中的PDU

文章目录 一、DBC报文信号的发送二、ARXML报文信号的发送2.1 什么是PDU2.2 PDU的类型2.3 Container-I-PDU的发送 三、小结 在CANFD支持可变速率和更大的数据长度(64字节)的情况下,可以使用DBC和ARXML两种数据库格式来进行报文通信&#xff0c…

el-scrollbar 动态更新内容 鼠标滚轮无效

有以下功能逻辑,实现了一个时间轴组件,点击、-号后像地图那样放大组件以显示不同的UI。 默认显示年月: 当点击一下加号时切换为年: 当点击减号时切换为日: 即加号、减号点击就是在年月日显示进行切换。给Scrollvie…

LED点阵显示(Proteus 与Keil uVision联合仿真)(点阵字模提取)

点阵字模提取: https://pan.baidu.com/s/1DZSeLyD_SUkaHRgTm26o-A 提取码: 1111 一、LED点阵显示器结构 点亮点阵中一个发光二极管条件:对应行为高电平,对应列为低电平。如在很短时间内依次点亮很多个发光二极管,LED点阵就可显示…

.NET中通过C#实现Excel与DataTable的数据互转

在.NET框架中,使用C#进行Excel数据与DataTable之间的转换是数据分析、报表生成、数据迁移等操作中的常见需求。这一过程涉及到将Excel文件中的数据读取并加载至DataTable中,以便于利用.NET提供的丰富数据处理功能进行操作,同时也包括将DataTa…

「Mac畅玩鸿蒙与硬件29」UI互动应用篇6 - 多选问卷小应用

本篇将带你实现一个多选问卷小应用,用户可以勾选选项并点击提交按钮查看选择的结果。通过本教程,你将学习如何使用 Checkbox 组件、动态渲染列表、状态管理及用户交互,构建完整的应用程序。 关键词 UI互动应用Checkbox 组件状态管理动态列表…

Java 并发编程学习笔记

参考资料: JAVA并发专题 - 终有救赎的专栏 - 掘金 Java并发编程学习路线(建议收藏��) | Java程序员进阶之路x沉默王二 面试题目: JUC第一讲:Java并发知识体系详解 面试题汇总(P6熟练 P7精通…

【Linux系统】—— 基本指令(二)

【Linux系统】—— 基本指令(二) 1 「alias」命令1.1 「ll」命令1.2 「alias」命令 2 「rmdir」指令与「rm」指令2.1 「rmdir」2.2 「rm」2.2.1 「rm」 删除普通文件2.2.2 「rm」 删除目录2.2.3 『 * 』 通配符 3 「man」 指令4 「cp」 指令4.1 拷贝普通…

Pytorch cuda版本选择(高效简洁版)

简而言之 Pytorch cuda版本选择 只需要低于cuda驱动版本即可,cuda驱动版本查看命令是nvidia-smi, nvcc -V 是runtimeapi版本可以不用管 1.只要看cuda驱动版本 安装pytorch 选择cuda版本,只要看你电脑cuda驱动版本即可。 2.选择依据 pytorch中cuda版本只…

Flink API 的层次结构

Apache Flink 提供了多层 API,每层 API 针对不同的抽象层次和用途,使得开发者可以根据具体需求选择合适的 API 层次。以下是 Flink API 的层次结构及其简要说明:

GPU集群上分布式训练大模型

总结一下如何在超算系统上进行预训练大模型的分布式训练 / 微调,文中代码已上传至 github 实验环境 集群1:国家广州超算 星逸A800智能AI集群 GPU:8 * Nvdia Tesla-A800 80G显存 CPU:2 * 28核 Intel Xeon Gold 6348 内存&#xff…

python爬虫自动库DrissionPage保存网页快照mhtml/pdf/全局截图/打印机另存pdf

目录 零一、保存网页快照的三种方法二、利用打印机保存pdf的方法 零 最近星球有人问如何使用页面打印功能,另存为pdf 一、保存网页快照的三种方法 解决方案已经放在星球内:https://articles.zsxq.com/id_55mr53xahr9a.html当然也可以看如下代码&…

Redis 中 Bitmap 原理和应用

Bitmap Redis中的Bitmap(位图)是一种较为特殊数据类型,它以最小单位bit来存储数据,我们知道一个字节由 8个 bit 组成,和传统数据结构用字节存储相比,这使得它在处理大量二值状态(true、false 或…

elementUI 点击弹出时间 date-picker

elementUI的日期组件,有完整的UI样式及弹窗,但是我的页面不要它的UI样式,点击的时候却要弹出类似的日期选择器,那怎么办呢? 以下是elementUI自带的UI风格,一定要一个输入框来触发。 这是我的项目中要用到的…