探索Spring Validation:优雅实现后端数据验证的艺术

在现代Web应用开发中,数据验证是一项至关重要的任务,确保应用程序接收到的用户输入符合预期规范,不仅能够提高系统的健壮性,也能有效防止潜在的安全漏洞。Spring Framework通过其内置的Spring Validation模块,为我们提供了强大的数据验证功能,本文将带你深入了解Spring Validation的实现原理及其最佳实践。

一、Spring Validation简介

Spring Validation是基于Java Bean Validation规范(JSR 303/349 for Java EE 6/7,JSR 380 for Java EE 8)实现的,主要依赖的是Hibernate Validator作为默认的实现引擎。它允许我们在Java Bean的属性上声明验证注解,从而在运行时对这些属性进行条件性验证。

在实际项目我们需要对客户端传递到服务端的参数进行校验,用于判定请求参数的合法性,假如请求参 数不合法则不可以再去执行后续的业务了。那如何校验呢?

一种方式是我们在控制层方法中每次都自己进行参数有效值的判断,不合法可以抛出异常,但是工作量 和代码复杂度会比较高;

第二种方式就是采用市场上主流的 Spring Validation 框架去实现校验,所以 Spring Validation 框架的主要作用是 检查参数的基本有效性。

二、集成与配置

1. 引入依赖

在Maven或Gradle构建项目中,我们需要引入Hibernate Validator的相关依赖,例如在Maven的pom.xml文件中添加:

Xml

1<dependency>
2    <groupId>org.springframework.boot</groupId>
3    <artifactId>spring-boot-starter-validation</artifactId>
4</dependency>

2.使用注解

Spring Validation提供了丰富的预定义注解,如@NotNull,@NotEmpty,@NotBlank,@Size,@Range等,可以直接应用于实体类的字段上:

@NotNull注解

作用:用于验证对象是否为 null

用法: @NotNull 注解用于对象类型上

示例

@NotNull(message = "用户名不能为null")
private String username;

@NotEmpty注解

作用:用于验证字符串是否为空,并且会检查是否为 null 值(为null值时报错)

用法:用于字符串类型上

示例

@NotEmpty(message = "用户名不能为空")
private String username;

@NotBlank注解

作用:不允许为空白,即不允许是“仅由空格、TAB等空白值组成的字符串”,也不允许为空字符串,也不允许为空值null

用法:用于字符串类型上

示例

@NotBlank(message = "用户名不能为空白串")
private String username;

@Size注解

作用:可以指定最小值和最大值限制字符串的长度串,也不允许为空值null

用法:用于字符串类型参数

示例

@Size(min = 6, max = 20, message = "用户名长度必须在6到20之间")
private String username;

@Range 注解

作用:用于验证数字类型字段的取值范围,通过配置min和max属性来限制数值类型参数的值区间 包括最小值和最大值

用法:用于数值类型参数

示例

@Range(min = 1, max = 10, message = "年龄必须在1-10岁之间")
private int age;@Range(min = 0.1, max = 1.0, message = "成绩必须在0.1到1.0之间")
private double score;

三、验证流程与控制器处理

  1. 控制器层处理: 在Spring MVC的Controller中,我们可以使用@Valid注解来触发模型对象的验证,紧接着一个BindingResult对象用于捕获验证结果:

    1@PostMapping("/users")
    2public ResponseEntity<?> createUser(@Valid @RequestBody User user, BindingResult bindingResult) {
    3    if (bindingResult.hasErrors()) {
    4        // 处理验证错误
    5        return handleValidationError(bindingResult);
    6    } else {
    7        // 验证通过,继续处理业务逻辑
    8        userService.createUser(user);
    9        return ResponseEntity.ok().build();
    10    }
    11}
  2. 自定义注解与验证器: 对于更复杂或特定的验证需求,Spring Validation允许我们自定义注解并编写相应的验证器实现javax.validation.ConstraintValidator接口:

    1@Target({ElementType.FIELD})
    2@Retention(RetentionPolicy.RUNTIME)
    3@Constraint(validatedBy = EncryptedIdValidator.class)
    4public @interface EncryptedId {
    5    String message() default "加密ID格式不正确";
    6    Class<?>[] groups() default {};
    7    Class<? extends Payload>[] payload() default {};
    8}
    9
    10public class EncryptedIdValidator implements ConstraintValidator<EncryptedId, String> {
    11    // 实现自定义的验证逻辑
    12    ...
    13}

 四、快速入门

