SpringBoot 使用validation数据校验-超级详细 - 第406篇
今天这节呢,我们主要是来介绍spring-boot-starter-validation,请跟着师傅来看看此玩意是何方神圣。
Spring Boot validation系列:
(1)✅《SpringBoot 使用validation数据校验-超级详细超级多干货》
(2)《SpringBoot 使用validation数据校验之分组校验怎么玩?·分组还有这么多的讲究》
(3)《SpringBoot 使用validation数据校验之自定义校验注解·源码分析+实例》
(4)《SpringBoot 使用validation数据校验之国际化问题怎么搞?》满满的干货
这一节我们先来看看《SpringBoot 使用validation数据校验-超级详细超级多干货》。
一、spring-boot-starter-validation是什么东东?
1.1 spring-boot-starter-validation概述
spring-boot-starter-validation可以用来校验SpringMVC 的入参,也就是可以用来校验参数的合理性。
1.2 spring-boot-starter-validation是什么?
spring-boot-starter-validation是快速使用validation的stateter,实际上是通过Hibernate Validator使用 Java Bean Validation。
我们来验证一下这一段话,点击spring-boot-starter-validation,进入到它的依赖包信息:
到这里我们就可以看到了spring-boot-starter-validation是使用了Hibernate Validator,它并没有自己进行实现。
具体要怎么使用呢?接下来我们通过一个小小的例子来进行说明。
二、spring-boot-starter-validation的小栗子
开发环境说明:
(1)操作系统:Mac OS
(2)Spring Boot 版本:2.5.5
(3)开发工具:IntelliJ IDEA
(4)JDK: 1.8
例子说明:IntelliJ IDEA
(1)创建一个Spring Boot项目;
(2)构建一个接口,此接口提供保存用户信息;
(3)使用validation进行数据的合理性校验;
2.1 准备工作
2.1.1 创建项目
首先使用IntelliJ IDEA创建一个项目spring-boot-validation-demo,引入基本的web依赖即可,版本选择2.5.5。
2.1.2 创建一个实体类
这个实体列就是用于来接收实体类的信息的:
package com.kfit.demo.springbootvalidationdemo.demo;
/**
* UserInfo实体类
*
* @author 悟纤「公众号SpringBoot」
* @date 2021-10-13
* @slogan 大道至简 悟在天成
*/
public class UserInfo {
private int id;
private String username;
private String password;
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
2.1.3 构建一个API请求访问
编写一个Controller:
package com.kfit.demo.springbootvalidationdemo.demo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* TODO
*
* @author 悟纤「公众号SpringBoot」
* @date 2021-10-13
* @slogan 大道至简 悟在天成
*/
@RestController
@RequestMapping("/userInfo")
public class UserInfoController {
@RequestMapping("/saveUserInfo")
public UserInfo saveUserInfo(UserInfo userInfo){
//save userInfo:将userInfo进行保存
//userInfoService.save(userInfo);
return userInfo;
}
}
2.1.4 启动应用进行测试
启动应用,访问如下地址进行测试:
http://127.0.0.1:8080/userInfo/saveUserInfo
结果:
这可了得,保存的数据一点都没有校验,啥都没填写,就保存到数据库了,这也不行呢。
对于数据的校验,那么就是我们接下来要进行讲解的,请接着往下看。
2.2 使用validation进行数据验证
2.2.1 引入依赖
要使用validation需要在pom.xml文件如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.2.2 属性上添加校验注解
在UserInfo类上添加校验注解:
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
@Length(min = 6,max = 20,message = "密码长度在6-20之间")
private String password;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不合理")
private String email;
2.2.3 在controller方法参数上添加校验注解
在Controller的方法的参数前面添加注解@Valid或者@Validated:
@RequestMapping("/saveUserInfo")
publicUserInfosaveUserInfo(@Valid UserInfo userInfo){
//save userInfo:将userInfo进行保存
//userInfoService.save(userInfo);
return userInfo;
}
2.2.4 重新启动测试
接下来就可以重新启动进行测试了:
不合理的请求:
http://127.0.0.1:8080/userInfo/saveUserInfo
在控制台可以看到:
合理的请求:
http://127.0.0.1:8080/userInfo/saveUserInfo?username=wuqian&email=aa@qq.com&password=123456
三、validation常用的校验注解
3.1 JSR提供的校验注解
JSR提供的校验注解:
@Null 被注释的元素必须为null
@NotNull 被注释的元素必须不为null
@AssertTrue 被注释的元素必须为true
@AssertFalse 被注释的元素必须为false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=,min=) 被注释的元素的大小必须在指定的范围内
@Digits(integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
3.2 Hibernate Validator提供的校验注解
HibernateValidator提供的校验注解:
@NotBlank(message=) 验证字符串非null,且trim后长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
3.3 小栗子
3.3.1 姓名长度必须在1-20之间
@NotNull(message = "姓名不能为空")
@Size(min = 1, max = 20, message = "姓名长度必须在1-20之间")
private String name;
3.3.2 年龄需要在10-150之间
@Min(value = 10, message = "年龄必须大于10")
@Max(value = 150, message = "年龄必须小于150")
private Integer age;
3.3.3 邮箱格式校验
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不合理")
private String email;
3.3.4 自定义邮箱正则
@Email(regexp = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$",message = "邮箱格式不合理")
private String email;
3.3.5 手机号格式校验
@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误")
@NotBlank(message = "手机号码不能为空")
private String phone;
四、进阶使用
4.1 有无@NotBlank的区别
看如下两段代码:
代码一:
@Length(min = 6,max = 20,message = "密码长度在6-20之间")
private String password;
代码二:
@NotBlank(message = "密码不能为空")
@Length(min = 6,max = 20,message = "密码长度在6-20之间")
private String password;
这两段代码的区别就在于方法上是否有@NotBlank的注解。
结果也是有区别的,当访问如下请求的话,
/userInfo/saveUserInfo?username=wuqian&email=aa@qq.com
代码一是可以正常访问的,代码二是就会提示:密码不能为空。
也就是代码一:入参password是可以不填写的,此时就会进行长度的校验了,可以正常的访问。
代码二:入参是必须要填写的,填写的规则必须满足长度要求。
4.2 异常处理
对于异常信息的处理,可以使用全局异常进行统一处理,具体查看文章:
《5. 全局异常捕捉【从零开始学Spring Boot】》
https://www.iteye.com/blog/412887952-qq-com-2291524
4.3 异常独自处理
如果你的异常信息想自己处理,而不想直接抛出,那么就添加一个参数BindingResult,具体使用如下:
@RequestMapping("/saveUserInfo")
public UserInfo saveUserInfo(@Valid UserInfo userInfo, BindingResult bindingResult){
if(bindingResult.hasErrors()){
System.out.println("有异常信息");
for (ObjectError error : bindingResult.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
return null;
}
//save userInfo:将userInfo进行保存
//userInfoService.save(userInfo);
return userInfo;
}
此时我们的代码会进入到我们的具体的业务代码里,具体怎么处理就你自己决定了。
4.4 单个参数校验
需要在类上添加@Validated注解,否则不会校验
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
/**
* TODO
*
* @author 悟纤「公众号SpringBoot」
* @date 2021-10-13
* @slogan 大道至简 悟在天成
*/
@RestController
@RequestMapping("/userInfo")
@Validated
public class UserInfoController {
@RequestMapping("/test")
public String test(@NotNull(message = "需要参数name") String name){
//save userInfo:将userInfo进行保存
//userInfoService.save(userInfo);
return name;
}
}
当参数平铺到方法入参中时,在这种情况下,必须在Controller类上标注@Validated注解,并在入参上声明约束注解(如@Min等)。
参数平铺到方法入参中。在这种情况下,必须在Controller类上标注@Validated注解,并在入参上声明约束注解(如@Min等)。
4.5 导入spring-boot-starter-web没有validation校验框架的解决办法
在低版本的Spring Boot,只需要导入spring-boot-starter-web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
就可以使用validation。在高版本的话,是需要手动导入spring-boot-starter-validation:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
总的来说:如果spring-boot版本小于2.3.x,spring-boot-starter-web会自动传入hibernate-validator依赖。如果spring-boot版本大于2.3.x,则需要手动引入依赖:
4.6 校验模式
上面例子中一次性返回了所有验证不通过的集合,通常按顺序验证到第一个字段不符合验证要求时,就可以直接拒绝请求了。Hibernate Validator有以下两种验证模式:
(1)普通模式(默认是这个模式)
普通模式(会校验完所有的属性,然后返回所有的验证失败信息)
(2)快速失败返回模式
快速失败返回模式(只要有一个验证失败,则返回)
配置为“快速失败返回模式”:
@Bean
public LocalValidatorFactoryBean getValidatorFactory() {
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.getValidationPropertyMap().put("hibernate.validator.fail_fast", "true");
return localValidatorFactoryBean;
}
或者:
- @Bean
- publicValidatorvalidator(){
- ValidatorFactory validatorFactory =Validation.byProvider( HibernateValidator.class )
- .configure()
- .failFast(true)
- //.addProperty("hibernate.validator.fail_fast", "true" )
- .buildValidatorFactory();
- Validator validator =validatorFactory.getValidator();
- return validator;
- }
4.7 嵌套调用/对象级联校验
前面的示例中,DTO类里面的字段都是基本数据类型和String类型。但是实际场景中,有可能某个字段也是一个对象,这种情况先,可以使用嵌套校验。比如,上面保存User信息的时候同时还带有Job信息。需要注意的是,此时DTO类的对应字段必须标记@Valid注解。
一句话说明就是:对象内部包含另一个对象作为属性,属性上加@Valid,可以验证作为属性的对象内部的验证。
4.8 @Valid和@Validated的区别
@Valid和@ Validated在基本用法上是没什么区别的,
@Valid 和 @Validated 都用来触发一次校验, @Valid 是 JSR 303规范的注解,
@Validated 是Spring 加强版的注解,在一些进阶的用法上就有区别了,具体是什么区别呢?我们下节揭晓!