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")@Validatedpublic 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)快速失败返回模式

         快速失败返回模式(只要有一个验证失败,则返回)

         配置为“快速失败返回模式”:

@Beanpublic LocalValidatorFactoryBean getValidatorFactory() {    LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();    localValidatorFactoryBean.getValidationPropertyMap().put("hibernate.validator.fail_fast", "true");    return localValidatorFactoryBean;}

         或者:

  1. @Bean
  2. publicValidatorvalidator(){
  3.     ValidatorFactory validatorFactory =Validation.byProvider( HibernateValidator.class )
  4.             .configure()
  5.             .failFast(true)
  6.             //.addProperty("hibernate.validator.fail_fast", "true" )
  7.             .buildValidatorFactory();
  8.     Validator validator =validatorFactory.getValidator();
  9.     return validator;
  10. }

4.7 嵌套调用/对象级联校验

         前面的示例中,DTO类里面的字段都是基本数据类型和String类型。但是实际场景中,有可能某个字段也是一个对象,这种情况先,可以使用嵌套校验。比如,上面保存User信息的时候同时还带有Job信息。需要注意的是,此时DTO类的对应字段必须标记@Valid注解。

         一句话说明就是:对象内部包含另一个对象作为属性,属性上加@Valid,可以验证作为属性的对象内部的验证。

4.8 @Valid和@Validated的区别

         @Valid和@ Validated在基本用法上是没什么区别的,

         @Valid 和 @Validated 都用来触发一次校验, @Valid 是 JSR 303规范的注解,

@Validated 是Spring 加强版的注解,在一些进阶的用法上就有区别了,具体是什么区别呢?我们下节揭晓!


  1. 购买完整视频,请前往:http://www.mark-to-win.com/TeacherV2.html?id=287