更简洁的参数校验,使用 SpringBoot Validation 对参数进行校验

news2025/8/8 4:17:17

在开发接口时,如果要对参数进行校验,你会怎么写?编写 if-else 吗?虽然也能达到效果,但是不够优雅。

今天,推荐一种更简洁的写法,使用 SpringBoot Validation 对方法参数进行校验,特别是在编写 Controller 层的方法时,直接使用一个注解即可完成参数校验。

示例代码:spring-validation-demo: SpringBootValidation Demo (gitee.com)

🚀引入依赖

想要完成上述所说的参数校验,我们需要一个核心依赖:spring-boot-starter-validation,此外,为了方便演示,还需要其他依赖。

依赖如下:

 <dependencies>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-validation</artifactId>
     </dependency>
     <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <optional>true</optional>
     </dependency>
   </dependencies>
复制代码

💡 以下部分不是核心内容:

你在编写下面的示例代码中,会发现主要使用到了javax.validation.constraints 包下的注解,而这个包主要来自于 jakarta.validation-api 这个依赖。

如果引入依赖的时候直接引入 jakarta.validation-api 是无法实现参数校验功能的,因为它只定义了规范,而没有具体实现。但是 hibernate-validator 实现了这个规范,直接引入 hibernate-validator 也是可以实现参数校验功能的。

 <!--无法完成功能-->
 <dependency>
     <groupId>jakarta.validation</groupId>
     <artifactId>jakarta.validation-api</artifactId>
 </dependency>
 <!--可以完成参数校验功能-->
 <!--spring-boot-starter-validation 引入了这个依赖-->
 <dependency>
     <groupId>org.hibernate.validator</groupId>
     <artifactId>hibernate-validator</artifactId>
 </dependency>
复制代码

🚀 相关注解说明

这里罗列出一些主要的注解,这些注解主要来自于包 javax.validation.constraints,有兴趣查看源码的可以去这个包下查看。

可以先跳过这部分内容,下面的代码如果遇到不清楚作用的注解再回来查阅。

✈ 空值检查

注解说明
@NotBlank用于字符串,字符串不能为null 也不能为空字符串
@NotEmpty字符串同上,对于集合(Map,List,Set)不能为空,必须有元素
@NotNull不能为 null
@Null必须为 null

✈ 数值检查

注解说明
@DecimalMax(value)被注释的元素必须为数字,其值必须小于等于指定的值
@DecimalMin(value)被注释的元素必须为数字,其值必须大于等于指定的值
@Digits(integer, fraction)被注释的元素必须为数字,其值的整数部分精度为 integer,小数部分精度为 fraction
@Positive被注释的元素必须为正数
@PositiveOrZero被注释的元素必须为正数或 0
@Max(value)被注释的元素必须小于等于指定的值
@Min(value)被注释的元素必须大于等于指定的值
@Negative被注释的元素必须为负数
@NegativeOrZero被注释的元素必须为负数或 0

✈ Boolean 检查

注解说明
@AssertFalse被注释的元素必须值为 false
@AssertTrue被注释的元素必须值为 true

✈ 长度检查

注解说明
@Size(min,max)被注释的元素长度必须在 minmax 之间,可以是 String、Collection、Map、数组

✈ 日期检查

注解说明
@Future被注释的元素必须是一个将来的日期
@FutureOrPresent被注释的元素必须是现在或者将来的日期
@Past被注释的元素必须是一个过去的日期
@PastOrPresent被注释的元素必须是现在或者过去的日期

✈ 其他检查

注解说明
@Email被注释的元素必须是电子邮箱地址
@Pattern(regexp)被注释的元素必须符合正则表达式

除此之外,org.hibernate.validator.constraints 包下还有其他校验注解,例如 @ISBN 检查一个字符串是否是一个有效地 ISBN 序列号。

🚀 参数校验

接下来开始体验 Spring Boot Validation。

首先,编写一个需要校验的实体类:

 @Data
 public class Student {
     @NotBlank(message = "主键不能为空")
     private String id;
     @NotBlank(message = "名字不能为空")
     @Size(min=2, max = 4, message = "名字字符长度必须为 2~4个")
     private String name;
     @Pattern(regexp = "^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}$", message = "手机号格式错误")
     private String phone;
     @Email(message = "邮箱格式错误")
     private String email;
     @Past(message = "生日必须早于当前时间")
     private Date birth;
     @Min(value = 0, message = "年龄必须为 0~100")
     @Max(value = 100, message = "年龄必须为 0~100")
     private Integer age;
     @PositiveOrZero
     private Double score;
 }
复制代码

随后编写一个控制层代码,进行测试:

 @RestController
 public class TestController {
 ​
     @GetMapping("/test")
     public Student test(@RequestBody @Validated Student student) {
         return student;
     }
 }
