一、介绍

Spring Validation 验证框架与 javax 都提供了对数据的校验功能

Spring Validation 验证框架对参数的验证机制提供了@Validated(Spring’s JSR-303 规范,是标准 JSR-303 的一个变种),javax 提供了@Valid(标准 JSR-303 规范),配合 BindingResult 可以直接提供参数验证结果。

在检验 Controller 的入参是否符合规范时,使用@Validated 或者@Valid 在基本验证功能上没有太多区别。但是在分组、注解地方、嵌套验证等功能上两个有所不同。

1、Validated

  • 分组:提供分组功能,可在入参验证时,根据不同的分组采用不同的验证机制。
  • 可注解位置:可以用在类型、方法和方法参数上。但是不能用在成员属性上
  • 嵌套验证:用在方法入参上无法单独提供嵌套验证功能;不能用在成员属性上;也无法提供框架进行嵌套验证;能配合嵌套验证注解 @Valid 进行嵌套验证。

2、Valid

  • 分组:无分组功能
  • 可注解位置:可以用在方法、构造函数、方法参数和成员属性上(两者是否能用于成员属性上直接影响能否提供嵌套验证的功能)
  • 嵌套验证:用在方法入参上无法单独提供嵌套验证功能;能够用在成员属性上,提示验证框架进行嵌套验证;能配合嵌套验证注解@Valid 进行嵌套验证。

二、使用

依赖

SpringBoot 工程下依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>

使用

新建一个 User 实体类

1
2
3
4
5
6
7
@Data
public class User {
@NotNull(message = "{user.username.notnull}")
private String username;
@NotNull(message = "{user.password.notnull}")
private String password;
}

resources 目录下创建 ValidationMessages.properties 文件

1
2
user.username.notnull=用户名不能为空
user.password.notnull=密码不能为空

Controller 测试

1
2
3
4
5
6
7
8
9
@RestController
@RequestMapping(value = "/test")
public class DemoController {

@PostMapping("/addUser")
public String addUser(@Validated @RequestBody User user) {
return "用户名:" + user.getUsername() + ",密码:" + user.getPassword();
}
}

异常处理,如果校验的结果不对,默认会返回 400

使用 @ControllerAdvice,,最常见的是结合@ExceptionHandler 注解用于全局异常的处理。

@ControllerAdvice 是在类上声明的注解,不用任何的配置,只要把这个类放在项目中,Spring 能扫描到的地方。就可以实现全局异常的回调,一般用于配合@ExceptionHandler 注解标注的方法:实现捕获 Controller 中抛出的不同类型的异常,从而达到异常全局处理的目的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 对Controller进行增强,可以实现
// 1、全局异常处理
// 2、全局数据绑定
// 3、全局数据预处理
@RestControllerAdvice
public class ValidatorHandlerAdvice {
//捕获Controller中抛出的MethodArgumentNotValidException的异常
@ExceptionHandler(MethodArgumentNotValidException.class)
protected List<String> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
List<String> errors = new ArrayList<>();
List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
for (ObjectError error : allErrors) {
errors.add(error.getDefaultMessage());
}
return errors;
}
}

测试

三、分组校验

新建两个分组接口

UserGroup1

1
2
public interface UserGroup1 {
}

UserGroup2

1
2
public interface UserGroup2 {
}

修改 User 实体类

用户一组和二组都需要校验用户名是否为空,但是只有用户二组校验密码是否为空

1
2
3
4
5
6
7
@Data
public class User {
@NotNull(message = "{user.username.notnull}", groups = {UserGroup1.class, UserGroup2.class})
private String username;
@NotNull(message = "{user.password.notnull}", groups = UserGroup2.class)
private String password;
}

修改注解,指定分组信息为用户一组

1
2
3
4
5
6
7
8
@RestController
@RequestMapping(value = "/test")
public class DemoController {
@PostMapping("/addUser")
public String addUser(@Validated(UserGroup1.class) @RequestBody User user) {
return "用户名:" + user.getUsername() + ",密码:" + user.getPassword();
}
}

测试是否校验密码,密码为空但是没有校验

指定分组信息为用户二组

1
2
3
4
5
6
7
8
@RestController
@RequestMapping(value = "/test")
public class DemoController {
@PostMapping("/addUser")
public String addUser(@Validated(UserGroup2.class) @RequestBody User user) {
return "用户名:" + user.getUsername() + ",密码:" + user.getPassword();
}
}

用户名和密码都进行了校验

四、嵌套校验

1
2
3
4
5
6
public class Account {
@NotNull
// 使用 @Valid 进行嵌套校验
@Valid
List<User> userList;
}

五、校验注解

空检查

注解作用
@Null验证对象是否为 null
@NotNull验证对象是否不为 null, 无法查检长度为 0 的字符串
@NotBlank检查约束字符串是不是 Null,长度是否大于 0,只对字符串,且会去掉前后空格
@NotEmpty检查约束元素是否为 NULL 或者是 EMPTY

Booelan 检查

注解作用
@AssertTrue验证 Boolean 对象是否为 true
@AssertFalse验证 Boolean 对象是否为 false

长度检查

注解作用
@Size(min=,max=)验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=,max=)验证字符串长度是否在给定的范围之内

日期检查

注解作用
@Past检查该字段的日期是否在过去
@Future检查该字段的日期是否是属于将来的日期
@Pattern(regex=,flag=)被注释的元素必须符合指定的正则表达式

数值检查

建议使用在 Stirng,Integer 类型,不建议使用在 int 类型上

因为表单值为“”时无法转换为 int,但可以转换为 Stirng 为””,Integer 为 null

注解作用
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Range(min=,max=)被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction)验证字符串是否是符合指定格式的数字,interger 指定整数精度,fraction 指定小数精度
@Email验证是否是邮件地址,如果为 null,不进行验证,算通过验证
@Valid递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个 map,则对其中的值部分进行校验.(是否进行递归验证)