化繁为简,使用Hibernate Validator实现参数校验
前言
在之前的悦享校园的开发中使用了SSM框架,由于当时并没有使用参数参数校验工具,方法的入参判断使用了大量的if else语句,代码十分臃肿,因此最近在重构代码时,将框架改为SpringBoot后,引入了Hibernate Validator校验工具对参数进行优雅校验(SSM同样可用)。本文将通过实例来演示如何使用该框架。
环境配置
JDK 1.8
Spring Boot 2.7.12
导入依赖
<!--SpringBoot 2.3开始,校验包单独组件,需要手动引入 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
基础校验
在正式开始之前,需要先对以下的内容作以了解,方便后续的学习。
校验注解
Hibernate Validator,提供了丰富的校验注解来供我们使用,这里列举一些比较常用的:
| 注解 | 解释 |
| @Null | 验证对象是否为空 |
| @NotNull | 验证对象是否为非空 |
| @NotBlank | 用于String对象,验证字符串不为空且trim()后长度大于0 |
| @Length | 验证对象的长度是否符合 |
| @Size | 验证对象长度是否在给定的范围之内(用于Array,Collection,Map,String) |
| @Min | 入参对象的值不能小于该值(用于Number和String) |
| @Max | 入参对象的值不能大于该值(用于Number和String) |
| @Digits | 验证number和string的构成是否合法 |
| @Past | 验证date和calendar对象是否在当前时间之前 |
| @Future | 验证date和calendar对象是否在当前时间之后 |
| @Pattern | 验证是否符合正则表达式规则 |
| 验证邮箱 | |
| @Positive | 验证输入的对象是否非负数 |
| @Range | 验证输入的对象的值是否在指定范围内 |
参数绑定
@PathVariable
该注解用于接收具有Restful风格的参数,如/api/v1/1001,最终userId的值为1001。
如下代码中,使用name属性可以指定GetMapping中的id名称与之对应,从而可以自定义参数名称userId,而不是使用默认名称id
@GetMapping("/v1/{id}")
public void getMsg(@PathVariable(name = "id") Integer userId){}
@RequestParam
该注解用于接收查询参数,如/api/v1/product?user="123",则user的值为123。该注解也可用于接收form-data类型的数据。
当在参数前使用@RequireParam时,当请求该方法时,对应的参数必须存在,否则会引发异常,可使用@RequireParam(required = false)指明该参数非必须,该注解在入参为null时可提供默认值。
@GetMapping("/v1/product")
public void getMsg(@RequestParam String user){}
@RequestBody
该注解用于接收JSON格式的数据,如请求为{"name":123,"age":18},需要有对应的实体类作为映射。
@PostMapping("/v1/user")
public void getMsg(@RequestBody User user){}
@Validated
该注解可以作用于类、方法、参数上,用于开启参数校验。
@Valid
该注解可以作用于方法、参数、字段、构造器上,同样可以用于参数校验。
单参校验
将@Validated注解用于类上,可以不用在每个方法的参数上重复开启校验,使用前面提的基础校验即可对参数作约束。
校验方法
@RestController
@RequestMapping("/v1/hello")
@Slf4j
@Validated
public class HelloController {/*** GetMapping 请求* @param name 用户名称* @param uid 用户uid* @return*/@GetMapping("/{id}")public ResultDataVO getMsg(@RequestParam String name,@Max(value = 10,message = "最大值不能超过10")@PathVariable(name = "id") int uid) {log.info("访问hello下的msg方法。");String result = "Hello,"+name+" id "+uid;return ResultDataVO.success(result);}
}
请求参数
响应结果

