1. 本地代码模拟:
前台参数校验,在@RequestBody参数中使用@Valid
@RequestMapping(value = "/goodsAdd",method = RequestMethod.POST)
public void addGoods(@RequestHeader String userCode,@RequestBody @Valid Goods goods) {
goodsService.addGoods(goods);
}
VO层校验,加上@NotBlank/NotNull/NotEmpty注解:
@Data
public class Goods {
@Valid
@NotEmpty(message = "商品id不能为空")
private String goodsId;
}
封装下Response输出格式
package com.evan;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求响应结果
* @param <T>
*/
@ApiModel(description = "返回对象")
@Data
public class Response<T> {
public final static String CODE_OK = "200";
public final static String MSG_OK = "ok";
public final static String CODE_ERROR = "5000";
public final static String MSG_ERROR = "error";
@ApiModelProperty(value = "返回码,200为成功,其他为失败", required = true)
private String resultCode;
@ApiModelProperty(value = "返回码描述", required = true)
private String resultMsg;
@ApiModelProperty(value = "结果签名", required = true)
private String sign;
@ApiModelProperty(value = "详细数据", required = false)
private T data;
public Response() {
resultCode = CODE_OK;
resultMsg = MSG_OK;
}
public Response(String resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}
/**
* 成功结果
*
* @param data
* @param <T>
* @return
*/
public static <T> Response<T> ok(T data, String sign) {
Response<T> response = new Response<>();
response.setData(data);
response.setSign(sign);
return response;
}
/**
* 成功结果
*
* @param data
* @param <T>
* @return
*/
public static <T> Response<T> ok(T data) {
Response<T> response = new Response<>();
response.setData(data);
return response;
}
/**
* 失败结果
*
* @param data
* @param <T>
* @return
*/
public static <T> Response<T> error(T data) {
Response<T> response = new Response<>();
response.setData(data);
return error(CODE_ERROR, MSG_ERROR, data);
}
/**
* 失败结果
*
* @param code
* @param msg
* @param data
* @param <T>
* @return
*/
public static <T> Response<T> error(String code, String msg, T data) {
Response<T> response = new Response<>(code, msg);
response.setData(data);
return response;
}
/**
* 失败结果
*
* @param data
* @param <T>
* @return
*/
public static <T> Response<T> error(Exception exception, T data) {
return errorException(exception, data);
}
/**
* 失败结果
* @param data
* @param <T>
* @return
*/
public static <T> Response<T> errorException(Exception exception, T data) {
Response<T> response = new Response<>();
response.setData(data);
response.setResultMsg(exception.getMessage());
return response;
}
}
这里简单使用@ControllerAdvice全局统一处理 数据校验异常 的结果
@RestControllerAdvice
@Slf4j
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public Response<?> handleException(Exception e) {
return Response.error( e.getMessage());
}
}
2.线上问题复现:
当请求实体中goodsId为空时,偶然会返回400异常,这样明显不够友好,但是这种偶然性发生校验异常也很不正常,后面参考了spring3.1 issue说明了@Valid这种校验性需要另外捕获下MethodArgumentNotValidException以及Bind这个异常类,才能避免400异常
3.程序中添加这类校验性异常,统一异常处理:
@RestControllerAdvice
@Slf4j
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public Response<?> handleException(Exception e) {
return Response.error( e.getMessage());
}
/**
* 拦截参数校验异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Response<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException methodArgumentNotValidException) {
StringBuilder errorMessage=new StringBuilder();
List<ObjectError> objectErrors=methodArgumentNotValidException.getBindingResult().getAllErrors();
if (!CollectionUtils.isEmpty(objectErrors)) {
for (int i = 0; i < objectErrors.size(); i++) {
if (i == 0) {
errorMessage.append(objectErrors.get(i).getDefaultMessage());
} else {
errorMessage.append(",");
errorMessage.append(objectErrors.get(i).getDefaultMessage());
}
}
}else {
errorMessage.append("MethodArgumentNotValidException occured.");
}
return Response.error("400", errorMessage.toString(),null);
}
}
4.前台请求层处理:
@RequestMapping(value = "/goodsAdd",method = RequestMethod.POST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public void addGoods(@RequestHeader String userCode,@RequestBody @Valid Goods goods) {
goodsService.addGoods(goods);
}
经验证,线上不再复现校验400异常,到此问题解决,参考文章:
https://github.com/spring-projects/spring-framework/issues/14790
https://blog.csdn.net/weixin_30409927/article/details/105448171