如何在 SpringBoot 中优雅的做参数校验?
一、故事背景
关于参数合法性验证的重要性就不多说了,即使前端对参数做了基本验证,后端依然也需要进行验证,以防不合规的数据直接进入服务器,如果不对其进行拦截,严重的甚至会造成系统直接崩溃!
本文结合自己在项目中的实际使用经验,主要以实用为主,对数据合法性验证做一次总结,不了解的朋友可以学习一下,同时可以立马实践到项目上去。
下面我们通过几个示例来演示如何判断参数是否合法,废话不多说,直接撸起来!
二、断言验证
对于参数的合法性验证,最初的做法比较简单,自定义一个异常类。
public class CommonException extends RuntimeException {private Integer code;public Integer getCode() {return code;}public CommonException(String message) {super(message);this.code = 500;}public CommonException(Integer code, String message) {super(message);this.code = code;}
}
当检查到某个参数不合法的时候,直接抛异常!
@RestController
public class HelloController {@RequestMapping("/upload")public void upload(MultipartFile file) {if (file == null) {throw new CommonException("请选择上传文件!");}//.....}
}
最后写一个统一异常拦截器,对抛异常的逻辑进行兜底处理。
这种做法比较简单直观,如果当前参数既要判断是否为空,又要判断长度是否超过最大限制的时候,代码就会显得很臃肿,而且复用性很差!
于是,程序界的大佬想到了一个更加优雅又能节省代码的方式,创建一个断言类工具类,专门用来判断参数的是否合法,如果不合法就抛异常,示例如下:
/*** 断言工具类*/
public abstract class LocalAssert {public static void isTrue(boolean expression, String message) throws CommonException {if (!expression) {throw new CommonException(message);}}public static void isStringEmpty(String param, String message) throws CommonException{if(StringUtils.isEmpty(param)) {throw new CommonException(message);}}public static void isObjectEmpty(Object object, String message) throws CommonException {if (object == null) {throw new CommonException(message);}}public static void isCollectionEmpty(Collection coll, String message) throws CommonException {if (coll == null || (coll.size() == 0)) {throw new CommonException(message);}}
}
当我们需要对参数进行验证的时候,直接通过这个类就可以完成,示例如下:
@RestController
public class HelloController {@RequestMapping("/save")public void save(String name, String email) {LocalAssert.isStringEmpty(name, "用户名不能为空!");LocalAssert.isStringEmpty(email, "邮箱不能为空!");//.....}
}
相比上面的实现方式,这种处理逻辑,代码明显要简洁的多!
类似这样的工具类还很多,比如spring
也提供了一个名为Assert
的断言工具类,在开发的时候,可以直接使用!
三、注解验证
下面我们要介绍的是另一种更简洁的参数验证逻辑,使用注解来对数据进行合法性验证,不仅代码会变得很简洁,阅读起来也十分令人赏心悦目!
以 Spring Boot 工程为例,下面我们一起来看看具体的实践方式。
3.1、添加依赖包
首先在pom.xml
中引入spring-boot-starter-web
依赖包即可,它会自动将注解验证相关的依赖包打入工程!
<!-- spring boot web -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
3.2、编写注解校验请求对象
接着创建一个实体User
,用于封装用户注册时的请求参数,并在参数属性上添加对应的注解验证规则!
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;public class User {@NotBlank(message = "用户名不能为空!")private String userName;@Email(message = "邮箱格式不正确")@NotBlank(message = "邮箱不能为空!")private String email;@NotBlank(message = "密码不能为空!")@Size(min = 8, max = 16,message = "请输入长度在8~16位的密码")private String userPwd;@NotBlank(message = "确认密码不能为空!")private String confirmPwd;// set、get方法等...
}
3.3、编写请求接口
在web
层创建一个register()
注册接口方法,同时在请求参数上添加@Valid
注解,示例如下:
import javax.validation.Valid;@RestController
public class UserController {@RequestMapping("/register")public ResultMsg register(@RequestBody @Valid User user){if(!user.getUserPwd().equals(user.getConfirmPwd())){throw new CommonException(4001, "确认密码与密码不相同,请确认!");}//业务处理...return ResultMsg.success();}
}
3.4、编写全局异常处理器
最后自定义一个异常全局处理器,用于处理异常逻辑,如下:
@ControllerAdvice
public class GlobalExceptionHandler {private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 拦截Controller层的异常* @param e* @return*/@ExceptionHandler(value = {Exception.class})@ResponseBodypublic Object exceptionHandler(HttpServletRequest request, Exception e){LOGGER.error("【统一异常拦截】请求地址:{}, 错误信息:{}", request.getRequestURI(), e.getMessage());// 注解验证抛出的异常if(e instanceof MethodArgumentNotValidException){// 获取错误信息String error = ((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage();return ResultMsg.fail(500, error);}// 自定义抛出的异常if(e instanceof CommonException){return ResultMsg.fail(((CommonException) e).getCode(), e.getMessage());}return ResultMsg.fail(999, e.getMessage());}
}
统一响应对象ResultMsg
,如下:
public class ResultMsg<T> {/**状态码**/private int code;/**结果描述**/private String message;/**结果集**/private T data;/**时间戳**/private long timestamp;// set、get方法等...
}
3.5、服务测试
启动项目,使用postman
来验证一下代码的正确性,看看效果如何?
- 测试字段是否为空
- 测试邮箱是否合法
- 测试密码长度是否符合要求
- 测试密码与确认密码是否相同
可以看到,验证结果与预期一致!
四、自定义注解验证
事实上,熟悉 SpringMVC 源码的同学可能知道,Spring Boot 内置了一个hibernate-validator
校验组件,上文就是利用它来完成对请求时入参上的注解验证。
默认的情况下,依赖包已经给我们提供了非常多的校验注解,如下!
- JSR 提供的校验注解!
- Hibernate Validator 提供的校验注解
但是某些情况,例如性别这个参数,可能需要我们自己去手动验证。
针对这种情况,我们也可以自定义一个注解来完成参数的校验,也便于进一步了解注解验证的原理。
自定义注解验证,实现方式如下!
首先,创建一个Sex
注解。
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = SexValidator.class)
@Documented
public @interface Sex {String message() default "性别值不在可选范围内";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
然后,创建一个SexValidator
类,实现自ConstraintValidator
接口
public class SexValidator implements ConstraintValidator<Sex, String> {@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {Set<String> sexSet = new HashSet<String>();sexSet.add("男");sexSet.add("女");return sexSet.contains(value);}
}
最后,在User
实体类上加入一个性别参数,使用自定义注解进行校验!
public class User {@NotBlank(message = "用户名不能为空!")private String userName;@Email(message = "邮箱格式不正确")@NotBlank(message = "邮箱不能为空!")private String email;@NotBlank(message = "密码不能为空!")@Size(min = 8, max = 16,message = "请输入长度在8~16位的密码")private String userPwd;/*** 自定义注解校验*/@Sex(message = "性别输入有误!")private String sex;// set、get方法等...
}
启动服务,重新请求,运行结果如下:
结果与预期一致!
五、总结
参数验证,在开发中使用非常频繁,如何优雅的进行验证,让代码变得更加可读,是业界大佬一直在追求的目标!
本文主要围绕在 Spring Boot 中实现参数统一验证进行相关的知识总结和介绍,如果有描述不对的地方,欢迎留言支持。
示例代码:spring-boot-example-valid
相关文章:

如何在 SpringBoot 中优雅的做参数校验?
一、故事背景 关于参数合法性验证的重要性就不多说了,即使前端对参数做了基本验证,后端依然也需要进行验证,以防不合规的数据直接进入服务器,如果不对其进行拦截,严重的甚至会造成系统直接崩溃! 本文结合…...

Godot入门 03世界构建1.0版
在game场景,删除StaticBody2D节点,添加TileMap节点 添加TileSet图块集 添加TileSet源 拖动图片到图块,自动创建图块 使用橡皮擦擦除。取消橡皮擦后按住Shift创建大型图块。 进入选择模式,TileMap选择绘制,选中图块后在…...

GitHub每日最火火火项目(7.26)
1. 项目名称:meta - llama / llama3 项目介绍:这是 Meta Llama 3 的官方 GitHub 站点。目前尚不清楚该项目的具体功能和特点,但从名称推测,它可能与 Llama 3 模型相关,或许涉及到该模型的开发、训练或应用等方面。 项…...

微服务实践和总结
H5原生组件web Component Web Component 是一种用于构建可复用用户界面组件的技术,开发者可以创建自定义的 HTML 标签,并将其封装为包含逻辑和样式的独立组件,从而在任何 Web 应用中重复使用。 <!DOCTYPE html> <html><head…...

Spring Boot中的策略模式:优雅地处理不同商品类型的订单
引言 在开发复杂的业务系统时,我们经常会遇到需要根据不同条件执行不同逻辑的情况。例如,在电商平台中,可能需要根据商品的不同类型(如电子产品、服装、食品等)来执行不同的业务逻辑,比如不同的库存管理、…...

django_创建菜单(实现整个项目的框架,调包)
文章目录 前言代码仓库地址在线演示网址启动网站的时候出现错误渲染路径的一些说明文件结构网页显示一条错误路由顺序js打包出现问题的代码函数没有起作用关于进度开发细节显示不了图片梳理一下函数调用的流程修改一些宽度参数classjs 里面的一些细节让三个按钮可以点击设置按钮…...

最新全新UI异次元荔枝V4.4自动发卡系统源码
简介: 最新全新UI异次元荔枝V4.4自动发卡系统源码 更新日志: 1增加主站货源系统 2支持分站自定义支付接口 3目前插件大部分免费 4UI页面全面更新 5分站可支持对接其他分站产品 6分站客服可自定义 7支持限定优惠 图片: 会员中心截图&…...

PyTorch安装CUDA标准流程(可解决大部分GPU无法使用问题)
最近一段时间在研究PyTorch中的GPU的使用方法,之前曾经安装过CUDA,不过在PyTorch中调用CUDA时无法使用。考虑到是版本不兼容问题,卸载后尝试了其他的版本,依旧没有能解决问题,指导查阅了很多资料后才找到了解决方案。 …...

C++从入门到起飞之——初始化列表类型转换static成员 全方位剖析!
🌈个人主页:秋风起,再归来~🔥系列专栏:C从入门到起飞 🔖克心守己,律己则安 目录 1、初始化列表 2、 类型转换 3. static成员 4、完结散花 1、初始化列表 • 之前我们实现构造函数…...

PHP框架简介
PHP是一种广泛使用的开源脚本语言,主要用于Web开发,它可以创建动态交互式Web页面。而PHP框架则是一套用于开发Web应用程序的工具和库的集合,它可以帮助开发者更高效地编写PHP代码,提高开发速度和代码的可维护性。 理解PHP框架&am…...

微信小程序-粘性组件
再次完善:将区域设置为粘性时,会脱离原有文档,使得出现下方页面突然遮盖一部分,不平滑 解决:给出一个新的空白区域,宽高与粘性区域一致,wx:if 控制其显示 /****************/ 后续补充&#…...

微服务注册中心
目录 1.微服务的注册中心 1.1 注册中⼼的主要作⽤ 1.2 常⻅的注册中⼼ 2.nacos简介 2.1 nacos实战⼊⻔ 2.2.1 搭建nacos环境 2.2.2 将商品微服务注册到nacos 3.服务调⽤Ribbon⼊⻔ 3.1 Ribbon概述 3.1.1 什么是Ribbon 3.1.2 Ribbon的主要作⽤ 3.2.2 ⼯程改造 4.服务…...

HDU1032——The 3n + 1 problem,HDU1033——Edge,HDU1034——Candy Sharing Game
目录 HDU1032——The 3n 1 problem 题目描述 运行代码 代码思路 HDU1033——Edge 题目描述 运行代码 代码思路 HDU1034——Candy Sharing Game 题目描述 运行代码 代码思路 HDU1032——The 3n 1 problem 题目描述 Problem - 1032 运行代码 #include <iostr…...

内网对抗-隧道技术篇防火墙组策略HTTP反向SSH转发出网穿透CrossC2解决方案
知识点: 1、C2/C2上线-CrossC2插件-多系统平台支持 2、隧道技术篇-应用层-SSH协议-判断&封装&建立&穿透 3、隧道技术篇-应用层-HTTP协议-判断&封装&建立&穿透隧道技术主要解决网络通讯问题:遇到防火墙就用隧道技术,…...

实战案例:如何用ChatGPT生成适合不同领域的高质量文章
随着人工智能技术的快速进展,制作高质量文章已变得轻而易举。尤其是OpenAI推出的ChatGPT,极大地简化了写作任务。接下来,本文将通过具体案例,详解如何利用ChatGPT撰写不同领域的高品质文章。 背景:光辉AI交流-免费问答…...

多线程案例-单例模式
单例模式是设计模式之一,能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例 单例模式的具体实现方法有很多,最常见的是 “饿汉” 和 “懒汉” 两种。 饿汉模式 class Singlenton{private static Singlenton instance new Sin…...

P6 优化篇 - 数据折线图可视化步骤
增加新页面, 则需要在 page.json里面增加页面信息 2.添加目录, 和路径 同时也要添加目录了 , 新建目录LineChart , 添加文件LineChart.vue 4.LineChart.vue 直接复制黏贴 <template><view class"container"><!-- 图表显示区域 --><view cla…...

优选算法之二分查找(上)
目录 一、二分查找 1.题目链接:704. 二分查找 2.题目描述: 3.算法流程: 4.算法代码: 二、在排序数组中查找元素的第一个和最后一个位置 1.题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 2.题目描述…...

JavaScript(16)——定时器-间歇函数
开启定时器 setInterval(函数,间隔时间) 作用:每隔一段时间调用这个函数,时间单位是毫秒 例如:每一秒打印一个hello setInterval(function () { document.write(hello ) }, 1000) 注:如果是具名函数的话不能加小括号…...

VUE中的重点*
1.MVC 和 MVVM的区别? MVC:M(model数据)、V(view视图),C(controlle控制器) 缺点是前后端无法独立开发,必须等后端接口做好了才可以往下走; 前端没…...

rabbitmq生产与消费
一、rabbitmq发送消息 一、简单模式 概述 一个生产者一个消费者模型 代码 //没有交换机,两个参数为routingKey和消息内容 rabbitTemplate.convertAndSend("test1_Queue","haha");二、工作队列模式 概述 一个生产者,多个消费者&a…...

spring-boot3.x整合Swagger 3 (OpenAPI 3) +knife4j
1.简介 OpenAPI阶段的Swagger也被称为Swagger 3.0。在Swagger 2.0后,Swagger规范正式更名为OpenAPI规范,并且根据OpenAPI规范的版本号进行了更新。因此,Swagger 3.0对应的就是OpenAPI 3.0版本,它是Swagger在OpenAPI阶段推出的一个…...

SM2隐式证书用户公私钥生成python代码实现
GMT0130-2023具体描述基于SM2算法的隐式证书公钥机制,这里尝试Python代码实现密钥生成部分功能,具体如下,椭圆曲线计算实现使用python第三方包gmssl。 #生成用户私钥Da和公钥Pa,其中Da(tAdA)mod N,Pa可以直…...

IEC104转MQTT网关快速实现了IEC104到MQTT的转换和数据交互
随着智能电网技术的不断进步,IEC 104(IEC 60870-5-104)协议作为电力系统中重要的远动通信标准,正逐步融入更广泛的物联网生态系统中。亚马逊AWS(Amazon Web Services),作为全球领先的云计算服务…...

【OpenCV C++20 学习笔记】调节图片对比度和亮度(像素变换)
调节图片对比度和亮度(像素变换) 原理像素变换亮度和对比度调整 代码实现更简便的方法结果展示 γ \gamma γ校正及其实操案例线性变换的缺点 γ \gamma γ校正低曝光图片矫正案例代码实现 原理 关于OpenCV的配置和基础用法,请参阅本专栏的其…...

web UI自动化测试 浏览器模式设置
自动化之浏览器模式设置 做selenium UI自动化测试时,每次都需要启动浏览器、用例运行结束后再关闭浏览器,浏览器启动相当地耗费时间,在本机运行用例的话还得放开双手,可以使用chrome的headless模式,让浏览器在后台运行…...

OpenCV图像滤波(1)双边滤波函数bilateralFilter的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 功能描述 bilateralFilter是图像处理和计算机视觉领域中的一种高级图像滤波技术,特别设计用于在去除噪声的同时保留图像的边缘和细节。相比于传…...

前端开发使用Big.js精算避免误差
1、下载 npm install big.js 全局引入还是局部引入可根据项目框架及个人需求 2、静态引入 < script src https://unpkg.com/big.js6.0.0/big.mjs > </ script > 或者 import Big from https://raw.githubusercontent.com/mikemcl/big.js/v6.0.0/big.mjs; i…...

在 Ubuntu 22.04/20.04 安装 CVAT 和 SAM 指南
1. 安装 Docker 和 Docker Compose sudo apt-get update sudo apt-get --no-install-recommends install -y \apt-transport-https \ca-certificates \curl \gnupg-agent \software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-ke…...

【SpringCloud】 微服务分布式环境下的事务问题,seata大合集
目录 微服务分布式环境下的事务问题 分布式事务 本地事务 BASE理论与强弱一致性 BASE理论 强弱一致性 常见分布式事务解决方案 - 2PC 常见分布式事务解决方案 - TCC 常见分布式事务解决方案 - 最大努力通知 常见分布式事务解决方案 - 最终一致性 Seata介绍与术语 Seata…...