Spring Boot中使用JSR-303实现请求参数校验

JSR-303是Java中的一个规范,用于实现请求参数校验。它定义了一组注解,可以应用于JavaBean的字段上,用于验证输入参数的合法性。下面是一些常用的JSR-303注解及其介绍:

@NotNull:用于验证字段值不能为null。
@NotEmpty:用于验证字符串字段不能为空。
@NotBlank:用于验证字符串字段不能为空,并且长度必须大于0。
@Min:用于验证数字字段的最小值。
@Max:用于验证数字字段的最大值。
@Size:用于验证字符串、集合或数组字段的长度或大小。
@Pattern:用于验证字符串字段是否匹配指定的正则表达式。
@Email:用于验证字符串字段是否符合Email格式。
@Range:用于验证数字字段的取值范围。
@Valid:用于嵌套验证,可以对对象中的字段进行递归验证。
其他如下图所示:
在这里插入图片描述

通过在JavaBean的字段上添加这些注解,可以在接收请求参数时进行自动校验。如果校验失败,可以通过异常处理机制来处理校验错误。

需要注意的是,JSR-303只提供了基本的验证注解,如果需要更复杂的校验逻辑,可以自定义注解或使用第三方库,如Hibernate Validator等。
Hibernate Validator附加的constraint:
在这里插入图片描述

至于我们为啥使用JSR-303来校验我们的参数,主要是为了解决我们在实际开发过程中,前后端的参数校验导致的问题。比如:

  1. 我们依靠前端框架解决参数校验,但是缺少服务器的参数校验,这种情况常见于需要同时进行开发前后端的时候,虽然程序的正常使用不会有问题,到那时开发者会忽略非正常的操作,比如绕过前端程序,直接模拟客户端请求,这样就绕过了我们对前端预设的各种限制,直击我们后端的数据访问接口,进而使得我们的后端系统存在严重的安全隐患。
  2. 大量的利用if/else语句嵌套实现,使得我们的校验逻辑晦涩难懂,并不利于我们对整个系统的维护。

JSR的定义标准

验证触发时机:

  • JSR-303校验标准定义了两个触发校验的时机:

    • 在对象被持久化之前(例如,保存到数据库之前)。
    • 在对象被修改之后(例如,更新数据库中的记录后)。
  • 校验约束注解:JSR-303标准提供了一组注解,可以用于对Java对象的属性进行校验。这些注解包括但不限于@NotNull@NotEmpty@Min@Max@Size@Pattern@Email 等。开发人员可以根据需求选择适当的注解来定义校验规则。

  • 校验组:JSR-303允许将校验规则分组,以便在特定情况下选择性地执行校验。开发人员可以为每个校验注解指定一个或多个校验组,然后在校验时选择要执行的校验组。

  • 嵌套校验:JSR-303允许对复杂对象进行嵌套校验,即在校验一个对象时,也会对其关联的其他对象进行校验。这样可以确保整个对象图的完整性和合法性。

  • 自定义校验:JSR-303还允许开发人员定义自己的校验注解和校验器,以满足特定的校验需求。通过实现 ConstraintValidator 接口来自定义校验器,并在自定义注解中使用该校验器。

  • 校验结果:JSR-303校验结果以校验异常的形式返回。当校验失败时,会抛出 ConstraintViolationException 异常,其中包含了校验失败的详细信息,例如校验失败的属性、校验失败的值、校验错误消息等。

接下来我们将围绕我们在Spring Boot的实体类中,使用JSR-303校验。值得注意的是,JSR-303校验我们一般都是对Java的实体类对象进行校验,主要检验在我们的实体类对象的属性上。

还是利用我们==>上一篇 <===的相关依赖文件,这篇我就不在重复导入相关依赖文件了。本篇着重文件主要正在User类和UserController,所用的依赖pom.xml文件和application.properties均与上篇一样,就不在重复描述了。

PS: 你可以看到我在依赖中均采用了lombok,但是我却并未使用这个玩意,原因在于,我使用了lombok

