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
}
