目的
痛点及现状
代码中常常见到如下代码:
if (Objects.equal(0L ,repertory)){
return ApiResultMap.errorResult(-1 ,"操作数量不可为0") ;
}
这种参数校验写在模块里有如下缺点:
- 代码冗余
- 影响代码可读性
- 需要通过注释来知道每个入参的约束是什么。
- 每个程序员做参数验证的方式不一样,参数验证不通过抛出的异常也不一样。
抛出问题:那么有没有一种方式可以简化代码呢?
JSR 303 - Bean Validation
Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API和Hibernate Validator。
- Bean Validation API是Java定义的一个验证参数的规范。
- Hibernate Validator是Bean Validation API的一个实现。
QUICK START
引入pom
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.3.1.Final</version> </dependency>
dto入参对象属性加入注解
`
@Data public class ValidDemo { @Size(min = 3, max = 12, message = “用户名必须的长度必须是3到12个字母之间”) @Pattern(regexp = “^[a-z]+$”, message = “用户名必须是a-z小字母”) private String name;@Size(min = 6, max = 6, message = “密码必须是6位数字”) @Pattern(regexp = “^[0-9]+$”, message = “密码必须是6位数字”) private String password;
@Range(min = 1, max = 9, message = “范围只能1到9”) private Integer range;
@NotNull(message = “邮箱不能为Null”) @Email(regexp = “(?:[a-z0-9!#$%&’+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&’+/=?^_`{|}~-]+)|\”(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])\”)@(?:(?:a-z0-9?\.)+a-z0-9?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])”,
message = "邮箱格式有误")
private String email;
}
3. controller方法入参加入校验(@Valid)
@GetMapping(“/validdemo”) public Map<String,Object> demo(@Valid ValidDemo validDemo){ return ApiResultMap.successResult(validDemo); }
4. 精简出参,加入全局异常处理
@ExceptionHandler(value = { BindException.class }) public Map<String, Object> validationException(BindException ex) { log.error(ex.getBindingResult().getFieldError().getDefaultMessage()); return ApiResultMap.errorResult(ex.getBindingResult().getFieldError().getDefaultMessage()); }
5. 测试结果:
$curl http://localhost:8080/validdemo?email=xxxxx
{ “message”: { “code”: -1, “message”: “邮箱格式有误” } }
**另一种方式,使用 spring 的 @Validated 注解:**
1. 配置 MethodValidationPostProcessor
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
2. 使用@Validated注解:
@Validated @RestController public class DemoController {}
3. 方法上加上校验
@GetMapping(“/validdemo3”) public Map<String,Object> demo3(@NotNull String str, @NotNull @Range(min = 0, max = 10) Integer a){ return ApiResultMap.successResult(str + a); }
4. 测试
$curl http://localhost:8080/validdemo?str=1&a=15 { “message”: { “code”: 202, “message”: “需要在0和10之间” } } `
应用场景
java程序员张三和ios程序员李四开发某一需求,明天就要demo了,今天得抓紧联调。
李四:三哥,地址发下,调试绑卡。
张三启动服务…
李四:三哥,帮忙看下,这个接口报错 -1,看下上面错误呗。
张三打开控制台,看了一下日志与排查,数据库报错,身份证字段没传导致插入身份证为空报错。
张三:李四你身份证没传。…张三一边埋怨着一边加入一行if else,同时在思考别的接口是不是也有这种情况,也给加上if else.
没过一会儿,李四:三哥,帮我看下有报错-1了。
张三又去查看,同样的,这次是银行卡号没传。张三重复着以上操作可没过一会儿李四又….
就这样一天过去,伴随着晚霞下班的张三,心情却没那么高兴…
抛出问题:是什么导致张三忙碌一天却觉得碌碌无为?是道德的沦丧还是人性的丧失?
如果张三整合Bean Validation的话,可能就没有那么不愉快了。他只需要一次在入参model里加入校验,之后在控制器的通过轻松的 @Valid 注解,就可以省去李四重复的提问,也省去的检查其他接口是否也需要添加代码校验,代码又可以少些几行了,何乐而不为。
常用注解
Bean Validation 中内置的 constraint:
- @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 附加的 constraint:
- @Email 被注释的元素必须是电子邮箱地址
- @Length 被注释的字符串的大小必须在指定的范围内
- @NotEmpty 被注释的字符串的必须非空
- @Range 被注释的元素必须在合适的范围内
优势
- 代码整洁
- 代码可读性强
- 解决不同开发者的不同的校验方式
总结
推荐使用 Bean Validation 的方式解决业务中参数校验; 这里只给出了一些基本的参数校验constraint,在实际业务中可根据业务情形自定义业务constraint。