优雅的参数校验(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";
}
- 这里的接口形式适合GET或Content-Type设置成application/x-www-form-urlencoded的Post的接口;
- 这里的@Valid也可以换成@Validated效果是一样的;
- 接口方法形参采用JavaBean形式(@RequestBody)如:
@GetMapping("test")
public String test(@Valid @RequestBody TestBean testBean) {
//TODO 自己的逻辑
return "success";
}
- 这里的接口形式适合Content-Type设置成application/json的Post的接口;
- 这里的@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提供(在上面的基础上做了扩展) |
下面讲下两者的区别:
- @Validated:作用在类上、方法、参数
- @Valid:方法、构造方法、参数、成员属性上
- @Validated支持分组:在接口形参上加上@Validated({A.class})(A是定义的一个分接口)则只会对相对应的XX类型的group起校验作用其他的分组则不会:
- @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上,才能实现嵌套校验。
四、最后
至此你就可以优雅灵活的完成对参数的校验了。
那校验过未通过抛出异常呢,异常信息那么长怎么处理呢,不要着急,敬请期待下期,《统一异常处理》。
更多资源:其实是白羊(https://gitee.com/zhanglinlu)