hibernate-validator优雅地校验参数,全局异常处理封装校验异常
hibernate-validator 是一个参数校验框架,可以对于入参进行优雅的进行数据校验,可以减少入参校验重复的代码。
对于hibernate-validator 对于校验异常的数据,会抛出MethodArgumentNotValidException,我们可以通过全局异常处理,进行异常封装,优雅地返回异常信息。
1.集成hibernate-validator需要进行依赖,我这边用的版本是
<hibernate-validator.version>6.0.13.Final</hibernate-validator.version>
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>${hibernate-validator.version}</version> </dependency>
2.对于controller层中,方法需要标记 @Valid
@RequestMapping(value = "/test",method = RequestMethod.POST) @ApiOperation(value = "问卷转换",tags={"问卷转换核保规则"}, notes = "问卷与核保规则转换",httpMethod = "POST") public Result<ConvertQuestionResponseMO> convertQuestion(@RequestBody @Valid ConvertQuestionRequestMO request) { }
3.MO上我们可以使用注解进行数据校验
public class ConvertQuestionRequestMO implements Serializable { /**请求流水号*/ @NotBlank(message = "messageId不能为空") private String messageId; /**渠道代码*/ @NotNull(message = "testId不能为空") private String testId;
@Pattern(regexp="^(\\s*|[0-9]+([.]{1}[0-9]+){0,1})$",message="scores格式不正确")
private String score;
@NotEmpty(message = "qtnList不能为空")
@Valid
private List<Test2Req> qtnList;
//注:Test2Req中的字段如果想要校验参数,必须也加上@Valid的注解,否则验证器不生效
4.对于验证器验证不通过抛出的异常,我们需要处理下,避免往外抛异常,返回友好的mo信息
package com.uwe.handler; import com.common.base.enums.ResponseCode; import com.common.base.excepiton.BusinessException; import com.common.base.mo.Result; import com.common.base.utils.JsonUtil; import com.uwe.eo.UwInterfaceLog; import com.uwe.service.UwInterfaceLogService; import org.apache.commons.collections4.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.HashMap; /** * 全局异常处理 code 200/500 * @Auther: tony_t_peng * @Date: 2020-07-31 16:22 * @Description: */ @RestControllerAdvice public class GlobalExceptionHandler { @Autowired private UwInterfaceLogService interfaceLogService; public static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * 全局异常统一处理 * @throws Exception */ @ExceptionHandler(value = Exception.class) @ResponseBody public Result defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { String requestBody = ""; if (req != null && req instanceof ContentCachingRequestWrapper) { ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) req; requestBody = new String(wrapper.getContentAsByteArray()); } logger.info("接口请求参数:{}",requestBody); logger.error("接口调用数据异常 ",e); Result response = new Result(); String errorMesssage = ""; if (e instanceof BusinessException) { errorMesssage=e.getMessage(); }else if(e instanceof MethodArgumentNotValidException){//参数校验异常 BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult(); if(bindingResult!=null&& CollectionUtils.isNotEmpty(bindingResult.getFieldErrors())){ for (FieldError fieldError : bindingResult.getFieldErrors()) { errorMesssage += fieldError.getDefaultMessage() + ","; } errorMesssage="参数校验异常:"+errorMesssage.substring(0,errorMesssage.length()-1); }else{ errorMesssage="参数校验异常"; } }else{//其他异常 errorMesssage="接口调用数据异常"; } ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) req; response.setTraceId(gettraceId(req)); response.setMessage(errorMesssage); response.setCode(ResponseCode.FAIL.getCode()); saveInterfaceLog(requestBody,errorMesssage,response); return response; } private void saveInterfaceLog(String requestBody,String errorMesssage,Result result){ UwInterfaceLog uwInterfaceLog = new UwInterfaceLog(); uwInterfaceLog.setTrxnId(result.getTraceId()); uwInterfaceLog.setException(errorMesssage); uwInterfaceLog.setRequestTime(new Date()); uwInterfaceLog.setResponseTime(new Date()); uwInterfaceLog.setRequestBody(requestBody); uwInterfaceLog.setResponseBody(JsonUtil.toJSONString(result)); uwInterfaceLog.setInterfaceType("convert_question"); interfaceLogService.saveInterfaceLog(uwInterfaceLog); } private String gettraceId(HttpServletRequest req){ if (req != null && req instanceof ContentCachingRequestWrapper) {ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) req; String body = new String(wrapper.getContentAsByteArray()); HashMap map = JsonUtil.parseObject(body, HashMap.class); Object traceId = map.get("traceId"); if(traceId!=null&&traceId instanceof String){ return (String) traceId; } } return null; } }
定义的返回Result
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.common.base.mo; import com.common.base.enums.ResponseCode; import java.io.Serializable; public class Result<T> implements Serializable { private String traceId; private String code; private String message; private T data; public Result() { this.code = ResponseCode.SUCCESS.getCode(); this.message = ResponseCode.SUCCESS.name(); } public String getCode() { return this.code; } public void setCode(String code) { this.code = code; } public String getMessage() { return this.message; } public void setMessage(String message) { this.message = message; } public T getData() { return this.data; } public void setData(T data) { this.data = data; } public String getTraceId() { return this.traceId; } public void setTraceId(String traceId) { this.traceId = traceId; } }
5. 测试下,hibernate-validator的异常信息,我们捕获之后,就可以优雅的返回给调用者
{ "traceId": "20200819174018044", "code": "1", "message": "参数校验异常:scores格式不正确,lifeRisk格式不正确",
"data": null
}