《Techporters架构搭建》-Day05 属性校验
属性校验
- 前言
- @Validated基础用法
- 集合校验
- 分组校验
- 嵌套校验
- 自定义校验器
源码地址
前言
在项目开发过程中,经常遇到需要对传递的参数进行校验,比如某个参数字段是否为空、值的取值是否在约定范围、格式是否合法等等,最原始的写法,通过if判断
@PostMappingpublic Result<Long> save(@RequestBody SystemUserDto systemUserDto) {if(systemUserDto.getUserName!=null){throw new BusinessException("用户名称不能为空");}if(systemUserDto.getMobile!=null){throw new BusinessException("用户电话不能为空");}if(systemUserDto.getMobile().length()>11){throw new BusinessException("用户电话长度不能超过11位");}return Result.ok(id);}
还有通过Spring框架提供的Assert类,它能够帮助我们确保方法参数符合预期,如果不符合会抛出IllegalArgumentException,然后通过全局异常捕获将错误按照统一格式返给前端。
示例:校验字符串非空
import org.springframework.util.Assert;public void checkString(String input) {// 校验字符串非空,如果为空则抛出异常Assert.hasText(input, "输入字符串不能为空");
}
常用的一些方法
Assert.notNull(Object object,"object is required"); // 对象非空
Assert.isTrue(Object object,"object must be true"); // 对象必须为true
Assert.notEmpty(Collection collection,"collection must not be empty"); // 集合不能为空
Assert.hasLength(String text,"text must be specified"); // 字符不为null且字符长度不为0
Assert.hasText(String text,"text must not be empty"); // text不为null且必须至少包含一个非空的字符
Assert.isInstanceOf(Class class, Object object,"class must be of type[class]"); // object必须为class指定的类
最后一种就是使用@Valid 注解和 @Validated 注解,首先我们要了解这两个注解的区别,然后着重描述一下@Validated 用法
1.来源不同
@Validated:是Spring框架特有的注解,属于Spring的一部分,也是JSR 303的一个变种。它提供了一些 @Valid
所没有的额外功能,比如分组验证。@Valid:Java EE提供的标准注解,它是JSR
303规范的一部分,主要用于Hibernate Validation等场景。
2.注解位置
@Validated: 用在类、方法和方法参数上,但不能用于成员属性。@Valid:可以用在方法、构造函数、方法参数和成员属性上。
3.是否支持分组
@Validated:支持分组验证,可以更细致地控制验证过程。此外,由于它是Spring专有的,因此可以更好地与Spring的其他功能(如Spring的依赖注入)集成。@Valid:主要支持标准的Bean验证功能,不支持分组验证。
4.嵌套验证
@Validated:不支持嵌套验证。@Valid:支持嵌套验证,可以嵌套验证对象内部的属性。
@Validated基础用法
① 在根目录build.gradle引入校验的依赖包
springValidationVersion = '3.3.2'
//Validation参数校验
implementation "org.springframework.boot:spring-boot-starter-validation:${springValidationVersion}"
② 在对象需要校验属性上添加注解,比如我要验证账号不能为空
@NotBlank(message = "用户账号不能为空")
private String username;
其它类型的注解
| 注解 | 验证的数据类型 | 描述 |
|---|---|---|
| @NotNull | 任意类型 | 验证属性不能为null |
| @NotBlank | 字符串 | 验证字符串属性不能为空且长度必须大于0 |
| @Size(min,max ) | CharSequence Collection Map Array | 字符串:字符串长度必须在指定的范围内 Collection:集合大小必须在指定的范围内 Map:map的大小必须在指定的范围内 Array:数组长度必须在指定的范围内 |
| @Min | 整型类型 | 验证数字属性的最小值 |
| @Max | 整型类型 | 验证数字属性的最大值 |
| @DecimalMin | 数字类型 | 验证数字属性的最小值(包括小数) |
| @DecimalMax | 数字类型 | 验证数字属性的最大值(包括小数) |
| @Digits(integer,fraction) | 数字类型 验证数字属性的整数位数和小数位数 | |
| 字符串类型 | 验证字符串属性是否符合Email格式 | |
| @Pattern | 字符串 | 验证字符串属性是否符合指定的正则表达式 |
| @Positive | 数字类型 | 验证数值为正数 |
| @PositiveOrZero | 数字类型 | 验证数值为正数或0 |
| @Negative | 数字类型 | 验证数值为负数 |
| @NegativeOrZero | 数字类型 | 验证数值为负数或0 |
| @AssertTrue | 布尔类型 | 参数值必须为 true |
| @AssertFalse | 布尔类型 | 参数值必须为 false |
| @Past | 时间类型(Date) | 参数值为时间,且必须小于 当前时间 |
| @PastOrPresent | 时间类型(Date) | 参数值为时间,且必须小于或等于 当前时间 |
| @Future | 时间类型(Date) | 参数值为时间,且必须大于 当前时间 |
| @FutureOrPresent | 时间类型(Date) | 参数值为时间,且必须大于或等于 当前日期 |
③ 在接口校验的对象前面添加上 @Validated 注解
@PostMapping
public Result<Long> save(@RequestBody @Validated SystemUserDto systemUserDto) {Long id=systemUserService.createUser(systemUserDto);return Result.ok(id);
}@PutMapping
public Result<Boolean> update(@RequestBody @Validated SystemUserDto systemUserDto) {systemUserService.updateUser(systemUserDto);return Result.ok(true);
}
④通过Apifox测试,可以看出返回的值并不能看出问题,因此下一步结构化一下异常显示

