Spring Boot 参数校验 hibernate-validator @Valid 与 @Validated

3,733 阅读4分钟

Spring Boot 参数校验 hibernate-validator @Valid 与 @Validated

在 Java 的 JSR303 标准中声明了 @Valid 该类参数校验接口,而 hibernate-validator 则是其中一个实现之一,它提供了一套比较完善、便捷的验证方式,并集成到了 spring-boot-starter-web 包中。

快速上手

创建查询对象 UserQuery

@Data
public class ValidDemoQuery {

    private String name;

    @Pattern(regexp = "admin|member", message = "角色必须为admin或member")
    private String role;

    @Min(value = 1, message = "年龄必须大于1岁!")
    private String age;

}

GET 接口对查询对象进行验证

@RestController
@RequestMapping("demo/valid")
@Payload
public class ValidDemoController {

    @GetMapping
    public String valid(@Valid ValidDemoQuery query) {
        return "success";
    }

}

执行请求

curl http://127.0.0.1:8080/deepexi-dubbo/demo/valid?age=0&role=developer

# 错误信息
角色必须为admin或member
年龄必须大于1岁!

校验模式

普通模式(默认):在普通模式下,会校验完所有的属性,然后返回所有验证失败信息。

快速失败模式:只要有一个验证失败,则返回。切换为快速失败模式可参考下面代码:

@Configuration
public class ValidatorConfiguration {
    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        return validator;
    }
}

校验失败处理

在使用 @Valid 注解的参数后可以紧跟着一个 BindingResult 类型的参数,用于获取校验结果,否则 Spring 会在校验不通过时抛出异常。

@GetMapping
public String valid(@Valid ValidDemoQuery query, BindingResult result) {
  if (result.hasErrors()) {
    for (FieldError fieldError : result.getFieldErrors()) {
      System.out.println(fieldError.getDefaultMessage());
    }
    return "fail";
  }
  return "success";
}

如果有多个参数需要进行校验,可以添加多个 @Valid 与 BindingResult。如:

public String valid(@Valid ValidDemoQuery query1, BindingResult result1,@Valid ValidDemoQuery query2, BindingResult result2){}

嵌套校验

对象内部包含另一个对象作为属性,属性上加 @Valid,可以验证作为属性的对象内部的验证。

@Data
public class ValidDemoQuery {

    private String name;

    @Valid
    private RoleDemoQuery role;

    @Min(value = 1, message = "年龄必须大于1岁!")
    private String age;

    @Data
    public class RoleDemoQuery {

        @Pattern(regexp = "admin|member", message = "角色必须为admin或member")
        private String role;

    }

}

@Validated

Spring Validation 验证框架对参数校验提供了 @Validated 支持,它还可以与 JSR303 的 @Valid 同时使用,提供更丰富的参数校验。

并提供了一个分组功能,可以在入参验证时,根据不同分组采用不同的验证机制。

对入参基本数据类型校验

@RestController
@RequestMapping("demo/valid")
@Payload
@Validated
public class ValidDemoController {

    @GetMapping
    public String valid(@Min(value = 1,message = "年龄必须大于1") Integer age) {
        return "success";
    }

}

执行请求

curl http://127.0.0.1:8080/deepexi-dubbo/demo/valid?age=0

# 错误信息
test.age: 年龄必须大于1

分组校验

通过注解的 groups 属性可以指定该校验属于一个或多个分组

@Data
public class ValidDemoQuery {

    private String name;

    @Pattern(regexp = "admin|member", message = "角色必须为admin或member", groups = {Role.class})
    private String role;

    @Min(value = 1, message = "年龄必须大于1岁!")
    private String age;

    public interface Role {}

}

仅在 Role 分组下,role 字段的参数校验才会起效

@GetMapping
public String valid(@Validated(ValidDemoQuery.Role.class) ValidDemoQuery query, BindingResult result) {
  if (result.hasErrors()) {
    for (FieldError fieldError : result.getFieldErrors()) {
      System.out.println(fieldError.getDefaultMessage());
    }
    return "fail";
  }
  return "success";
}

常用注解

注解 作用
@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=) 被注释的元素必须符合指定的正则表达式
@NotBlank(message =) 验证字符串非 null,且长度必须大于 0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内