SpringBoot 参数校验

准备工作

引入相关依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Bean Validation 中的约束

约束 说明
@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(value) 被注释的元素必须符合指定的正则表达式

Hibernate Validator 附加的约束

约束 说明
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内

代码中校验

spring 中可以直接从 aop 容器中获取

1
2
@Autowired
private Validator validator;

通过工厂方法获取

1
2
3
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<DemoDTO>> violations = validator.validate(dto);

@Validated 与@Valid 的简单对比说明

@Valid 注解与@Validated 注解功能大部分类似;两者的不同主要在于:

  • @Valid 属于 javax 下的,而@Validated 属于 Spring 下
  • @Valid 支持嵌套校验,而@Validated 不支持
  • @Validated 支持分组,而@Valid 不支持

普通方法校验参数

除了验证接口,我们还可以验证普通的方法,首先在需要验证的方法所在类上面增加注解 @Validated

1
2
3
4
5
6
@Validated
@Service
public class DemoService {
public void demo(@Valid DemoDTO dto) {
}
}

注意:

  • 基于 aop 机制,被验证的方法需要注册为组件
  • 只能在类上面增加注解@Validated,不能在单个方法上
  • 抛出的异常为 ConstraintViolationException,需要单独拦截,示例:
1
2
3
4
5
6
7
8
9
10
@ResponseBody
@ExceptionHandler(ConstraintViolationException.class)
public HttpResponse handle(ConstraintViolationException e) {
return HttpResponse.builder().code(500).msg(
e.getConstraintViolations()
.stream()
.findFirst()
.map(ConstraintViolation::getMessage)
.orElse("参数校验失败")).build();
}

嵌套验证

当我们要验证的类 DemoBO 有成员属性为 OtherObject,对 OtherObject 中的属性进行校验,需要在 DemoBO 的成员属性 OtherObject 之上加上 @Valid

1
2
3
4
5
6
@Data
public class DemoBO {

@Valid
private OtherObject otherObject;
}

分组校验

在很多时候,同一个模型可能会在多处被用到,但每处的校验场景又不一定相同(如:新增用户接口、修改用户接口,参数都是 User 模型,在新增时 User 中 name 字段不能为空,userNo 字段可以为空;在修改时 User 中 name 字段可以为空,userNo 字段不能为空)。我们可以用 groups 来实现:同一个模型在不同场景下,(动态区分)校验模型中的不同字段。

创建分组 Create 和 Update 继承 Default

继承 Default 并不是必须的。只是说,如果继承了 Default,那么@Validated(value = Create.class)的校验范畴就为【Create】和【Default】;如果没继承 Default,那么@Validated(value = Create.class)的校验范畴只为【Create】,而@Validated(value = {Create.class, Default.class})的校验范畴才为【Create】【Default】。

Default 组和无参构造机制类似,当没有指定分组时,会默认当前校验属于 Default 组,但是一旦主动给当前校验指定
了分组(如上图中的 name 字段,主动指定了属于 Create 组),那么就不会再额外指定属于 Default 组了。
当然,也可以画蛇添足的主动指定所属分组为 Default。

1
2
3
import javax.validation.groups.Default;

public interface Create extends Default{}
1
2
3
import javax.validation.groups.Default;

public interface Update extends Default{}

创建实体类 Student

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

@Data
public class Student{

@NotNull(groups=Update.class)
private Long id;

@NotEmpty(groups=Create.class)
private String name;

@NotEmpty
private Integer age;

@NotEmpty(groups=Default.class) // 等价于 @NotEmpty
private Integer gender;
}