@ApiModel(description = "用户实体")
public class User {@ApiModelProperty("用户编号")private Long id;@NotNull //校验定义的字段不能为空@Size(min = 2, max = 5)@ApiModelProperty("用户姓名")private String name;@NotNull@Max(100)@Min(10)@ApiModelProperty("用户年龄")private Integer age;@NotNull@Email@ApiModelProperty("用户邮箱")private String email;public User(Long id, String name, Integer age, String email) {this.id = id;this.name = name;this.age = age;this.email = email;}public User() {}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", email='" + email + '\'' +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(age, user.age) && Objects.equals(email, user.email);}@Overridepublic int hashCode() {return Objects.hash(id, name, age, email);}
}
@Api(tags = "用户管理")
@RestController
@RequestMapping(value = "/users")     // 通过这里配置使下面的映射都在/users下
public class UserController {// 创建线程安全的Map,模拟users信息的存储static Map<Long, User> users = Collections.synchronizedMap(new HashMap<>());@GetMapping("/")@ApiOperation(value = "获取用户列表")public List<User> getUserList() {List<User> r = new ArrayList<>(users.values());return r;}@PostMapping("/")@ApiOperation(value = "创建用户", notes = "根据User对象创建用户")public String postUser(@Valid @RequestBody User user) {users.put(user.getId(), user);return "success";}@GetMapping("/{id}")@ApiOperation(value = "获取用户详细信息", notes = "根据url的id来获取用户详细信息")public User getUser(@PathVariable Long id) {return users.get(id);}@PutMapping("/{id}")@ApiImplicitParam(paramType = "path", dataType = "Long", name = "id", value = "用户编号", required = true, example = "1")@ApiOperation(value = "更新用户详细信息", notes = "根据url的id来指定更新对象,并根据传过来的user信息来更新用户详细信息")public String putUser(@PathVariable Long id, @RequestBody User user) {User u = users.get(id);u.setName(user.getName());u.setAge(user.getAge());users.put(id, u);return "success";}@DeleteMapping("/{id}")@ApiOperation(value = "删除用户", notes = "根据url的id来指定删除对象")public String deleteUser(@PathVariable Long id) {users.remove(id);return "success";}
}

接下来启动项目,当然启动类还是需要加:
@EnableSwagger2Doc这个注解

之后,我们可以通过使用相关工具比如Postman等测试工具发起,也可以使用curl发起,比如:
在这里插入图片描述

curl -X POST \http://localhost:8080/users/ \-H 'Content-Type: application/json' \-H 'Postman-Token: 114db0f0-bdce-4ba5-baf6-01e5104a68a3' \-H 'cache-control: no-cache' \-d '{"name": "abcdefg","age": 8,"email": "aaaa"
}'

得到相关信息:

{"timestamp": "2019-10-05T06:24:30.518+0000","status": 400,"error": "Bad Request","errors": [{"codes": ["Size.user.name","Size.name","Size.java.lang.String","Size"],"arguments": [{"codes": ["user.name","name"],"arguments": null,"defaultMessage": "name","code": "name"},5,2],"defaultMessage": "个数必须在2和5之间","objectName": "user","field": "name","rejectedValue": "abcdefg","bindingFailure": false,"code": "Size"},{"codes": ["Min.user.age","Min.age","Min.java.lang.Integer","Min"],"arguments": [{"codes": ["user.age","age"],"arguments": null,"defaultMessage": "age","code": "age"},10],"defaultMessage": "最小不能小于10","objectName": "user","field": "age","rejectedValue": 8,"bindingFailure": false,"code": "Min"},{"codes": ["Email.user.email","Email.email","Email.java.lang.String","Email"],"arguments": [{"codes": ["user.email","email"],"arguments": null,"defaultMessage": "email","code": "email"},[],{"defaultMessage": ".*","codes": [".*"],"arguments": null}],"defaultMessage": "不是一个合法的电子邮件地址","objectName": "user","field": "email","rejectedValue": "aaaa","bindingFailure": false,"code": "Email"}],"message": "Validation failed for object='user'. Error count: 3","path": "/users/"
}

其中返回名称的各参数含义如下:

timestamp:请求时间
status:HTTP返回的状态码,这里返回400,即:请求无效、错误的请求,通常参数校验不通过均为400
error:HTTP返回的错误描述,这里对应的就是400状态的错误描述:Bad Request
errors:具体错误原因,是一个数组类型;因为错误校验可能存在多个字段的错误,比如这里因为定义了两个参数不能为Null,所以存在两条错误记录信息
message:概要错误消息,返回内容中很容易可以知道,这里的错误原因是对user对象的校验失败,其中错误数量为2,而具体的错误信息就定义在上面的errors数组中
path:请求路径

从errors数组中各个错误明细,知道各个字段的defaultMessage,可以看到很清晰的错误描述。

浏览器访问:
http://localhost:8080/swagger-ui.html

在这里插入图片描述
在这里插入图片描述
我们可以看到校验的部分限制。

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

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

相关文章

【Qt之控件QKeySequenceEdit】分析及使用

描述 QKeySequenceEdit小部件允许输入一个QKeySequence。 该小部件允许用户选择一个QKeySequence&#xff0c;通常用作快捷键。当小部件获取焦点时&#xff0c;录制将开始&#xff0c;并在用户释放最后一个键后的一秒钟结束。 用户可以使用输入键盘来输入键序列。通过调用get…

【JAVA学习笔记】48 - 八大常用Wrapper类(包装类)

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter13/src/com/yinhai/wrapper_ 内的wrapper 一、包装类 1.针对八种基本定义相应的引用类型一包装类 2.有了类的特点&#xff0c;就可以调用类中的方法。 黄色背景的表示父类是Number 二、包装…

如何防范AI等技术带来的诈骗风险?从技术、法律、教育等多方面入手

文章目录 前言什么是AI诈骗案例案例一案例二 AI诈骗的特点如何预防和应对AI诈骗建议后记 前言 互联网是一把双刃剑&#xff0c;这是我们常说的一个问题。 随着人工智能技术的快速发展&#xff0c;AI诈骗成为当今社会面临的新兴威胁。不法分子利用人工智能技术&#xff0c;以更…

el-table(vue2中)滚动条被固定列盖住

一、项目场景&#xff1a; vue2 el-table 二、问题描述 1、现场图片&#xff1a; 2、全局css环境配置了滚动条高度为6px /* 全局滚动条配置 */ ::-webkit-scrollbar {width: 6px;height: 6px; }::-webkit-scrollbar-track {background-color: #f1f1f1; }::-webkit-scrollbar-…

【Git企业开发】第二节.Git 的分支管理

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;Git企业级开发 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&#xff0…

深入浅出排序算法之基数排序

目录 1. 前言 1.1 什么是基数排序⭐⭐⭐ 1.2 执行流程⭐⭐⭐⭐⭐ 2. 代码实现⭐⭐⭐ 3. 性能分析⭐⭐ 3.1 时间复杂度 3.2 空间复杂度 1. 前言 一个算法&#xff0c;只有理解算法的思路才是真正地认识该算法&#xff0c;不能单纯记住某个算法的实现代码&#xff01; 1.…

设计模式大赏(一):桥接模式,组合模式

设计模式大赏&#xff08;一&#xff09;&#xff1a;桥接模式&#xff0c;组合模式 导言 本篇文章是设计模式大赏中的第一篇文章&#xff0c;这个系列的文章中我们主要将介绍一些常见的设计模式&#xff0c;主要是我在看Android源码中发现用到的一些设计模式。本篇文章将主要…

实用搜索小技巧——站在巨人的肩膀上看世界

文章目录 1. 关于搜索效率2. 谷歌搜索语法2.1 “” 限定关键词2.2 intitle 限定标题2.3 限定关键词限定标题2.4 allintitle 标题多个关键词2.5 intext 限定内容关键词2.6 inurl 限定网址关键词2.7 site 限定网址来源2.8 imagesize 限定图片尺寸2.9 filetype 限定文件格式 3. in…

应用案例|基于三维机器视觉的曲轴自动化上下料应用方案