1. 在处理请求的方法的参数列表中,在POJO类型的参数上添加 @Validated 注解,表示需要通过 Spring Validation框架检查此参数,例如UserController中注册功能:

@ApiOperation(value = "注册功能")
@PostMapping("reg")
public JsonResult reg(@RequestBody @Validated UserRegDTO userRegDTO){}

2. 在此POJO类中的属性上,添加对应的检查注解,以配置检查规则, 例如,添加 @NotNull 注解,就表示“不允许为 null ”的规则!

在 UserRegDTO 类

@Data
public class UserRegDTO {@NotNull@ApiModelProperty(value = "用户名", required = true, example = "赵丽颖")private String username;@ApiModelProperty(value = "密码", required = true, example = "123456")private String password;@ApiModelProperty(value = "昵称", required = true, example = "萤火虫")private String nickname;
}

3. 重启工程,在Knife4j中测试,当提交请求时,如果没有提交username参数,服务器端将响应 400 错误。

同时在终端也会出现异常

五、异常处理与响应

1.处理异常

为了给前端提供友好的反馈,通常我们会统一处理验证失败时的异常,将其转换成HTTP状态码和错误信息:

第1步:全局异常处理器 GlobalExceptionHandler 中定义处理异常方法

@ExceptionHandler
public JsonResult handleBindException(MethodArgumentNotValidException ex){return new JsonResult(3002, "请求参数错误");
}

第2步:重启工程,在Knife4j中测试

2.明确提示信息

当提交的 username 的值为 null 时,可以发现异常已被处理!

但是,处理结果并不合适,因为,客户端得到此结果后,仍无法明确出现了什么错误!

所有的检查注解都可以配置 message 参数,用于对错误进行描述。

第1步: @NotNull 注解中添加 message 参数

@NotNull(message = "必须提交用户名")
private String username;

第2步:自定义枚举状态码 StatusCode

VALIDATE_ERROR(3002, "参数校验失败")

第3步:异常方法中获取提示信息 message

在处理异常时,需要调用 MethodArgumentNotValidException 对象的

getFieldError().getDefaultMessage() 获取以上配置的描述文本

@ExceptionHandler
public JsonResult handleBindException(MethodArgumentNotValidException ex){/*ex.getFieldError().getDefaultMessage():获取 @NotNull(message="xxx") 中
message的消息*/String message = ex.getFieldError().getDefaultMessage();return new JsonResult(StatusCode.VALIDATE_ERROR, message);
}

第3步:重启工程,在Knife4j中测试

六、非POJO类参数校验

在 Spring Validation 中,除了对 POJO(Plain Old Java Object)进行校验的功能外,还支持对非 POJO 进行校验,比如 String、Integer、Double 等类型的参数。

1.使用流程

  • 在当前方法所在的类上添加 @Validated 注解
  • 在参数上添加对应的检查注解

2.使用示例

对于微博详情页的 id 参数进行范围校验,范围只能在1-10之间

第1步:在类 WeiboController 中添加 @Validated 注解

@Validated
public class WeiboController {}

第2步:在控制器方法参数 id 上添加对应的检查注解

public JsonResult selectById(@Range(min = 1, max=10, message = "请提交合法的ID
值!") int id)

第3步:重启工程,在Knife4j或者浏览器中测试

七、总结

Spring Validation的使用极大地简化了Java应用程序中的数据验证过程,通过标准化的注解机制实现了业务逻辑和数据验证的分离,提高了代码的可读性和可维护性。同时,它也具有良好的扩展性,让我们能够轻松应对各类复杂的验证场景,确保了系统数据质量的同时提升了用户体验。

通过深入理解和灵活运用Spring Validation,开发者能够在后端建立起坚固的数据过滤防线,为构建安全可靠的应用程序打下坚实基础。而随着RESTful API设计的流行,Spring Validation更是成为了保障API契约得到严格遵循的重要工具之一。

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

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

相关文章

极值图论基础

目录 一&#xff0c;普通子图禁图 二&#xff0c;Turan问题 三&#xff0c;Turan定理、Turan图 1&#xff0c;Turan定理 2&#xff0c;Turan图 四&#xff0c;以完全二部图为禁图的Turan问题 1&#xff0c;最大边数的上界 2&#xff0c;最大边数的下界 五&#xff0c;…

Vue3大事件项目(ing)

文章目录 核心内容1.大事件项目介绍2.大事件项目创建3.Eslint配置代码风格4.配置代码检查工作流问题: pnpm lint是全量检查,耗时问题,历史问题 5.目录调整6.vue-router4 路由代码解析7.引入 Element Plus 组件库8.Pinia 构建仓库 和 持久化9.Pinia 仓库统一管理 核心内容 Vue3…

【C/C++ 10】扫雷小游戏

一、题目 写一个扫雷小游戏&#xff0c;每次输入一个坐标&#xff0c;若该处是地雷&#xff0c;则游戏失败&#xff0c;若该处不是地雷&#xff0c;则显示周围地雷数量&#xff0c;若扫除全部非地雷区域&#xff0c;则扫雷成功。 二、算法 设置两张地图&#xff08;二维数组&…

04-Java建造者模式 ( Builder Pattern )

建造者模式 摘要实现范例 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象 一个Builder 类会一步一步构造最终的对象&#xff0c;该 Builder 类是独立于其他对象的 建造者模式属于创建型模式&#xff0c;它提供了一种创建对…

【Java八股面试系列】并发编程-并发关键字,线程池

目录 并发关键字 Synchronized synchronized最主要的三种使用方式&#xff1a; 具体使用&#xff1a;双重校验锁单例模式 synchronized 底层实现原理&#xff1f; synchronized锁的优化 偏向锁 轻量级锁 重量级锁 Mark Word 与 Monitor 之间的关系 总结 偏向锁、轻量…

Aethir和Well-Link Tech携手革新云游戏,释放人工智能(AI)潜力

​Aethir将为Well-Link Tech的2亿用户提供先进的GPU计算能力&#xff0c;大幅提升他们的游戏体验。 新加坡&#xff0c;2024年2月7日 - 先驱性的去中心化GPU网络Aethir与实时云渲染技术领导者Well-Link Tech携手共创云游戏和元宇宙发展的新时代。 借助Well-Link Tech对领先游戏…

2024年第一篇博客

这是2024年的第一篇博客&#xff0c;2023年笔者经历了一连串的生活、工作、学习上的转折和调整&#xff0c;跌跌撞撞时光飞逝&#xff0c;转眼间就踏着元旦的钟声步入了2024年&#xff0c;前思后想、辗转反侧、犹豫再三不知道从哪里开始博客新的篇章&#xff0c;这个问题坦诚说…

.NET高级面试指南专题六【线程安全】5种方法解决线程安全问题

前言 多线程编程相对于单线程会出现一个特有的问题&#xff0c;就是线程安全的问题。所谓的线程安全&#xff0c;就是如果你的代码所在的进程中有多个线程在同时运行&#xff0c;而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的&#xff0c;而且…

ElementUI Data:Table 表格

ElementUI安装与使用指南 Table 表格 点击下载learnelementuispringboot项目源码 效果图 el-table.vue&#xff08;Table表格&#xff09;页面效果图 项目里el-table.vue代码 <script> export default {name: el_table,data() {return {tableData: …

ubuntu22.04@laptop OpenCV Get Started: 000_hello_opencv

ubuntu22.04laptop OpenCV Get Started: 000_hello_opencv 1. 源由2. Hello OpenCV2.1 C应用Demo2.2 Python应用Demo 3. 参考资料 1. 源由 之前&#xff0c;通过敲门砖已经砸开了OpenCV的大门&#xff0c;接下来是体验下“Hello World&#xff01;”程序。 2. Hello OpenCV …

【01】判断素数/质数(C语言)

目录 &#xff08;1&#xff09;素数特点&#xff1a;只能被1和本身整除 &#xff08;2&#xff09;代码如下&#xff1a; &#xff08;3&#xff09;运行结果如下 ​编辑 &#xff08;4&#xff09;函数引申 &#xff08;1&#xff09;素数特点&#xff1a;只能被1和本身…

每日五道java面试题之java基础篇(四)

第一题. 访问修饰符 public、private、protected、以及不写&#xff08;默认&#xff09;时的区别&#xff1f; Java 中&#xff0c;可以使⽤访问控制符来保护对类、变量、⽅法和构造⽅法的访问。Java ⽀持 4 种不同的访问权限。 default (即默认&#xff0c;什么也不写&…

5-3、S曲线生成器【51单片机+L298N步进电机系列教程】

↑↑↑点击上方【目录】&#xff0c;查看本系列全部文章 摘要&#xff1a;本节介绍步进电机S曲线生成器的计算以及使用 一.计算原理 根据上一节内容&#xff0c;已经计算了一条任意S曲线的函数。在步进电机S曲线加减速的控制中&#xff0c;需要的S曲线如图1所示&#xff0c;横…

containerd中文翻译系列(十九)cri插件

cri插件包含的内容比较多&#xff0c;阅读之前请深呼吸三次、三次、三次。 CRI 插件的架构 本小节介绍了 containerd 的 cri 插件的架构。 该插件是 Kubernetes 容器运行时接口&#xff08;CRI&#xff09; 的实现。Containerd与Kubelet在同一个节点上运行。containerd内部的…

【Rust日报】2024-02-08 Loungy:使用 Rust 和 GPUI 开发的 MacOS 启动器

Mira Screenshare&#xff1a;基于 Rust 和 WebRTC 的高性能屏幕分享工具 一群大学生宣布推出了他们的期末项目&#xff1a;Mira Screenshare&#xff0c;一个开源、高性能的屏幕共享工具&#xff0c;由 Rust 和 WebRTC 构建。此项目支持 4k 60 FPS 和 110ms 端到端延迟的屏幕…

pycharm deployment 灰色 一直无法点击

我的development的配置如下&#xff0c;我看了很多教程一直不知道为什么一直是灰色的&#xff0c; 文件夹配置&#xff1a; 如果你这里 Autodect&#xff0c;那么你Mapping 的文件夹应该是应该省略这个前缀的&#xff0c;例如我下面&#xff0c;我应该将本地文件夹映射到/home…

深度学习入门笔记(九)自编码器

自编码器是一个无监督的应用&#xff0c;它使用反向传播来更新参数&#xff0c;它最终的目标是让输出等于输入。数学上的表达为&#xff0c;f(x) x&#xff0c;f 为自编码器&#xff0c;x 为输入数据。 自编码器会先将输入数据压缩到一个较低维度的特征&#xff0c;然后利用这…

天猫平台数据查询(天猫数据分析):床上用品市场规模达335亿,床品消费呈现功能化趋势

作为人们的生活必需品之一&#xff0c;床上用品的市场规模庞大。近年来&#xff0c;越来越多消费者购物习惯发生转变&#xff0c;线上客流大幅提升&#xff0c;面对这一变化&#xff0c;许多家纺企业开始借助线上平台开展销售&#xff0c;不断创新营销模式&#xff0c;通过短视…

【大厂AI课学习笔记】【1.5 AI技术领域】(8)文本分类

8,9,10&#xff0c;将分别讨论自然语言处理领域的3个重要场景。 自然语言处理&#xff0c;Natual Language Processing&#xff0c;NLP&#xff0c;包括自然语言识别和自然语言生成。 用途是从非结构化的文本数据中&#xff0c;发掘洞见&#xff0c;并访问这些信息&#xff0…

[职场] 抖音运营SOP全攻略 #微信#职场发展

抖音运营SOP全攻略 1.养号的步骤 注册一机—卡一号&#xff0c;在注册的前5天只看视频不发视频&#xff0c;单日观看视频的时长不少于30分钟。观看过程中正常评论点赞互动&#xff0c;关注5-10个头部大号。关注20个二三十万至百万的竟品账号。 粉丝量低于1W的账号下不要留下…