后端报错
Resolved [org.springframework.web.bind.MethodArgumentNotValidException:Validation failed for argument [0] in public com.tps.cloud.response.Result<java.lang.Long>com.tps.cloud.system.controller.SystemUserController.save(com.tps.cloud.system.dto.SystemUserDto): [Field error in object 'systemUserDto' on field 'username': rejected value []; codes [NotBlank.systemUserDto.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [systemUserDto.username,username]; arguments []; default message [username]]; default message [用户账号不能为空]] ]
⑤ 给前端返回结构化错误提示,需要配置全局异常捕获类GlobalExceptionHandler中添加捕获MethodArgumentNotValidException的方法
/*** validation Exception* @param exception* @return Result
*/
@ExceptionHandler({ MethodArgumentNotValidException.class })
public Result handleBodyValidException(MethodArgumentNotValidException exception) {FieldError fieldError = exception.getBindingResult().getFieldError();return Result.failed(String.format("%s", fieldError.getDefaultMessage()));
}
通过org.springframework.boot.autoconfigure.AutoConfiguration.imports完成自动配置注册

⑥ 通过Apifox测试,可以看出返回的值已经结构化

集合校验
分组校验
分组验证是为了在不同的验证场景下能够对对象的属性进行灵活地验证,从而提高验证的精细度和适用性。一般我们在对同一个对象进行保存或修改时,会使用同一个类作为入参。那么在创建时需要校验某个字段,但是更新的时候不需要校验,这个时候就需要用到分组校验了。
对于定义分组有两点要特别注意:
- 定义分组必须使用接口
- 要校验字段上必须加上分组,分组只对指定分组生效,不加分组不校验
① 创建分组
用于创建时指定分组:
package com.tps.cloud.group;public interface AddGroup {}
用于更新时指定分组:
package com.tps.cloud.group;public interface UpdateGroup {}
② 在实体类上添加注解,我们只添加了AddGroup.class 分组
/**
* 用户账号
*/
@NotBlank(message = "用户账号不能为空",groups = {AddGroup.class})
private String username;
/**
* 密码
*/
@NotBlank(message = "密码不能为空",groups = {AddGroup.class})
private String password;
③ 在新增接口上添加分组,更新不添加分组,通过Apifox测试
@PostMapping
public Result<Long> save(@RequestBody @Validated({AddGroup.class}) SystemUserDto systemUserDto) {Long id=systemUserService.createUser(systemUserDto);return Result.ok(id);
}@PutMapping
public Result<Boolean> update(@RequestBody @Validated SystemUserDto systemUserDto) {systemUserService.updateUser(systemUserDto);return Result.ok(true);
}


④在实体类以及对应接口上添加UpdateGroup.class分组,通过Apifox测试
/*** 用户账号*/@NotBlank(message = "用户账号不能为空",groups = {AddGroup.class,UpdateGroup.class})private String username;/*** 密码*/@NotBlank(message = "密码不能为空",groups = {AddGroup.class,UpdateGroup.class})private String password;
@PutMappingpublic Result<Boolean> update(@RequestBody @Validated({UpdateGroup.class}) SystemUserDto systemUserDto) {systemUserService.updateUser(systemUserDto);return Result.ok(true);}

嵌套校验
嵌套校验(Nested Validation) 指的是在验证对象时,对对象内部包含的其他对象进行递归验证的过程。当一个对象中包含另一个对象作为属性,并且需要对这个被包含的对象也进行验证时,就需要进行嵌套校验。
嵌套属性指的是在一个对象中包含另一个对象作为其属性的情况。换句话说,当一个对象的属性本身又是一个对象,那么这些被包含的对象就可以称为嵌套属性。
我们继续以保存用户接口为例:
① 创建SystemDeptDto类,并添加验证注解,现在把SystemDeptDto作为SystemUserDto的嵌套属性
package com.tps.cloud.system.dto;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.tps.cloud.entity.TenantEntity;
import com.tps.cloud.group.AddGroup;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;/*** 部门传输类*/
@Data
public class SystemDeptDto{/*** 部门id*/@TableIdprivate Long id;/*** 部门名称*/@NotBlank(message = "部门名称不能为空",groups = {AddGroup.class})private String name;/*** 父部门id*/private Long parentId;/*** 显示顺序*/private Integer sort;/*** 负责人id*/private Long leaderUserId;/*** 联系电话*/private String phone;/*** 邮箱*/private String email;/*** 部门状态(0正常 1停用)*/@Validprivate Integer status;
}
public class SystemUserDto {...省略前面属性/*** 部门*/private SystemDeptDto systemDept;
}
@PostMapping
public Result<Long> save(@RequestBody @Validated SystemUserDto systemUserDto) {Long id=systemUserService.createUser(systemUserDto);return Result.ok(id);
}
② 测试