Part.1 项目背景 此案例服务对象为国内某知名大型汽车零部件制造工厂&#xff0c;该工厂有针对曲轴工件的自动化上下料需求。由于之前来料码放不规范&#xff0c;工件无序散乱摆放&#xff0c;上料节拍要求高&#xff0c;该工厂上下料效率极低。 Part.2 传统曲轴上下料存在的缺…

Android-登录注册页面(第三次作业)

第三次作业 - 登录注册页面 题目要求 嵌套布局。使用线性布局的嵌套结构&#xff0c;实现登录注册的页面。&#xff08;例4-3&#xff09; 创建空的Activity 项目结构树如下图所示&#xff1a; 注意&#xff1a;MainActivity.java文件并为有任何操作&#xff0c;主要功能集中…

自学爬虫—作业1—requests模块

视频&#xff1a; 要求&#xff1a; 肯德基地址查询&#xff0c;爬某个关键字&#xff0c;获取下面的所有page的信息&#xff0c;存到一个json或者txt。 代码&#xff1a; 关键点&#xff0c;&#xff08;1&#xff09;每一个ajax的请求第一个键值对就是所有获得的地址的总数…

探索低代码PaaS平台的优势与选择原因

PaaS是一种云产品&#xff0c;它为应用程序的开发和部署提供基础结构。它提供中间件、开发工具和人工智能来创建功能强大的应用程序&#xff0c;大多数PaaS服务都与存储和网络基础架构捆绑在一起&#xff0c;就像基础架构即服务&#xff08;IaaS&#xff09;一样&#xff0c;可…

微信小程序学习(03)

什么是生命周期函数 生命周期函数&#xff1a;是由小程序框架提供的内置函数&#xff0c;会伴随着生命周期&#xff0c;自动按次序执行。 生命周期函数的作用&#xff1a;允许程序员在特定的时间点&#xff0c;执行某些特定的操作。例如&#xff0c;页面刚加载的时候&#xff0…

信息系统项目管理师教程 第四版【第4章-信息系统管理-思维导图】

信息系统项目管理师教程 第四版【第4章-信息系统管理-思维导图】

Spring Boot和XXL-Job:高效定时任务管理

Spring Boot和XXL-Job&#xff1a;高效定时任务管理 前言第一&#xff1a;XXL-Job简介什么是XXL-job对比别的任务调度 第二&#xff1a; springboot整合XXL-job配置XXL-Job Admin拉取XXL-Job代码修改拉取的配置 配置执行器自己的项目如何整合maven依赖properties文件配置执行器…

基于哈里斯鹰算法的无人机航迹规划-附代码

基于哈里斯鹰算法的无人机航迹规划 文章目录 基于哈里斯鹰算法的无人机航迹规划1.哈里斯鹰搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用哈里斯鹰算法来优化无人机航迹规划。 …

【Linux】centos安装配置及远程连接工具的使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《微信小程序开发实战》。&#x1f3af;&#x1f3a…

Python 模块:创建、导入和使用

什么是模块&#xff1f; 将模块视为代码库。模块是一个包含一组函数的文件&#xff0c;您想要在应用程序中包含这些函数。 创建一个模块 要创建一个模块&#xff0c;只需将要包含在其中的代码保存在扩展名为 .py 的文件中&#xff1a; 示例&#xff1a;将以下代码保存在名为…

利用Excel支持JUnit参数化测试

在JUnit里面&#xff0c;可以使用CsvFileSource读取csv文件进行参数化测试&#xff0c;可是CSV文件不支持格式&#xff0c;编辑颇为麻烦&#xff0c;尤其是多次编辑&#xff0c;因此自然想到是否可以使用Excel文件&#xff0c;可以有各种格式&#xff0c;支持各类数据。 最新开…

【Java网络原理】 六

本文主要介绍了网络层的IP协议/NAT机制/IPv6的由来以及在数据链路层涉及到的以太网协议和DNS域名解析系统 一.网络层 1.IP协议 各个字段所表示的含义 >4位版本号 用来表示IP协议的版本&#xff0c;现在只有两个版本IPv4 &#xff0c;IPv6 >4位首部长度 IP报头可变&…