复制代码

使用 postman 进行测试,发送一个不带参数的请求,查看结果:

💡后端控制台日志打印是这样的(显示极度不友好),可以看到校验规则生效了:

 2022-11-23 22:10:13.249  WARN 19840 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.example.springvalidationdemo.domain.Student com.example.springvalidationdemo.controller.TestController.test(com.example.springvalidationdemo.domain.Student) with 2 errors: [Field error in object 'student' on field 'name': rejected value [null]; codes [NotBlank.student.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.name,name]; arguments []; default message [name]]; default message [名字不能为空]] [Field error in object 'student' on field 'id': rejected value [null]; codes [NotBlank.student.id,NotBlank.id,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.id,id]; arguments []; default message [id]]; default message [主键不能为空]] ]
复制代码

🚀 全局异常处理

查看上面的日志打印,可以看到当参数校验不通过时,会抛出异常 MethodArgumentNotValidException,同时也会打印那些参数没有通过校验,以及该参数校验规则

为了方便查看,我们可以编写一个全局异常处理,处理这个参数校验异常,并使用统一返回实体返回给前端。

 @ControllerAdvice
 @Slf4j
 public class GlobalExceptionHandler {
 ​
     @ExceptionHandler(MethodArgumentNotValidException.class)
     @ResponseBody
     public ResponseEntity<Object> exception(MethodArgumentNotValidException e, HttpServletRequest request) {
         Map<String, String> result = new HashMap<>();
         BindingResult bindingResult = e.getBindingResult();
         log.error("请求[ {} ] {} 的参数校验发生错误", request.getMethod(), request.getRequestURL());
         for (ObjectError objectError : bindingResult.getAllErrors()) {
             FieldError fieldError = (FieldError) objectError;
             log.error("参数 {} = {} 校验错误:{}", fieldError.getField(), fieldError.getRejectedValue(), fieldError.getDefaultMessage());
             result.put(fieldError.getField(), fieldError.getDefaultMessage());
         }
         // 一般项目都会有自己定义的公共返回实体类,这里直接使用现成的 ResponseEntity 进行返回,同时设置 Http 状态码为 400
         return ResponseEntity.badRequest().body(result);
 ​
     }
 ​
 }
复制代码

再次使用 postman 发起测试:

