内容不多耐心看完 建议手动操作一下即可 (文章 需要会swagger 也可以进行相应的忽略,springboot 整合 swagger链接:https://blog.csdn.net/weixin_40516924/article/details/103443607)
项目中,难免需要对参数 进行一些参数正确性的效验,这些小样出现在业务代码中,多次出现if效验数据使得业务代码显得臃肿,所以Hibernate validator框架刚好解决这些问题,可以很优雅的方式实现参数的效验,让业务代码和小样逻辑分开,不在编写重复的效验逻辑。hibernate Validator提供了JSR303规范中所有内置约束的实现,除此之外还有一些附加约束。
Bean Validator为JavaBean验证定义了相关数据模型和API。缺省的元数据是Java Annotations,通过XML可以对原有的元数据信息进行覆盖和扩展,Bean validation是一个运行时数据验证框架,在验证之后的验证的错误信息会被马上返回。
开发中,主要用于 接口api的入参效验 和 封装工具类 在代码中效验两种使用方法。
注意:在springboot中 不需要引入Hibernate Validator , 因为 在引入的 spring-boot-starter-web(springbootweb启动器)依赖的时候中,内部已经依赖了 hibernate-validator 依赖包
注解 | 适用的数据类型 | 说明 |
@AssertFalse | Boolean, boolean | 验证注解的元素值是false |
@AssertTrue | Boolean, boolean | 验证注解的元素值是true |
@DecimalMax(value=x) | BigDecimal,BigInteger,String,byte,short,int,long和原始类型的相应包装。HV额外支持:Number和CharSequence的任何子类型。 | 验证注解的元素值小于等于@ DecimalMax指定的value值 |
@DecimalMin(value=x) | BigDecimal,BigInteger,String,byte,short,int,long和原始类型的相应包装。HV额外支持:Number和CharSequence的任何子类型。 | 验证注解的元素值小于等于@ DecimalMin指定的value值 |
@Digits(integer=整数位数, fraction=小数位数) | BigDecimal,BigInteger,String,byte,short,int,long和原始类型的相应包装。HV额外支持:Number和CharSequence的任何子类型。 | 验证注解的元素值的整数位数和小数位数上限 |
@Future | java.util.Date,java.util.Calendar; 如果类路径上有Joda Time日期/时间API ,则由HV附加支持:ReadablePartial和ReadableInstant的任何实现。 |
验证注解的元素值(日期类型)比当前时间晚 |
@Max(value=x) | BigDecimal,BigInteger,byte,short,int,long和原始类型的相应包装。HV额外支持:CharSequence的任何子类型(评估字符序列表示的数字值),Number的任何子类型。 | 验证注解的元素值小于等于@Max指定的value值 |
@Min(value=x) | BigDecimal,BigInteger,byte,short,int,long和原始类型的相应包装。HV额外支持:CharSequence的任何子类型(评估char序列表示的数值),Number的任何子类型。 | 验证注解的元素值大于等于@Min指定的value值 |
@NotNull | 任意种类 |
验证注解的元素值不是null |
@Null | 任意种类 |
验证注解的元素值是null |
@Past | java.util.Date,java.util.Calendar; 如果类路径上有Joda Time日期/时间API ,则由HV附加支持:ReadablePartial和ReadableInstant的任何实现。 |
验证注解的元素值(日期类型)比当前时间早 |
@Pattern(regex=正则表达式, flag=) | 串。HV额外支持:CharSequence的任何子类型。 | 验证注解的元素值与指定的正则表达式匹配 |
@Size(min=最小值, max=最大值) | 字符串,集合,映射和数组。HV额外支持:CharSequence的任何子类型。 | 验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小 |
@Valid | Any non-primitive type(引用类型) |
验证关联的对象,如账户对象里有一个订单对象,指定验证订单对象 |
@NotEmpty | CharSequence,Collection, Map and Arrays | 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0) |
@Range(min=最小值, max=最大值) | CharSequence, Collection, Map and Arrays,BigDecimal, BigInteger, CharSequence, byte, short, int, long 以及原始类型各自的包装 | 验证注解的元素值在最小值和最大值之间 |
@NotBlank | CharSequence | 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格 |
@Length(min=下限, max=上限) | CharSequence | 验证注解的元素值长度在min和max区间内 |
CharSequence | 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式 |
在对象参数上添加验证注解
@Data
public class TestReqVO {
@ApiModelProperty(value = "名称")
@NotBlank(message = "name 不能为空")
private String name;
@ApiModelProperty(value = "年龄")
@NotNull(message = "age 不能为空")
private Integer age;
@ApiModelProperty(value = "id集合")
@NotEmpty(message = "id 集合不能为空")
private List ids;
}
controller中 @Valid注解 指定 需要效验参数
@Api(tags = "测试模块")
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/valid/error")
@ApiOperation(value = "测试Validator抛出业务异常接口")
public DataResult testvalidError(@RequestBody @Valid TestReqVO vo){
return DataResult.ok();
}
}
捕获controller中的全局异常
/**
* @Author 18011618
* @Description
* @Date 16:38 2018/7/5
* @Modify By
*/
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
private static final String logExceptionFormat = "Capture Exception By GlobalExceptionHandler: Code: %s Detail: %s";
private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
//数据效验异常 (针对处理validation 框架)
@ExceptionHandler(value = {BindException.class, MethodArgumentNotValidException.class})
public Object validationExceptionHandler(Exception ex) {
return validationDataResultFormat(14, ex);
}
//记录日志
private void log(Integer status, T exception) {
exception.printStackTrace();
log.error(String.format(logExceptionFormat, status, exception.getMessage()));
}
private DataResult validationDataResultFormat(Integer status, T exception) {
log(status, exception); // 记录日志
BindingResult bindResult = null;
if (exception instanceof BindException) {
bindResult = ((BindException) exception).getBindingResult();
} else if (exception instanceof MethodArgumentNotValidException) {
bindResult = ((MethodArgumentNotValidException) exception).getBindingResult();
}
String msg;
if (bindResult != null && bindResult.hasErrors()) {
msg = bindResult.getAllErrors().get(0).getDefaultMessage();
if (msg.contains("NumberFormatException")) {
msg = "参数类型错误!";
}
} else {
msg = "系统繁忙,请稍后重试...";
}
return DataResult.build(status, msg);
}
}
DataResult是自定义响应结构(使用自己项目的相应结构):
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 自定义响应结构
*/
public class DataResult {
// 定义jackson对象
private static final ObjectMapper MAPPER = new ObjectMapper();
// 响应业务状态
private Integer status;
// 响应消息
private String msg;
// 响应中的数据
private Object data;
public static DataResult build(Integer status, String msg, Object data) {
return new DataResult(status, msg, data);
}
public static DataResult ok(Object data) {
return new DataResult(data);
}
public static DataResult ok() {
return new DataResult(null);
}
public DataResult() {
}
public static DataResult build(Integer status, String msg) {
return new DataResult(status, msg, null);
}
public DataResult(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public DataResult(Object data) {
this.status = 200;
this.msg = "OK";
this.data = data;
}
// public Boolean isOK() {
// return this.status == 200;
// }
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
/**
* 将json结果集转化为DataResult对象
*
* @param jsonData json数据
* @param clazz DataResult中的object类型
* @return
*/
public static DataResult formatToPojo(String jsonData, Class> clazz) {
try {
if (clazz == null) {
return MAPPER.readValue(jsonData, DataResult.class);
}
JsonNode jsonNode = MAPPER.readTree(jsonData);
JsonNode data = jsonNode.get("data");
Object obj = null;
if (clazz != null) {
if (data.isObject()) {
obj = MAPPER.readValue(data.traverse(), clazz);
} else if (data.isTextual()) {
obj = MAPPER.readValue(data.asText(), clazz);
}
}
return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
} catch (Exception e) {
return null;
}
}
/**
* 没有object对象的转化
*
* @param json
* @return
*/
public static DataResult format(String json) {
try {
return MAPPER.readValue(json, DataResult.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Object是集合转化
*
* @param jsonData json数据
* @param clazz 集合中的类型
* @return
*/
public static DataResult formatToList(String jsonData, Class> clazz) {
try {
JsonNode jsonNode = MAPPER.readTree(jsonData);
JsonNode data = jsonNode.get("data");
Object obj = null;
if (data.isArray() && data.size() > 0) {
obj = MAPPER.readValue(data.traverse(),
MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
}
return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
} catch (Exception e) {
return null;
}
}
}