自定义校验器
有时,内置的校验器无法满足您的需求。例如,您可能需要验证用户名是否唯一,这需要访问数据库。在这种情况下,您可以定义自己的校验器。
要定义自定义校验器,请创建一个实现 javax.validation.ConstraintValidator 接口的类。在下面的示例中,我们将创建一个用于验证用户名是否唯一的校验器:
① 创建校验器UniqueUsernameValidator,ConstraintValidator包含以下两种方法:
- 初始化方法 initialize:这个方法在验证器的生命周期中仅被调用一次。它传递了与验证器关联的注解实例,允许验证器从注解实例中提取和存储配置详情。
- 验证方法isValid: 这是实现验证逻辑的地方。这个方法对于每个要验证的值都会被调用,并返回一个布尔值,表示数据是否符合约束条件。
package com.tps.cloud.system.constraint;import com.tps.cloud.system.service.SystemUserService;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import lombok.AllArgsConstructor;@AllArgsConstructor
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {private final SystemUserService systemUserService;@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (value == null) {return true;}return systemUserService.findByUsername(value) == null;}
}
② 创建一个自定义注解UniqueUsername,以便在代码中使用
package com.tps.cloud.system.constraint;import jakarta.validation.Constraint;
import jakarta.validation.Payload;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {UniqueUsernameValidator.class})
public @interface UniqueUsername {String message() default "用户名已存在";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
② 在用户账号属性上添加改注解
public class SystemUserDto {.../*** 用户账号*/@NotBlank(message = "用户账号不能为空",groups = {AddGroup.class,UpdateGroup.class})@UniqueUsernameprivate String username;...
}
③ 测试接口

相关文章:
《Techporters架构搭建》-Day05 属性校验
属性校验 前言Validated基础用法集合校验分组校验嵌套校验自定义校验器 源码地址 前言 在项目开发过程中,经常遇到需要对传递的参数进行校验,比如某个参数字段是否为空、值的取值是否在约定范围、格式是否合法等等,最原始的写法,…...
HTTP的场景实践
HTTP的场景实践:任选一个浏览器,对于其涉及的请求中的缓存策略展开具体分析 1. 强缓存: Cache-Control用于指定缓存的最长有效时间。 Expires用于指定资源过期的日期。 2. 协商缓存: ETag用于标识资源的唯一标识符,…...
MySQL:表的设计原则和聚合函数
所属专栏:MySQL学习 💎1. 表的设计原则 1. 从需求中找到类,类对应到数据库中的实体,实体在数据库中表现为一张一张的表,类中的属性对应着表中的字段 2. 确定类与类的对应关系 3. 使用SQL去创建具体的表 范式࿱…...
介绍springmvc-水文
Spring MVC 是一个基于 Java 的开源 Web 框架,它是 Spring Framework 的一部分。Spring MVC 提供了一个架构,用于开发灵活、可扩展的 Web 应用程序。 Spring MVC 的主要特点包括: 基于模型-视图-控制器(MVC)的架构&am…...
uni-app学习笔记
一、下载HBuilder https://www.dcloud.io/hbuilderx.html 上述网址下载对应版本,下载完成后进行解压,不需要安装,解压完成后,点击HBuilder X.exe文件进行运行程序 二、创建uni-app项目 此处我是按照文档创建的uni-ui项目模板…...
Windows Server修改远程桌面端口
新建入站规则 填写端口 允许连接 修改远程桌面端口 winR打开注册表 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp修改PortNumber为新端口 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wi…...
界面组件Kendo UI for Vue 2024 Q2亮点 - 发布一系列新组件
随着最新的2024年第二季度发布,Kendo UI for Vue为应用程序开发设定了标准,包括生成式AI集成、增强的设计系统功能和可访问的数据可视化。新的2024年第二季度版本为应用程序界面提供了人工智能(AI)提示,从设计到代码的生产力增强、可访问性改…...
达梦数据库 逻辑备份还原
达梦的逻辑备份还原 1.背景2.要求3.实验步骤3.1 相关术语3.2 dexp逻辑导出3.2.1 使用dexp工具3.2.2 dexp相关参数含义3.2.3 四种级别导出3.2.3.1 FULL3.2.3.2 OWNER3.2.3.3 SCHEMAS3.2.3.4 TABLES 3.2.4 使用范例3.2.4.1 环境准备3.2.4.2 dexp逻辑导出 3.3 dimp逻辑导入3.3.1 使…...
Stable Diffusion绘画 | 图生图-上传重绘蒙版
上传重绘蒙版,可以弥补局部重绘的缺点,能够更精细的修改画面中的指定区域 使用PS制作的蒙版图片为耳朵下方区域,可以为图片中的女生带上不同款式的耳环。 参数配置: 调整提示词: 生成图片如下所示: 调整提…...
打开Office(word、excel、ppt)显示操作系统当前的配置不能运行此应用程序最全解决方案!
我以前用过分区助手把office从c盘挪到d盘了,从那以后office就用不了了,然后我就删了(貌似没删干净)。 最近由于有使用word的需求,所以我从学校官网找到正版软件的安装包,按照步骤重新卸载电脑中office残留…...
猫头虎 分享已解决Bug || TypeError: Cannot read property ‘map‘ of undefined 解决方案
🐯 猫头虎 分享已解决Bug || TypeError: Cannot read property map of undefined 解决方案 摘要: 今天猫头虎带大家深入探讨在前端开发中常见的一个令人头疼的问题:TypeError: Cannot read property map of undefined。这个错误通常出现在我…...
大模型快速部署,以浪潮源2.0为例
step1: 申请PAI-DSW试用 step2:魔塔社区授权 由于本地授权一直失败,于是采用了魔塔免费平台实例进行学习。 搭建好之后,打开就有相关页面了: demo搭建: 按照官方提示的步骤进行搭建,内容如下:…...
Python知识点:使用FastAI进行快速深度学习模型构建
使用FastAI构建深度学习模型非常方便,尤其是对于快速原型开发和实验。以下是一个使用FastAI构建深度学习模型的完整示例,涵盖数据准备、模型训练和评估。 安装依赖 首先,确保你安装了FastAI库和其他必要的库: pip install fast…...
Nginx配置全局https
Nginx配置全局https 要在 Nginx 中配置将 HTTP (80 端口) 请求重定向到 HTTPS (443 端口),可以在 Nginx 的配置文件中添加以下配置。假设你已经配置好了 HTTPS 相关的证书和密钥。 打开你的 Nginx 配置文件,通常是 /etc/nginx/nginx.conf。 在配置文件…...
DBAPI 如何对SQL查询出的日期字段进行统一格式转换
DBAPI 如何对SQL查询出的日期字段进行统一格式转换 mysql有一张订单表,有两个datetime类型的字段create_time update_time 新建一个API,SQL内容是查询所有数据 访问API发现日期字段默认返回时间戳格式 如果修改成自己想要的年月日格式,就要使…...
C:每日一题:字符串左旋
题目:实现一个函数,可以实现字符串的左旋 例如:ABCD左旋一个字符就是BCDA;ABCD左旋两个字符就是CDAB; 1、解题思路: 1.确定目标旋转k个字符,我们要获取字符串的长度 len,目的是根…...
深兰科技荣获2024年度金势奖“AI出海先锋品牌”金奖
近日,由金势奖组委会、凤凰网、营销国际协会等国内外知名机构、集团共同主办的“第四届未来营销大会暨锐品牌盛典”在上海举行。大会揭晓了第四届“金势奖锐品牌大赏”奖项的评选结果,深兰科技凭借自身在机器人产品出口和海外市场开拓等出海全球化发展方…...
服务器启动jar包的时候报”no main manifest attribute“异常(快捷解决)
所以,哥们,又出现问题咯.没事,我也出现了,哈哈哈哈哈,csdn感觉太麻烦了,所以搞了一篇这个. 没得事,往下看,包解决的. 希望可以帮助到各位,感谢阅览! 小手点个赞,作者会乐烂哈哈哈哈哈哈😆😆😆😆…...
部分控件的setText文案没有出现在retranslateUi()中,多语言切换不生效问题
问题:在designer中设计UI,我从其他ui文件copy了部分控件,新ui文件重新编译生成后,setText()并没有出现在新文件的retranslateUi()函数中,导致多语言切换不生效。 void retranslateUi(QWidget * …...
ubuntu系统下安装LNMP集成环境的详细步骤(保姆级教程)
php开发中集成环境的安装是必不可少的技能,而LNMP代表的是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构。今天就给大家分享下LNMP的安装步骤。 1 Nginx安装 在安装Nginx前先执行下更新命令: sudo apt-get update 接下来开始安装Nginx, 提示:Could not get lock /v…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
