项目开发过程中,会遇到两种类型的异常情况,这里分开来讨论。
一、请求正确被分发了,但是处理请求过程中,出现异常。
这种情况,我们可以自定义一个Controller增强类,并加上@ControllerAdvice,要确保该注解能被Spring自动扫描到。
@Slf4j
@ControllerAdvice
public class BaseController {
/**
* 系统异常处理
*
* @param request Http请求
* @param ex 异常对象
*/
@ExceptionHandler(Exception.class)
@ResponseBody
protected ApiResp exceptionProcess(HttpServletRequest request, Exception ex) {
log.error("[System Exception] url: {}", request.getRequestURL().toString(), ex);
return ApiResp
.retFail(BaseError.SYSTEM_EXCEPTION.getCode(), BaseError.SYSTEM_EXCEPTION.getMsg());
}
}
注意,如果是返回JSON的数据格式,该处理方法需要加上@ResponseBody注解。
另外,除了最后兜底的Exception外,还可以自定义特殊处理一些其他的异常,已给出更加精确的返回结果,例如,参数校验异常等等。
/**
* 参数校验异常
*
* @param request Http请求
* @param ex 异常对象
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
protected ApiResp exceptionProcess(HttpServletRequest request, MethodArgumentNotValidException ex) {
List<ObjectError> list = ex.getBindingResult().getAllErrors();
List<String> detailMsg = Lists.newArrayList();
for (ObjectError objectError : list) {
detailMsg.add(objectError.getDefaultMessage());
}
int code = BaseError.SYSTEM_ARGUMENT_NOT_VALID.getCode();
String msg = BaseError.SYSTEM_ARGUMENT_NOT_VALID.getMsg();
ArgumentNotValidMsg data = new ArgumentNotValidMsg();
data.setDetailMsg(detailMsg);
log.info("[Method Args Exception] URL: {}, ERROR: {}", request.getRequestURL().toString(),
StringUtils.join(detailMsg, ","));
return ApiResp.retFail(code, msg, data);
}
这样配置后,当出现参数校验不通过时,提示信息更明确,效果如下:
二,第二种情况是,请求没有被正确分发,比如请求地址404,或者权限不足等等。
这种情况下,SpringBoot内置了一个BasicErrorController来进行处理。但是其返回格式不满足我们的项目要求,所以实现ErrorController,重写里面的处理方法。
@RestController
@RequestMapping("/error")
public class ErrorHandleController implements ErrorController {
private final ErrorAttributes errorAttributes;
private final ServerProperties serverProperties;
@Autowired
public ErrorHandleController(ErrorAttributes errorAttributes,
ServerProperties serverProperties) {
this.errorAttributes = errorAttributes;
this.serverProperties = serverProperties;
}
@RequestMapping("")
public ApiResp error(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request));
response.setStatus(HttpStatus.OK.value());
int code = (int) body.get("status");
String msg = "'" + body.get("path") + "' " + body.get("error");
return ApiResp.retFail(code, msg);
}
@Override
public String getErrorPath() {
return "/error";
}
private Map<String, Object> getErrorAttributes(HttpServletRequest request,
boolean includeStackTrace) {
WebRequest webRequest = new ServletWebRequest(request);
return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
}
private boolean isIncludeStackTrace(HttpServletRequest request) {
ErrorProperties.IncludeStacktrace include = this.serverProperties.getError().getIncludeStacktrace();
if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
return true;
}
if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
return getTraceParameter(request);
}
return false;
}
private boolean getTraceParameter(HttpServletRequest request) {
String parameter = request.getParameter("trace");
if (parameter == null) {
return false;
}
return !"false".equals(parameter.toLowerCase());
}
}
处理前效果:
处理后效果:
将默认的返回消息格式,转成了项目要求的自定义格式。