由于我们没有给String参数赋值,因此默认为空字符串。但由于在uid上使用了@Max注解,因此当入参为100时便会出现异常,而返回的JSON格式为全局的响应处理,在后续的文章会提到。
对象校验
当我们在使用中,往往会遇到多个参数的情况,由于get请求对参数长度有限制,且参数会拼接在URL中, 因此对于此场景我们一般使用对象的方式来接收参数,而对于我们的参数也将传入也将用JSON格式。
由于使用JSON格式传参,将使用@RequestBody注解,除此之外还要使用@Validated或@Valid注解,关于两者的选择,只需要记住分组校验使用@Validated,否则二者均可。
即使在类上添加了@Validated注解,此处仍然需要以上两个注解之一,否则参数校验将无效。
校验方法
@RestController
@RequestMapping("/v1/hello")
@Slf4j
@Validated
public class HelloController {/*** 对象校验示例* @param user* @return */@PostMapping("/msg")public ResultDataVO(@RequestBody @Valid UserDTO user){return ResultDataVO.success(user.toString());}
}
UserDTO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {/*** id*/@Min(value = 1,message = "id值不合法")@Positiveprivate Integer id;/*** 昵称*/@NotBlank(message = "用户名不能为空")@Length(min = 4,max=10,message = "用户名必须在4-10个字符之间")private String username;/*** 年龄*/@Range(min = 1,max=120,message = "年龄不在合法范围内")@NotNull(message = "age不能为空")private Integer age;
}
上述代码中,参数为UserDTO对象,其中包含三个待校验的字段,由于未涉及到分组校验,因此校验方法中参数前使用@Valid注解(@Validated注解亦可),注意入参的字段名称需要和UserDTO中的名称一致。
请求参数
响应结果

分组校验
假设有这样的场景,在用户登录和修改密码中,两者分别需要如下参数,登录时需要用户名、密码,而修改时需要密码、新密码,为了方便管理,只使用了一个DTO类来存放上述的字段,此时由于这些参数需要在不同的场景下校验,而加上基础校验的注解会导致所有参数均被校验,面对此问题,我们将使用分组校验来解决。
可以看到如何代码中,均是使用了@Validate注解,且注解中含有一个分组接口,使用该接口可以帮助我们告知程序该对那些参数进行校验。
校验方法
@RestController
@Slf4j
@RequestMapping("/local/auth")
@Validated
public class LocalAuthController {@Autowiredprivate LocalAuthService localAuthService;/*** 用户登录接口* @param localAuthDTO 账号对象* @return*/@PostMapping("/login")LocalAuthVO loginCheck(@RequestBody @Validated(AuthCheckSequence.class) LocalAuthDTO localAuthDTO){return localAuthService.userLoginCheck(localAuthDTO);}/*** 用户修改密码接口* @param localAuthDTO 账号对象* @return*/@PutMapping("/password")ResultDataVO userChangePassword(@RequestBody@Validated(AuthChangeSequence.class)LocalAuthDTO localAuthDTO){return localAuthService.userModifyPassword(localAuthDTO);}
}
分组接口
AuthChangeSequence
/*** 分组校验,用户登录*/public interface AuthCheckSequence {}
AuthCheckSequence
/*** 分组校验,密码修改*/
public interface AuthChangeSequence {}
LocalAuthDTO
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class LocalAuthDTO {/*** 用户名*/@NotBlank(message = "用户名不能为空",groups = AuthCheckSequence.class)@Pattern(regexp = "^[a-zA-Z0-9]{3,8}$", message = "用户名必须由3-8个字母或数字组成",groups = AuthCheckSequence.UsernameCheck.class)private String username;/*** 密码*/@NotBlank(message = "密码不能为空",groups = AuthCheckSequence.class)@Size(min = 6,max = 18, message = "密码长度必须在6到18位之间",groups = AuthCheckSequence.PasswordCheck.class)private String password;/*** 新密码,修改密码时使用*/@NotBlank(message = "新密码不能为空",groups = AuthChangeSequence.class)@Size(min = 6,max = 18, message = "新密码长度必须在6到18位之间",groups = AuthChangeSequence.ChangePassword.class)private String newPassword;
}
请求参数

响应结果

从上图所示请求结果可以看出,使用@Validated注解+分组校验接口后,将只对该参数中标明了对应分组接口的字段进行校验,并不会对全部参数进行校验。
顺序校验
由于用户人数增多,我们将在对用户进行分组,将在LocalAuthDTO中引入新的字段,而我们所期望的是能够按照用户类型,用户名,密码的顺序来校验,而不是像之前对象校验最终显示的结果那样,随机进行校验并返回结果,因此需要使用在分组校验的基础上做一些改进,使其可以按照我们所指定的顺序执行校验,以前面登录为例:
校验方法
@RestController
@Slf4j
@RequestMapping("/local/auth")
@Validated
public class LocalAuthController {@Autowiredprivate LocalAuthService localAuthService;/*** 用户登录接口* @param localAuthDTO 账号对象* @return*/@PostMapping("/login")LocalAuthVO loginCheck(@RequestBody @Validated(AuthCheckSequence.class) LocalAuthDTO localAuthDTO){return localAuthService.userLoginCheck(localAuthDTO);}
}
AuthCheckSequence
通过在该分组接口中新建接口,并使用@GroupSequence注解中参数的顺序来实现校验参数的顺序。如下将标识使用AuthCheckSequence分组的参数将按照用户类型、用户名、密码的顺序进行校验。
@GroupSequence({AuthCheckSequence.UserTypeCheck.class, AuthCheckSequence.UsernameCheck.class, AuthCheckSequence.PasswordCheck.class})
public interface AuthCheckSequence {/*** 用户类型*/interface UserTypeCheck {};/*** 用户名校验*/interface UsernameCheck {};/*** 密码校验*/interface PasswordCheck {};
}
LocalAuthDTO
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class LocalAuthDTO {/*** 用户类型 1.用户 2.商家 3.管理员,目前仅支持用户和商家类型注册*/@NotNull(message = "用户类型不能为空",groups = AuthCheckSequence.UserTypeCheck.class)@Range(min = 1,max = 3,message = "用户类型不合法",groups = AuthCheckSequence.UserTypeCheck.class)private Integer userType;/*** 用户名*/@NotBlank(message = "用户名不能为空",groups = AuthCheckSequence.UsernameCheck.class)@Pattern(regexp = "^[a-zA-Z0-9]{3,8}$", message = "用户名必须由3-8个字母或数字组成",groups = AuthCheckSequence.UsernameCheck.class)private String username;/*** 密码*/@NotBlank(message = "密码不能为空",groups = AuthCheckSequence.PasswordCheck.class)@Size(min = 6,max = 18, message = "密码长度必须在6到18位之间",groups = AuthCheckSequence.PasswordCheck.class)private String password;
}
请求参数

响应结果