控制台打印出自定义的日志信息:

 2022-11-23 22:16:37.800 ERROR 19880 --- [nio-8080-exec-2] c.e.s.handler.GlobalExceptionHandler     : 请求[ GET ] http://localhost:8080/test 的参数校验发生错误
 2022-11-23 22:16:37.800 ERROR 19880 --- [nio-8080-exec-2] c.e.s.handler.GlobalExceptionHandler     : 参数 name = null 校验错误:名字不能为空
 2022-11-23 22:16:37.800 ERROR 19880 --- [nio-8080-exec-2] c.e.s.handler.GlobalExceptionHandler     : 参数 id = null 校验错误:主键不能为空
 2022-11-23 22:19:36.594 ERROR 19880 --- [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 请求[ GET ] http://localhost:8080/test 的参数校验发生错误
 2022-11-23 22:19:36.594 ERROR 19880 --- [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 email = abc.com 校验错误:邮箱格式错误
 2022-11-23 22:19:36.594 ERROR 19880 --- [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 score = -20 校验错误:必须是正数或零
 2022-11-23 22:19:36.595 ERROR 19880 --- [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 birth = Thu Jan 01 08:00:00 CST 2099 校验错误:生日必须早于当前时间
 2022-11-23 22:19:36.595 ERROR 19880 --- [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 phone = 12233 校验错误:手机号格式错误
 2022-11-23 22:19:36.595 ERROR 19880 --- [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 age = -40 校验错误:年龄必须为 0~100
 2022-11-23 22:19:36.595 ERROR 19880 --- [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 name = 我是很长的名字 校验错误:名字字符长度必须为 2~4个
 2022-11-23 22:19:36.595 ERROR 19880 --- [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 score = -20 校验错误:需要在0和9223372036854775807之间
复制代码

🚀 传递校验

我们也可以使用传递校验,即一个参数类中包含了另一个参数类,被包含的参数类也可以被校验。

在声明一个新的参数类,同时修改 Student 类。

 @Data
 public class ClassInfo {
     @NotBlank(message = "班主任姓名不能为空")
     private String teacher;
     @NotNull(message = "教师不能为空")
     private Integer classroom;
     @NotNull(message = "年级不能为空")
     @Min(value = 1, message = "年级只能是 1-6")
     @Max(value = 6, message = "年级只能是 1-6")
     private Integer grade;
 }
 ​
 @Data
 public class Student {
    //.............
     // 新加的字段,被包含的参数类,使用 @Valid 就能传递校验,如果不使用 @Valid 注解,则无法传递校验。
     @Valid
     private ClassInfo classInfo;
 }
复制代码

再使用 postman 测试一次

🚀 分组校验

此外还可以使用分组校验,令一组方法对某些字段校验,而令一组方法对其他字段校验,例如:一般情况下,新增实体的接口方法 [POST] 不需要主键 ID,修改实体的接口方法 [PUT] 就需要主键 ID 以便进行修改。

为注解 @Validated 赋值属性 value,以及为那些校验注解赋值属性 group, 即可达到分组的效果。

接下来看看如何实现分组校验。

Student 类中添加两个内部接口 Inteface,同时修改 id 字段的注解,以进行分组

 @Data
 public class Student {
     // id 字段属于 Create 组
     @NotBlank(message = "主键不能为空", groups = {Student.Create.class})
     private String id;
     // .............
     // 更新分组
     public interface Update {}
 ​
     // 创建分组
     public interface Create {}
 }
复制代码

在控制层新增两个接口

 @RestController
 public class TestController {
 ​
     // @Validated 注解可以赋值 value 属性进行分组,value 是可以以数组的形式赋值,既可以分配多个组
     @PostMapping("/students")
     public Student create(@RequestBody @Validated(Student.Create.class) Student student) {
         return student;
     }
 ​
     @PutMapping("/students")
     public Student update(@RequestBody @Validated(Student.Update.class) Student student) {
         return student;
     }
 }
复制代码

在 postman 上进行测试:

可以看到分组校验也生效了。

🚀 总结

在实际开发中,我们可以使用 Spring Boot Validation 提供的注解进行参数校验,提高代码的可读性,避免编写大量的 if-else 代码块和重复的校验语句。

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

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

相关文章

k8s dashboard安装部署实战详细手册

文章目录一、k8s dashboard搭建1.选择版本2.下载yaml3.执行yaml4.访问dashboard5.token登录6.配置权限结尾一、k8s dashboard搭建 1.选择版本 dashboard和k8s存在版本对应关系&#xff0c;具体可以去github查找https://github.com/kubernetes/dashboard/releases 由于我的k8s…

亮相2022南京软博会,创邻科技携Galaxybase图平台展现信创硬核实力

11月23日&#xff0c;2022中国&#xff08;南京&#xff09;国际软件产品和信息服务交易博览会&#xff08;以下简称”软博会“&#xff09;在南京博览中心隆重开幕。此次展会以“软件赋能 数智转型”为主题&#xff0c;由江苏省工业和信息化厅、南京市人民政府、中国工业技术软…

iwebsec靶场 数据库漏洞通关1-MySQL数据库漏洞

iwebsec靶场的数据库漏洞第一项内容为MySQL弱口令漏洞渗透&#xff0c;如下所示我们可以使用kali的msf模块对其进行渗透。 一、获取iwebsec虚拟机环境的MySQL服务映射的端口号 由于iwebsec靶场是通过docker搭建在ubuntu这个宿主虚拟机中 接下来在机器在ubuntu64位这台虚拟机里…

基于JAVA的物流信息管理平台【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86406113 摘要 随着全球供应链持续受到 COVID-19 的影响&#xff0c;许多物流公司正在考虑如何重构新常态运营环境&#xff0c;实现降本增效。对于业务网点遍布全球的物流公司而言&#xff0…

【看球和学Go】错误和异常、CGO、fallthrough

这篇文章将详解「Go必知必会」的知识点&#xff1a; 错误和异常的对比、发生panic后如何执行代码&#xff1f;会执行到defer代码段吗&#xff1f;CGO是什么&#xff1f;CGO的作用是什么&#xff1f;switch中的fallthrough 错误&异常 错误指的是可能出现问题的地方出现了…

云原生系列 六【轻松入门容器基础操作】

✅作者简介&#xff1a; CSDN内容合伙人&#xff0c;全栈领域新星创作者&#xff0c;阿里云专家博主&#xff0c;华为云享专家博主&#xff0c;掘金后端评审团成员 &#x1f495;前言&#xff1a; 最近云原生领域热火朝天&#xff0c;那么云原生是什么&#xff1f;何为云原生&a…

深度学习第一次作业 - 波士顿房价预测

文章目录划分训练集和测试集建立线性回归模型特征选择重建模型尝试使用GradientBoostingimport pandas as pd import numpy as np import seaborn as sns from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklea…

第一个SpringBoot项目的创建

目录 一、SpringBoot是什么&#xff1f; 初识springboot springboot的优点 二、SpringBoot项目的创建与简单运行 &#x1f351;使用idea创建springboot项目 &#x1f351; Spring Boot 项目目录介绍 &#x1f351;springboot项目的简单运行与使用 一、SpringBoot是什么&a…

emq证书过期问题

近期在进行远程漏洞扫描后发现&#xff0c;有存在服务器证书过期的情况&#xff0c;可以通过以下步骤进行问题的处理&#xff1a; 1、先根据对应出现漏洞的端口进行检查&#xff0c;看端口对应的服务是哪个服务&#xff1a; netstat -tunlp|grep 端口号 2、通过命令&#xf…

40 - 前置操作符和后置操作符

---- 整理自狄泰软件唐佐林老师课程 问题 下面代码的区别&#xff1f;why&#xff1f; 1.1 编程实验 汇编中的处理是一样的&#xff0c;所以不可能从编译后的二进制程序还原 i 还是 i 1.2 事实 现代编译器产品会对代码进行优化优化使得最终的二进制程序更加高效优化后的二…

[附源码]java毕业设计渔具店管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【Linux】系统编程之网络编程(socket)

目录一、Socket编程1、TCP/UDP&#xff08;1&#xff09;TCP/UDP区别&#xff08;2&#xff09;TCP/UDP服务端和客户端的通信流程2、IP地址和端口号&#xff08;1&#xff09;IP地址&#xff08;2&#xff09;端口号二、字节序1、字节系相关概念2、有关字节序转换的函数&#x…

小小王总,如何变成任正非、化腾、强东这样的巨人!

原创&#xff1a;小姐姐味道&#xff08;微信公众号ID&#xff1a;xjjdog&#xff09;&#xff0c;欢迎分享&#xff0c;非公众号转载保留此声明。王总特别迷信外面的企业培训。当遇到问题时&#xff0c;他喜欢去取经。这个经不像唐僧取经一样&#xff0c;需要历经九九八十一难…

ImmunoChemistry艾美捷通用阻断ELISA阻断缓冲液说明书

ImmunoChemistry艾美捷通用阻断ELISA阻断缓冲液包含适用于大多数抗体捕获ELISA格式和肽或蛋白质抗原下调ELISA格式的哺乳动物蛋白质阻断剂。这种封闭缓冲液为干燥的抗原或抗体外壳蛋白提供了长期稳定的环境&#xff0c;并使测定过程中的非特异性结合相互作用最小化。 General B…

中石油测井-技术研发岗回顾

前提&#xff1a; 时间&#xff1a;2022年11月25日 结果&#xff1a;暂未可知 阶段&#xff1a;面试结束 等结果 整个过程中&#xff0c;注意查看官网 中石油招聘 投递之前关注一下基本要求&#xff08;学历-专业&#xff09; 招聘人数&#xff1a;应聘人数 &#xff08;通过能…

【富文本编辑器】Ueditor的demo——创建、修改——代码使用

文章目录富文本编辑器简述&#xff1a;使用&#xff1a;1.下载的demo&#xff1a;2.项目创建&#xff1a;3.修改代码&#xff1a;4.使用富文本编辑器&#xff1a;示例&#xff1a;富文本编辑器 Ueditor的资源官网&#xff1a;http://fex.baidu.com/ueditor/ 资源下载官网&…

【Linux】进程优先级(PRI,NI)和进程切换

1、进程优先级 无论是外设还是CPU&#xff0c;所能提供给操作系统的资源很少&#xff0c;而操作系统中的进程又非常多&#xff0c;安排谁先使用资源&#xff0c;就需要进程优先级的存在。   查看进程优先级 ps -al 查看当前运行进程的优先级信息。 ps -al | grep test 配合…

卫龙食品冲刺港股:上半年经调整利润超4亿 高瓴与红杉是股东

雷帝网 雷建平 11月24日卫龙食品日前更新招股书&#xff0c;准备在香港上市。这是卫龙食品通过聆讯后第二次更新招股书。卫龙食品的前身可追溯到20多年前。当年&#xff0c;卫龙食品创办人刘卫平下了火车&#xff0c;找到一个简陋的小旅馆休息&#xff0c;天亮后&#xff0c;就…

DirectX12 - Swap Chain(交换链)

这里是SunshineBooming&#xff0c;GPU公司一枚小小的Driver工程师&#xff0c;主要工作是写DirectX12 Driver&#xff0c;我会持续更新这个DX12 Spec系列&#xff0c;可能比较冷门&#xff0c;但是都是干货和工作中的心得体会&#xff0c;有任何GPU相关的问题都可以在评论区互…

宠物经济:千亿级的孤独生意,如何用智能化玩出新花样?

今年双十一&#xff0c;由铲屎官们主导的宠物经济成功实现逆增长&#xff0c;在众多行业中脱颖而出&#xff0c;成为消费疲软环境下的一匹强势黑马。 天猫、京东数据显示&#xff1a;双十一期间&#xff0c;天猫平台宠物烘焙粮销量同比增长超 700%&#xff0c;京东平台宠物高端…