探索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.mzph.cn/news/676169.shtml

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

相关文章

机器学习系列——(十一)回归

引言 在机器学习领域&#xff0c;回归是一种常见的监督学习任务&#xff0c;它主要用于预测数值型目标变量。回归分析能够通过对输入特征与目标变量之间的关系建模&#xff0c;从而对未知数据做出预测。 概念 回归是机器学习中的一种监督学习方法&#xff0c;用于预测数值型目…

yo!这里是Linux线程保姆级入门介绍

目录 前言 Linux线程基础 线程概念 底层示意图 线程vs进程 Linux线程控制 创建线程 线程ID 线程终止 线程等待 线程分离 Linux线程互斥 背景概念 互斥量mutex 1.相关接口 2.实现原理 可重入vs线程安全 死锁 Linux线程同步 条件变量 生产者消费者模型 基于…

MacOS 查AirPods 电量技巧:可实现低电量提醒、自动弹窗

要怎么透过macOS 来查询AirPods 电量呢&#xff1f;当AirPods 和Mac 配对后&#xff0c;有的朋友想通过Mac来查询AirPods有多少电量&#xff0c;这个里有几个技巧&#xff0c;下面我们来介绍一下。 透过Mac 查AirPods 电量技巧 技巧1. 利用状态列上音量功能查询 如要使用此功能…

FastJson、Jackson使用AOP切面进行日志打印异常

FastJson、Jackson使用AOP切面进行日志打印异常 一、概述 1、问题详情 使用FastJson、Jackson进行日志打印时分别包如下错误&#xff1a; 源码&#xff1a; //fastjon log.info("\nRequest Info :{} \n"&#xff0c; JSON.toJSONString(requestInfo)); //jackson …

人力资源智能化管理项目(day04:组织架构)

学习源码可以看我的个人前端学习笔记 (github.com):qdxzw/humanResourceIntelligentManagementProject 觉得有帮助的同学&#xff0c;可以点心心支持一下哈 树组件应用 <!-- 展示树形结构 --><!-- default-expand-all默认展开所有节点 --><el-tree default-ex…

CTFshow web(命令执行 41-44)