相关文章:
化繁为简,使用Hibernate Validator实现参数校验
前言 在之前的悦享校园的开发中使用了SSM框架,由于当时并没有使用参数参数校验工具,方法的入参判断使用了大量的if else语句,代码十分臃肿,因此最近在重构代码时,将框架改为SpringBoot后,引入了Hibernate V…...
【Qt】多线程
线程创建 自定义线程类 #ifndef CUSTOMETHREAD_H #define CUSTOMETHREAD_H#include <QObject> #include <QThread> #include "add.h"class CustomeThread : public QThread {Q_OBJECT public:// Bind the thread kernel function.explicit CustomeThre…...
腾讯云GPU服务器GN7实例NVIDIA T4 GPU卡
腾讯云GPU服务器GN7实例搭载1颗 NVIDIA T4 GPU,8核32G配置,系统盘为100G 高性能云硬盘,自带5M公网带宽,系统镜像可选Linux和Windows,地域可选广州/上海/北京/新加坡/南京/重庆/成都/首尔/中国香港/德国/东京/曼谷/硅谷…...
3. 爬取自己CSDN博客列表(自动方式)(分页查询)(网站反爬虫策略,需要在代码中添加合适的请求头User-Agent,否则response返回空)
文章目录 步骤打开谷歌浏览器输入网址按F12进入调试界面点击网络,清除历史消息按F5刷新页面找到接口(community/home-api/v1/get-business-list)接口解读 撰写代码获取博客列表先明确返回信息格式json字段解读 Apipost测试接口编写python代码…...
利用HTTP代理实现请求路由
嘿,大家好!作为一名专业的爬虫程序员,我知道构建一个高效的分布式爬虫系统是一个相当复杂的任务。在这个过程中,实现请求的路由是非常关键的。今天,我将和大家分享一些关于如何利用HTTP代理实现请求路由的实用技巧&…...
深度学习(36)—— 图神经网络GNN(1)
深度学习(36)—— 图神经网络GNN(1) 这个系列的所有代码我都会放在git上,欢迎造访 文章目录 深度学习(36)—— 图神经网络GNN(1)1. 基础知识2.使用场景3. 图卷积神经网…...
深入理解JVM——垃圾回收与内存分配机制详细讲解
所谓垃圾回收,也就是要回收已经“死了”的对象。 那我们如何判断哪些对象“存活”,哪些已经“死去”呢? 一、判断对象已死 1、引用计数算法 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加一&…...
基于SSH框架实现的管理系统(包含java源码+数据库)
资料下载链接 介绍 基于SSH框架的管理系统 简洁版 ; 实现 登录 、 注册 、 增 、 删 、 改 、 查 ; 可继续完善增加前端、校验、其他功能等; 可作为 SSH(Structs Spring Hibernate)项目 开发练习基础模型…...
图像识别代做服务:实现创新应用的新契机
导言: 随着人工智能和图像处理技术的不断进步,图像识别已经成为了许多领域中的关键应用。然而,图像识别技术的开发和应用往往需要庞大的团队和大量的资源。这就是为什么图像识别代做服务正在崭露头角。本文将探讨图像识别代做服务如何成为实现…...
Coreutils工具包,Windows下使用Linux命令
之前总结过两篇有关【如何在Windows系统下使用Linux的常用命令】的文章: GnuWin32,Windows下使用Linux命令 UnxUtils工具包,Windows下使用Linux命令 今天再推荐一个类似的工具包Coreutils 一、简介 GNU core utilities是GNU操作系统基本…...
神经网络基础-神经网络补充概念-13-python中的广播
概念 在 Python 中,广播(Broadcasting)是一种用于在不同形状的数组之间执行二元操作的机制。广播允许你在不显式复制数据的情况下,对不同形状的数组进行运算。这在处理数组的时候非常有用,尤其是在科学计算、数据分析…...
HDFS原理剖析
一、概述 HDFS是Hadoop的分布式文件系统(Hadoop Distributed File System),实现大规模数据可靠的分布式读写。HDFS针对的使用场景是数据读写具有“一次写,多次读”的特征,而数据“写”操作是顺序写,也就是…...
css学习2(利用id与class修改元素)
1、id选择器可以为标有特定id的html元素指定特定的样式。 2、选择器以#开头,后跟某id的属性值。 3、class选择器用于描述一组元素的样式,class可以在多个元素使用。 4、类选择器用.选择。 5、指定特定的元素使用class。 6、元素的多个类用空格分开&…...
wsl2(debian)安装python的不同版本例如3.8
要在Debian上安装 Python 3.8,可以按照以下步骤操作: 1.确保你的 Debian 系统已经更新到最新版本,可以使用以下命令更新: sudo apt update sudo apt upgrade2.安装 Python 3.8 的依赖项,以及构建 Python 时需要的工具…...
Python教程(9)——Python变量类型列表list的用法介绍
列表操作 创建列表访问列表更改列表元素增加列表元素修改列表元素删除列表元素 删除列表 在Python中,列表(list)是一种有序、可变的数据结构,用于存储多个元素。列表可以包含不同类型的元素,包括整数、浮点数、字符串等…...
springboot+VUE智慧公寓管理系统java web酒店民宿房屋住宿报修信息jsp源代码
本项目为前几天收费帮学妹做的一个项目,Java EE JSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 springbootVUE智慧公寓管理系统 系统有2权限…...
神经网络基础-神经网络补充概念-36-dropout正则化
概念 Dropout 是一种常用的正则化技术,用于减少深度神经网络中的过拟合问题。它在训练过程中随机地将一部分神经元的输出置为零,从而强制模型在训练过程中学习多个独立的子模型,从而减少神经元之间的依赖关系,提高模型的泛化能力…...
Go语言基础之变量和常量
标识符与关键字 标识符 在编程语言中标识符就是程序员定义的具有特殊意义的词,比如变量名、常量名、函数名等等。 Go语言中标识符由字母数字和_(下划线)组成,并且只能以字母和_开头。 举几个例子:abc, _, _123, a123 关键字 关…...
Spring Boot 项目实现 Spring AOP
【注】实现在SpringBoot项目中,同时给两个类的方法添加AOP前置通知 1、创建一个SpringBoot项目 2、创建两个目标类和方法 package com.tqazy.learn_spring_project.spring_aop;import org.springframework.stereotype.Service;/*** ClassName SpringAopUserServi…...
Baumer工业相机堡盟工业相机如何通过BGAPISDK设置相机的固定帧率(C#)
Baumer工业相机堡盟工业相机如何通过BGAPI SDK设置相机的固定帧率(C#) Baumer工业相机Baumer工业相机的固定帧率功能的技术背景CameraExplorer如何查看相机固定帧率功能在BGAPI SDK里通过函数设置相机固定帧率 Baumer工业相机通过BGAPI SDK设置相机固定帧…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
