Spring Boot之实现JSR-303请求参数校验

2024-02-11 18:32

本文主要是介绍Spring Boot之实现JSR-303请求参数校验,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

请求参数的校验是很多新手开发非常容易犯错,或存在较多改进点的常见场景。比较常见的问题主要表现在以下几个方面:

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

所以,针对上面的问题,建议服务端开发在实现接口的时候,对于请求参数必须要有服务端校验以保障数据安全与稳定的系统运行。同时,对于参数的校验实现需要足够优雅,要满足逻辑易读、易维护的基本特点。

接下来,我们就在本篇教程中详细说说,如何优雅地实现Spring Boot服务端的请求参数校验。

JSR-303

在开始动手实践之前,我们先了解一下接下来我们将使用的一项标准规范:JSR-303

什么是JSR?

JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

JSR-303定义的是什么标准?

JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。

Bean Validation中内置的constraint

Hibernate Validator附加的constraint

在JSR-303的标准之下,我们可以通过上面这些注解,优雅的定义各个请求参数的校验。更多关于JSR的内容可以参与官方文档或参考资料中的引文[1]。

动手实践

已经了解了JSR-303之后,接下来我们就来尝试一下,基于此规范如何实现参数的校验!

准备工作

读者可以拿任何一个使用Spring Boot 2.x构建的提供RESTful API的项目作为基础。也可以使用Spring Boot 2.x基础教程:使用Swagger2构建强大的API文档中构建的实验工程作为基础,您可以通过下面仓库中的chapter2-2目录取得:

  • Github:https://github.com/dyc87112/SpringBoot-Learning/tree/2.x
  • Gitee:https://gitee.com/didispace/SpringBoot-Learning/tree/2.x

当然,您也可以根据前文再构建一个作为复习,也是完全没有问题的。

快速入门

我们先来做一个简单的例子,比如:定义字段不能为Null。只需要两步

第一步:在要校验的字段上添加上@NotNull注解,具体如下:

@Data
@ApiModel(description="用户实体")
public class User {@ApiModelProperty("用户编号")private Long id;@NotNull@ApiModelProperty("用户姓名")private String name;@NotNull@ApiModelProperty("用户年龄")private Integer age;}

第二步:在需要校验的参数实体前添加@Valid注解,具体如下:

@PostMapping("/")
@ApiOperation(value = "创建用户", notes = "根据User对象创建用户")
public String postUser(@Valid @RequestBody User user) {users.put(user.getId(), user);return "success";
}

完成上面配置之后,启动应用,并用POST请求访问localhost:8080/users/接口,body使用一个空对象,{}。你可以用Postman等测试工具发起,也可以使用curl发起,比如这样:

curl -X POST \http://localhost:8080/users/ \-H 'Content-Type: application/json' \-H 'Postman-Token: 72745d04-caa5-44a1-be84-ba9c115f4dfb' \-H 'cache-control: no-cache' \-d '{}'

不出意外,你可以得到如下结果:

{"timestamp": "2019-10-05T05:45:19.221+0000","status": 400,"error": "Bad Request","errors": [{"codes": ["NotNull.user.age","NotNull.age","NotNull.java.lang.Integer","NotNull"],"arguments": [{"codes": ["user.age","age"],"arguments": null,"defaultMessage": "age","code": "age"}],"defaultMessage": "不能为null","objectName": "user","field": "age","rejectedValue": null,"bindingFailure": false,"code": "NotNull"},{"codes": ["NotNull.user.name","NotNull.name","NotNull.java.lang.String","NotNull"],"arguments": [{"codes": ["user.name","name"],"arguments": null,"defaultMessage": "name","code": "name"}],"defaultMessage": "不能为null","objectName": "user","field": "name","rejectedValue": null,"bindingFailure": false,"code": "NotNull"}],"message": "Validation failed for object='user'. Error count: 2","path": "/users/"
}

其中返回内容的各参数含义如下:

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

请求的调用端在拿到这个规范化的错误信息之后,就可以方便的解析并作出对应的措施以完成自己的业务逻辑了。

尝试一些其他校验

在完成了上面的例子之后,我们还可以增加一些校验规则,比如:校验字符串的长度、校验数字的大小、校验字符串格式是否为邮箱等。下面我们就来定义一些复杂的校验定义,比如:

