优雅的参数校验(JSR-303的实现Hibernate-Validator)

1,082 阅读6分钟

优雅的参数校验(JSR-303的实现Hibernate-Validator)

日常求赞,感谢老板。

欢迎关注公众号:其实是白羊。干货持续更新中......

一、背景

在我们平时开发中,经常会对前台传给我们的参数进行校验,如:

@GetMapping("test")
public String test(String id) {
    if (id != null && id.trim() != "") {
        throw new RuntimeException("id不能为空");
    }
    //TODO some method
    return "haha";
}

单独一个还行,但要说来个十个八个需要校验的,而且还有邮箱什么的格式验证,又繁琐又重复。我心态就炸了。。。

那么怎么才能简单灵活而又不重复且优雅的解决这个问题呢?

二、方案

其实Java为我们提供了很多的Java规范提案(JSR),如我们接下来要使用的JSR-303。

JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,用来对参数的校验。但是这只是一个规范,我们不能直接使用。在规范的实现中我们常用的就是:Hibernate-Validator。接下来我们来体验一下。

三、Hibernate-Validator配合注解使用

1)引入依赖

<!-- springboot下web启动器已经依赖了这个包 -->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.0.Final</version>
</dependency>

2)使用

注解分为两类(先介绍一个简单实用,更多注解介绍见:4)注解):

一种,加在字段上如:

@Data
public class TestBean {
    private int id;

    /**
     * 这个注解保证name不为null且不为空格字符串,否则抛出异常
     */
    @NotBlank(message = "name不能为空")
    private String name;
}

这样就可以校验了吗?No,这样还不行哦,还要配合第二种注解@Valid/@Validated,加在接口参数对象上,如:

/**
 * 这里的@Valid也可以换成@Validated效果是一样的
 * @param testBean
 * @return
 */
@GetMapping("test")
public String test(@Valid TestBean testBean) {
    //TODO 自己的逻辑
    return "success";
}

如果这里的name为null或者空格字符串(如:"")那么就会抛出异常:org.springframework.validation.BindException

Field error in object 'testBean' on field 'name': rejected value []; codes [NotBlank.testBean.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [testBean.name,name]; arguments []; default message [name]]; default message [name不能为空]]

3)场景

下面总结几种能够起到校验作用的使用场景:

  • 接口方法形参采用JavaBean形式(就像上面的那个例子一样)如:

    @GetMapping("test")
    public String test(@Valid TestBean testBean) {
        //TODO 自己的逻辑
        return "success";
    }
    
  1. 这里的接口形式适合GET或Content-Type设置成application/x-www-form-urlencoded的Post的接口;

  2. 这里的@Valid也可以换成@Validated效果是一样的;

  • 接口方法形参采用JavaBean形式(@RequestBody)如:

    @GetMapping("test")
    public String test(@Valid @RequestBody TestBean testBean) {
        //TODO 自己的逻辑
        return "success";
    }
    
  1. 这里的接口形式适合Content-Type设置成application/json的Post的接口;

  2. 这里的@Valid也可以换成@Validated效果是一样的;

  • 接口方法形参采用非JavaBean形式,如:

    @GetMapping("test")
    public String test(@NotBlank(message = "name不能为空")String name) {
        //TODO 自己的逻辑
        return "success";
    }
    

    注意这种形式在参数前使用@Valid/@Validated经测试起不到校验作用,这种情况只能只能使用@Validated并注释在此方法所在的Controller类上才能起到作用。

4)注解

下面介绍下常用注解的如下:

  • 加载需要校验的字段上的(javax.validation提供)

    注解 含义(用法)
    @AssertTrue 用于boolean字段,该字段只能为true
    @AssertFalse 该字段的值只能为false
    @CreditCardNumber 对信用卡号进行一个大致的验证
    @DecimalMax(value) 只能小于或等于该值(必须是数字)
    @DecimalMin(value) 只能大于或等于该值(必须是数字)
    @Digits(integer=,fraction=) 验证是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
    @Email 检查是否是一个有效的email地址
    @Future 检查该字段的日期是否是属于将来的日期
    @Length(min=,max=) 检查所属的字段的长度是否在min和max之间(只能用于字符串)
    @Max(value) 该字段的值只能小于或等于该值(必须是数字)
    @Min(value) 该字段的值只能大于或等于该值(必须是数字)
    @NotNull 不能为null(字符串)
    @NotBlank trim()之后不能为empty(字符串)
    @NotEmpty 不能为null和empty,这里的空是指空字符串也可集合
    @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
    @Null 被注释的元素必须为 null
    @Past 检查该字段的日期是在过去
    @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
    @Range(min=,max=) 被注释的元素必须在合适的范围内
    @Size(min=, max=) 检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等
    @URL(protocol=,host,port) 检查是否是一个有效的URL,如果提供了protocol,host等,则该URL还需满足提供的条件

上面注解都包含message这个属性,在满足条件时异常输出的提醒就是message设置的值

  • 要想上面的字段必须在参数对象上加上

    注解 注意
    @Valid javax.validation提供
    @Validated spring提供(在上面的基础上做了扩展)

    下面讲下两者的区别:

    1. @Validated:作用在类上、方法、参数

    2. @Valid:方法、构造方法、参数、成员属性上

    3. @Validated支持分组:在接口形参上加上@Validated({A.class})(A是定义的一个分接口)则只会对相对应的XX类型的group起校验作用其他的分组则不会:

      • @NotBlank(group={A.class},message = "name不能为空")
      • @NotBlank(group={B.class,C.class},message = "name不能为空")
    4. @Valid可做嵌套:

      @Data
      public class TestBean {
          /**
           * 姓名 notblank
           */
          @NotBlank(message = "name不能为空")
          private String name;
          @Valid
          private List<TestBean2> beans; 
      }
      
      @Data
      public class TestBean2 {
          @NotNull(message = "age不能为null")
          private Integer age;
      }
      

      在接口形参上要校验TestBean且还要校验TestBean下对象成员变量TestBean2下的成员变量,那就只能使用@Valid标注在TestBean下成员变量beans上,才能实现嵌套校验。

四、最后

至此你就可以优雅灵活的完成对参数的校验了。

那校验过未通过抛出异常呢,异常信息那么长怎么处理呢,不要着急,敬请期待下期,《统一异常处理》。

更多资源:其实是白羊(gitee.com/zhanglinlu)

欢迎star

日常求赞

  • 如果你认为本文对你有帮助,还请「在看/转发/赞/star」,多谢
  • 如果你还发现了更好或不同的想法,还请在留言区不吝赐教,一起探讨交流修改,万分感谢

欢迎关注公众号:「其实是白羊」干货持续更新中......