数据校验是在平时的编码过程中常做的工作,在系统的各个层可能都要去实现一些校验逻辑,再去做业务处理。这些繁琐的校验与我们的业务代码在一块就会显得臃肿。而且这些校验通常是业务无关的。
Bean Validation 2.0(JSR 380)定义了用于实体和方法验证的元数据模型和 API,Hibernate Validator 是目前最好的实现,下面整理具体使用。
依赖
如果是 Spring Boot2.3 之前的项目,那么 spring-boot-starter-web 中就已经依赖 hibernate-validator 了
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
|
否则可以添加 hibernate-validator 依赖
1 2 3 4 5
| <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.17.Final</version> </dependency>
|
注解介绍
validator 内置注解
@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
被注释的元素必须在合适的范围内
@NotBlank
验证字符串非 null,且长度必须大于 0
注意:
- @NotNull 适用于任何类型被注解的元素必须不能与 NULL
- @NotEmpty 适用于 String Map 或者数组不能为 Null 且长度必须大于 0
- @NotBlank 只能用于 String 上面 不能为 null,调用 trim()后,长度必须大于 0
使用
模拟用户注册封装了一个 UserVO
当提交数据的时候如果使用以前的做法就是 IF ELSE 判断参数,使用 validator 则是需要增加注解即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Data @NoArgsConstructor @AllArgsConstructor public class UserVO {
@NotBlank(message = "用户名不能为空") @Email(message = "邮箱格式不正确") private String name;
@Size(min = 6, message = "密码不能少于6位") @NotBlank(message = "密码不能为空") private String password;
@NotBlank(message = "验证码不能为空") private String code; }
|
然后需要在 controller 方法体添加@Validated 不加@Validated 校验会不起作用
1 2 3 4 5 6 7 8 9 10 11 12 13
| @RestController public class UserAuthController {
@Autowired private UserAuthService userAuthService;
@PostMapping("/register") public ApiResponse<?> register(@Valid @RequestBody UserVO user) { userAuthService.register(user); return ApiResponse.ok(); } }
|
测试
生成一个符合校验的数据
1 2 3 4 5
| { "name": "dzgu@163.com", "password": 1234567, "code": "6319" }
|
返回结果
生成一个不符合 name 校验的数据
1 2 3 4 5
| { "name": "dzgu", "password": 1234567, "code": "6319" }
|
返回结果
这样是能校验成功,但是有个问题就是返回参数并不理想,前端也并不容易处理返回参数,所以我们添加一下参数校验的全局异常处理(https://www.yuque.com/ugdongzhou/kd5pz2/ecv4fp),然后添加一下全局统一返回参数这样比较规范。
添加全局异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @Slf4j @RestControllerAdvice public class ExceptionHandlerConfig {
@ExceptionHandler(value = BizException.class) public ApiResponse<?> errorHandler(BizException e) { log.error("业务异常:" + e); return ApiResponse.fail(e.getCode(), e.getMessage()); }
@ExceptionHandler(value = MethodArgumentNotValidException.class) public ApiResponse<?> errorHandler(MethodArgumentNotValidException e) { List<ObjectError> errors = e.getBindingResult().getAllErrors(); StringBuilder errorMessages = new StringBuilder(); errors.forEach(error -> errorMessages.append(error.getDefaultMessage()).append(";")); String s = errorMessages.toString(); log.error("参数校验异常:" + s); return ApiResponse.fail(ExceptionEnum.VALID_ERROR.getCode(), s);
}
@ExceptionHandler(value = Exception.class) public ApiResponse<?> errorHandler(Exception e) { log.error("系统异常:" + e); return ApiResponse.fail(ExceptionEnum.SYSTEM_ERROR); } }
|
此方法主要捕捉 MethodArgumentNotValidException 异常然后对异常结果进行封装,如果需要在自行添加其他异常处理。
添加完之后我们在看一下运行结果,调用接口返回: