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

news2024/11/26 8:52:34

在现代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.coloradmin.cn/o/1444359.html

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

相关文章

案例:CentOS8 在 MySQL8.0 实现半同步复制

异步复制 MySQL 默认的复制即是异步的&#xff0c;主库在执行完客户端提交的事务后会立即将结果返给给客户端&#xff0c;并不关心从库是否已经接收并处理&#xff0c;这样就会有一个问题&#xff0c;主节点如果 crash 掉了&#xff0c;此时主节点上已经提交的事务可能并没有传…

进程间通信-消息队列

消息队列的公共资源是链表结构。 通信双方不会和消息队列进行挂接&#xff0c;而是像管道一样&#xff0c;访问内存中的消息队列。 消息队列由操作系统维护&#xff0c;但是由通信的某一方创建和删除 通信双方都需要获取到消息队列&#xff0c;和共享内存一样。 当发送方有数据…

ARP欺骗攻击利用之抓取https协议的用户名与密码

1.首先安装sslstrip 命令执行&#xff1a;apt-get install sslstrip 2.启动arp欺骗 arpspoof -i ech0 -t 192.168.159.148 192.168.159.2 arpspoof -i ech0(网卡) -t 目标机ip 本地局域网关 3.命令行输入: vim /etc/ettercap/etter.conf进入配置文件 找到下红框的内容&a…

Java核心设计模式:代理设计模式

一、生活中常见的代理案例 房地产中介&#xff1a;客户手里没有房源信息&#xff0c;找一个中介帮忙商品代购&#xff1a;代理者一般有好的资源渠道&#xff0c;降低购物成本&#xff08;如海外代购&#xff0c;自己不用为了买东西出国&#xff09; 二、为什么要使用代理 对…

《动手学深度学习(PyTorch版)》笔记8.5

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

3D立方体图册

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>3D立方体图册</title><style>* {pad…

奶茶点餐|奶茶店自助点餐系统|基于微信小程序的饮品点单系统的设计与实现(源码+数据库+文档)

奶茶店自助点餐系统目录 目录 基于微信小程序的饮品点单系统的设计与实现 一、前言 二、系统功能设计 三、系统实现 1、商品信息管理 2、商品评价管理 3、商品订单管理 4、用户管理 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&#xff1a; 五、核心代码 …

###C语言程序设计-----C语言学习(11)#数据的存储和基本数据类型

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 一. 数据的存储 1.整型数据的存储 计算机处理的所有信息都以二进制形式表示…

政安晨:梯度与导数~示例演绎《机器学习·神经网络》的高阶理解

这篇文章确实需要一定的数学基础&#xff0c;第一次接触的小伙伴可以先看一下我示例演绎这个主题的前两篇文章&#xff1a; 示例演绎机器学习中&#xff08;深度学习&#xff09;神经网络的数学基础——快速理解核心概念&#xff08;一&#xff09;&#xff1a; 政安晨&#…

HCIA-HarmonyOS设备开发认证V2.0-3.2.轻量系统内核基础-时间管理

目录 一、时间管理1.1、时间接口 一、时间管理 时间管理以系统时钟为基础&#xff0c;给应用程序提供所有和时间有关的服务。系统时钟是由定时器/计数器产生的输出脉冲触发中断产生的&#xff0c;一般定义为整数或长整数。输出脉冲的周期叫做一个“时钟滴答”。系统时钟也称为…

课时20:全局变量_嵌套shell

2.3.3 嵌套shell 学习目标 这一节&#xff0c;我们从 export原理、嵌套实践、小结 三个方面来学习。 export原理 原理解析 用户登录时:用户登录到Linux系统后&#xff0c;系统将启动一个用户shell。在这个shell中&#xff0c;可以使用shell命令或声明变量&#xff0c;也可…

MySQL篇----第十九篇

系列文章目录 文章目录 系列文章目录前言一、什么是存储过程?用什么来调用?二、如何通俗地理解三个范式?三、什么是基本表?什么是视图?四、试述视图的优点?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这…

AI助力农作物自动采摘,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建作番茄采摘场景下番茄成熟度检测识别计数分析系统

去年十一那会无意间刷到一个视频展示的就是德国机械收割机非常高效自动化地24小时不间断地在超广阔的土地上采摘各种作物&#xff0c;专家设计出来了很多用于采摘不同农作物的大型机械&#xff0c;看着非常震撼&#xff0c;但是我们国内农业的发展还是相对比较滞后的&#xff0…

vue项目搭建测试

5&#xff0c;项目测试 导入elementplus以及样式 import ElementPlus from element-plus import element-plus/dist/index.csscreateApp(App).use(store).use(router).use(ElementPlus).mount(#app)<template><el-row class"mb-4"><el-button>De…

Windows10安装PCL1.14.0及点云配准

一、下载visual studio2022 下载网址&#xff1a;Visual Studio: 面向软件开发人员和 Teams 的 IDE 和代码编辑器 (microsoft.com) 安装的时候选择"使用C的桌面开发“&#xff0c;同时可以修改文件路径&#xff0c;可以放在D盘。修改文件路径的时候&#xff0c;共享组件、…

【PWN · heap | Arbitrary Alloc】2015_9447ctf_search-engine

和【PWN heap | House Of Spirit】2014_hack.lu_oreo-CSDN博客略有区别&#xff0c;但都是通过malloc一块fake_chunk到指定区域&#xff0c;获得对该区域的写权限 目录 零、简单介绍 一、题目分析 1.主要功能 2.index_sentence(): 增添一条语句到“库”中 3.search_word(…

ubuntu下修改hosts读写权限

ubuntu下修改hosts文件的操作&#xff1a; 由于需要在hosts文件下添加ip地址信息&#xff0c;但是初始情况下系统该文件为只读权限无法修改&#xff0c;具体操作如下所示&#xff1b; 1.cd到系统etc目录下&#xff0c;执行如下命令,此时会提示输入密码&#xff0c;直接输入回…

Java实现陕西非物质文化遗产网站 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 设计目标2.2 研究内容2.3 研究方法与过程2.3.1 系统设计2.3.2 查阅文献2.3.3 网站分析2.3.4 网站设计2.3.5 网站实现2.3.6 系统测试与效果分析 三、系统展示四、核心代码4.1 查询民间文学4.2 查询传统音乐4.3 增改传统舞…

用C语言列出Linux或Unix上的网络适配器

上代码&#xff1a; 1. #include <sys/socket.h> 2. #include <stdio.h> 3. 4. #include <netdb.h> 5. #include <ifaddrs.h> 6. 7. int main() { 8. struct ifaddrs *addresses; 9. if(getifaddrs(&addresses) -1) { 10. printf("…

软件价值11-简单计算器

用python的tkinter做的简单计算器 代码&#xff1a; import tkinter as tkdef button_click(item):global expressionexpression expression str(item)input_text.set(expression)def button_clear():global expressionexpression ""input_text.set(""…