@Data
@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;}

发起一个可以出发nameageemail都校验不通过的请求,比如下面这样:

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/"
}

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

Swagger文档中的体现

可能有读者会问了,我的接口中是定了这么多。上一篇教程中,不是还教了如何自动生成文档么,那么对于参数的校验逻辑该如何描述呢?

这里要分两种情况,Swagger自身对JSR-303有一定的支持,但是支持的并那么完善,并没有覆盖所有的注解的。

比如,上面我们使用的注解是可以自动生成的,启动上面我们的实验工程,然后访问http://localhost:8080/swagger-ui.html,在Models不是,我们可以看到如下图所示的内容:

其中:nameage字段相比上一篇教程中的文档描述,多了一些关于校验相关的说明;而email字段则没有体现相关校验说明。目前,Swagger共支持以下几个注解:@NotNull@Max@Min@Size@Pattern。在实际开发过程中,我们需要分情况来处理,对于Swagger支自动生成的可以利用原生支持来产生,如果有部分字段无法产生,则可以在@ApiModelProperty注解的描述中他,添加相应的校验说明,以便于使用方查看。

番外:也许你会有这些疑问

当请求参数校验出现错误信息的时候,错误格式可以修改吗?

答案是肯定的。这里的错误信息实际上由Spring Boot的异常处理机制统一组织并返回的,我们将在后面的教程中详细介绍,Spring Boot是如何统一处理异常返回以及我们该如何定时异常返回。

spring-boot-starter-validation是必须的吗?

有读者之前问过,看到很多教程都写了还要引入spring-boot-starter-validation依赖,这个依赖到底是否需要?(本篇中并没有引入)

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>

其实,只需要仔细看一下spring-boot-starter-validation依赖主要是为了引入了什么,再根据当前自己使用的Spring Boot版本来判断即可。实际上,spring-boot-starter-validation依赖主要是为了引入下面这个依赖:

<dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>6.0.14.Final</version><scope>compile</scope>
</dependency>

我们可以看看当前工程的依赖中是否有它,就可以判断是否还需要额外引入。在Spring Boot 2.1版本中,该依然其实已经包含在了spring-boot-starter-web依赖中,并不需要额外引入,所以您在本文中找不到这一步。

这篇关于Spring Boot之实现JSR-303请求参数校验的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/700518

相关文章

Java使用Curator进行ZooKeeper操作的详细教程

《Java使用Curator进行ZooKeeper操作的详细教程》ApacheCurator是一个基于ZooKeeper的Java客户端库,它极大地简化了使用ZooKeeper的开发工作,在分布式系统... 目录1、简述2、核心功能2.1 CuratorFramework2.2 Recipes3、示例实践3

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

springboot security使用jwt认证方式

《springbootsecurity使用jwt认证方式》:本文主要介绍springbootsecurity使用jwt认证方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录前言代码示例依赖定义mapper定义用户信息的实体beansecurity相关的类提供登录接口测试提供一

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

基于SpringBoot实现文件秒传功能

《基于SpringBoot实现文件秒传功能》在开发Web应用时,文件上传是一个常见需求,然而,当用户需要上传大文件或相同文件多次时,会造成带宽浪费和服务器存储冗余,此时可以使用文件秒传技术通过识别重复... 目录前言文件秒传原理代码实现1. 创建项目基础结构2. 创建上传存储代码3. 创建Result类4.

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

Tomcat版本与Java版本的关系及说明

《Tomcat版本与Java版本的关系及说明》:本文主要介绍Tomcat版本与Java版本的关系及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Tomcat版本与Java版本的关系Tomcat历史版本对应的Java版本Tomcat支持哪些版本的pythonJ

springboot security验证码的登录实例

《springbootsecurity验证码的登录实例》:本文主要介绍springbootsecurity验证码的登录实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录前言代码示例引入依赖定义验证码生成器定义获取验证码及认证接口测试获取验证码登录总结前言在spring

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

springboot security快速使用示例详解

《springbootsecurity快速使用示例详解》:本文主要介绍springbootsecurity快速使用示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录创www.chinasem.cn建spring boot项目生成脚手架配置依赖接口示例代码项目结构启用s