Hibernate-Validator
Fluent-Validator
Valid
自己定义
思考
以前不同 Hibernate-Validator 的魅力,觉得设计了太多的注解。
后来发现使用注解组合的方式,其实拓展性很强。
而且支持自定义注解。
个人的 i18n 和 自定义注解就是模仿这个项目的。
Hibernate-Validator 入门
maven 引入
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.6.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
工具类
public class ValidateUtil {
/**
* 使用hibernate的注解来进行验证
*/
private static Validator validator = Validation
.byProvider(HibernateValidator.class).configure().failFast(true).buildValidatorFactory().getValidator();
public static <T> void validate(T t) {
Set<ConstraintViolation<T>> constraintViolations = validator.validate(t);
// 抛出检验异常
if (constraintViolations.size() > 0) {
final String msg = constraintViolations.iterator().next().getMessage();
throw new ParameterCheckException(msg);
}
}
}
常见注解
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(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的 constraint
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
springboot 整合
maven 引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.9.RELEASE</version>
</dependency>
springboot 包中是默认包含 hibernate-validator 的,不需要重复引入。
controller 定义
@Controller
@RequestMapping("/valid")
public class ValidController {
@RequestMapping("/user")
@ResponseBody
public String user(@Valid User user) {
return "ok";
}
}
POSTMAN 测试
使用 postman 模拟测试
通过的例子
不通过的例子
我们不传输 username
{
"timestamp": 1600652797208,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.validation.BindException",
"errors": [
{
"codes": [
"NotEmpty.user.username",
"NotEmpty.username",
"NotEmpty.java.lang.String",
"NotEmpty"
],
"arguments": [
{
"codes": [
"user.username",
"username"
],
"arguments": null,
"defaultMessage": "username",
"code": "username"
}
],
"defaultMessage": "用户名称不可为空",
"objectName": "user",
"field": "username",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotEmpty"
}
],
"message": "Validation failed for object='user'. Error count: 1",
"path": "/valid/user"
}
这里有一个小问题,错误信息提示过于详细,我们不可能直接传递给用户。
获取错误信息参数
BindingResult 参数。
BindingResult:主要是存储校验结果的数据,我们可以通过 BindingResult 来判断校验是否通过和获取校验错误提示信息。
@RequestMapping("/result")
@ResponseBody
public String result(@Valid User user, BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
String message = bindingResult.getFieldError().getDefaultMessage();
// 可以封装对象,此处偷懒,直接返回。
return message;
}
return "ok";
}
POSTMAN 测试
调整 POST: http://localhost:8080/valid/result
返回结果:
用户名称不可为空
继续改进
这里还有一个问题,每一次都需要加一个参数,还是很麻烦,完全没有体现出优势。
解决思路:通过 springboot 统一异常拦截进行处理。
package com.github.houbb.springboot.learn.validator.controller;
import com.github.houbb.springboot.learn.validator.dto.ValidVo;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* @author binbin.hou
* @since 1.0.0
*/
@ControllerAdvice
public class ValidInterceptor {
/**
* Validator 参数校验异常处理
*
* @param ex 异常
* @return 结果
*/
@ExceptionHandler(value = BindException.class)
public ResponseEntity<Object> handleMethodVoArgumentNotValidException(BindException ex) {
FieldError err = ex.getFieldError();
String message = "参数{".concat(err.getField()).concat("}").concat(err.getDefaultMessage());
ValidVo validVo = new ValidVo();
validVo.setCode("400");
validVo.setMsg(message);
return ResponseEntity.ok(validVo);
}
/**
* Validator 参数校验异常处理
*
* @param ex 异常
* @return 结果
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public ResponseEntity<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
FieldError err = ex.getBindingResult().getFieldError();
String message = "参数{".concat(err.getField()).concat("}").concat(err.getDefaultMessage());
return ResponseEntity.ok(RespUtil.fail(message));
}
}
- 报错提示
{
"code": "400",
"msg": "参数{username}用户名称不可为空"
}
参考资料
Spring Boot集成Hibernate Validator