web41 <?php /* # -*- coding: utf-8 -*- # Author: 羽 # Date: 2020-09-05 20:31:22 # Last Modified by: h1xa # Last Modified time: 2020-09-05 22:40:07 # email: 1341963450qq.com # link: https://ctf.show */ if(isset($_POST[c])){ $c $_POST[c]; if(!p…

Intellij Idea的数据库工具 DataGrip

DataGrip DataGrip&#xff1a; IDEA自带&#xff0c;非常好用。智能提示很强大&#xff0c;快捷键跟IDEA自身一致。 如果下载不了 DataGrip&#xff0c;也可以直接用 IDEA 自带的。 常用的快捷键 alt8&#xff1a; 打开数据库Service ctrlshiftF10&#xff1a;打开常用的数…

【C++】类的6个默认成员函数

目录 1. 类的6个默认成员函数 2. 构造函数 3. 析构函数 4. 拷贝构造函数 5. 运算符重载 5.1运算符重载 5.2赋值运算符重载 5.3前置和后置重载 5.4日期类的实现 6. const成员函数 7. 取地址及const取地址操作符重载 1. 类的6个默认成员函数 对于一个空类&#xff0c;编…

JSDoc 真能取代 TypeScript?

这几个月&#xff0c;想必大家都听到过一个新闻&#xff1a; Svelte 弃用 TypeScript&#xff0c;改用 JSDoc 了。 TypeScript 我们知道&#xff0c;是用来给 JS 加上类型的&#xff0c;可以实现类型提示和编译时的类型检查。 那 JSDoc 能够完成一样的功能么&#xff1f;Svel…

图像处理常用算法—6个算子 !!

目录 前言 1、Sobel 算子 2、Isotropic Sobel 算子 3、Roberts 算子 4、Prewitt 算子 5、Laplacian算子 6、Canny算子 前言 同图像灰度不同&#xff0c;边界处一般会有明显的边缘&#xff0c;利用此特征可以分割图像。 需要说明的是&#xff1a;边缘和物体间的边界并不…

Android应用图标微技巧,8.0系统中应用图标的适配

大家好,2018年的第一篇文章到的稍微有点迟,也是因为在上一个Glide系列结束之后一直还没想到什么好的新题材。 现在已经进入了2018年,Android 8.0系统也逐渐开始普及起来了。三星今年推出的最新旗舰机Galaxy S9已经搭载了Android 8.0系统,紧接着小米、华为、OV等国产手机厂…

一句话总结Docker与K8S的关系

一句话总结&#xff1a;Docker只是容器的一种&#xff0c;它面向的是单体&#xff0c;K8S可以管理多种容器&#xff0c;它面向的是集群&#xff0c;Docker可以作为一种容器方案被K8S管理。下文继续具体介绍。 1、容器的核心概念 介绍这几个核心概念&#xff1a;OCI、CR、Runc、…

C语言指针运算

指针运算 指针加法意味着地址向上移动若干个目标指针减法意味着地址向下移动若干个目标示例&#xff1a; int a 100; int *p &a; // 指针 p 指向整型变量 aint *k1 p 2; // 向上移动 2 个目标&#xff08;2个int型数据&#xff09; int *k2 p - 3; // 向下移动 3 个…

PWM输入输出

PWM&#xff08;Pulse Width Modulation&#xff09;即脉冲宽度调制&#xff0c;在具有惯性的系统中&#xff0c;可以通过对一系列脉冲的宽度进行制&#xff0c;来等效地获得所需要的模拟参量&#xff0c;常应用于电机控速、开关电源等领域。 PWM参数 PWM 中有三个重要参数&…

寒假作业-day7

1>现有文件test.c\test1.c\main.c , 请编写Makefile. 代码&#xff1a; CCgcc EXEstr OBJS$(patsubst %.c,%.o,$(wildcard *.c)) CFLAGS-c -oall:$(EXE)$(EXE):$(OBJS)$(CC) $^ -o $%.o:%.c$(CC) $(CFLAGS) $ $^head.o:head.hclean:rm $(OBJS) $(EXE) 2>C编程实现&…

封装sku组件

1. 准备模板渲染规格数据 使用Vite快速创建一个Vue项目&#xff0c;在项目中添加请求插件axios&#xff0c;然后新增一个SKU组件&#xff0c;在根组件中把它渲染出来&#xff0c;下面是规格内容的基础模板 <script setup> import { onMounted, ref } from vue import axi…

NOVATEK显示技术系列之CEDSCHPI Training差异简介

CEDS的数据封包格式&#xff1a;首先CEDS数据封包包括三个部分&#xff1a; Training Pattern即Phase1Control Data 即 Phase2RGB Data 即Phase3 Power on Timing&#xff1a; 工作原理&#xff1a; Power ON时&#xff0c;TCON会发Training Pattern&#xff0c;当COF接受Tr…

江科大STM32 终

目录 SPI协议10.1 SPI简介W25Q64简介10.3 SPI软件读写W25Q6410.4 SPI硬件外设读写W25Q64 BKP备份寄存器、PER电源控制器、RTC实时时钟11.0 Unix时间戳代码示例&#xff1a;读写备份寄存器BKP11.2 RTC实时时钟 十二、PWR电源控制12.1 PWR简介代码示例&#xff1a;修改主频12.3 串…

分享90个行业PPT,总有一款适合您

分享90个行业PPT&#xff0c;总有一款适合您 90个行业PPT下载链接&#xff1a;https://pan.baidu.com/s/1bHvhk_42-IFAjNdjPPtMZw?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易…

106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

题目描述 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 题目示例 输入&#xff1a;inorder [9,3,15,20,7], postorder [9,15,7,20,3] 输出&a…