前言
记得很早之前,写过一篇springmvc全局异常处理总结springmvc异常统一处理。由于springmvc是传统的mvc框架,所以最终异常处理是在项目的配置文件中配置error异常页面,这里再和大家一起总结下springboot项目中的全局异常处理。公司的项目都是前后端分离的,所以统一异常处理是指接口的统一异常处理,返回对应的状态码,由前端或者客户端同学根据约定好的状态码做出相应的交互相应。避免直接将具体的异常堆栈直接抛给用户,下面给出具体的demo。
接口返回包装类
在日常开发中,我们通常都会给接口定义一个通用规范的响应包装类,便于前端同学解析结果响应
/**
* @author 爱琴孩
*/
public class Response<T> {
public static Response SUCCESS_RESPONSE = new Response(RetCode.SUCCESS.getCode(),
RetCode.SUCCESS.getDesc());
private String code;
private String desc;
private T biz;
public Response() {
}
public Response(RetCode retCode) {
this.code = retCode.getCode();
this.desc = retCode.getDesc();
}
public Response(RetCode retCode, T biz) {
this.code = retCode.getCode();
this.desc = retCode.getDesc();
this.biz = biz;
}
public Response(String code, String desc) {
this.code = code;
this.desc = desc;
}
public Response(String code, String desc, T biz) {
this.code = code;
this.desc = desc;
this.biz = biz;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public T getBiz() {
return biz;
}
public void setBiz(T biz) {
this.biz = biz;
}
}
通用状态码类
我们在接口中的一些参数,权限校验返回的状态码都需要在接口文档中规范清楚,在联调测试中减少提高联调效率
public class RetCode {
public static final RetCode SUCCESS = new RetCode("000000", "success");
public static final RetCode PARAM_ERROR = new RetCode("000001", "param is error");
public static final RetCode ERROR = new RetCode("999999", "error");
/**
* 返回码
*/
private final String code;
/**
* 描述
*/
private final String desc;
/**
* getter method
*
* @return the code
* @see RetCode#code
*/
public String getCode() {
return code;
}
/**
* getter method
*
* @return the desc
* @see RetCode#desc
*/
public String getDesc() {
return desc;
}
/**
* @param code
* @param desc
*/
public RetCode(String code, String desc) {
super();
this.code = code;
this.desc = desc;
}
}
全局异常处理类
其实这里的全局异常处理和springmvc框架一样的,都是基于@ControllerAdvice和@ExceptionHandler来实现的
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Log LOGGER = LogFactory.getLog(GlobalExceptionHandler.class);
//这里是一个自定义异常
@ExceptionHandler(value = {ParameterException.class})
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public void handleServletException(HttpServletResponse response,
ServletException e) throws IOException {
LOGGER.info(e, e);
Response<Object> responseObj =
new Response<>(RetCode.PARAM_ERROR.getCode(), RetCode.PARAM_ERROR.getDesc());
writeEntityToHttpServletResponse(responseObj, response);
}
@ExceptionHandler({Exception.class})
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public void handleException(HttpServletResponse response, Exception e)
throws IOException {
LOGGER.error(e, e);
Response<Object> responseObj = new Response<>(RetCode.ERROR.getCode(),
RetCode.ERROR.getDesc());
writeEntityToHttpServletResponse(responseObj, response);
}
private void writeEntityToHttpServletResponse(Object object,
HttpServletResponse response) throws IOException {
byte[] returnByte = JSON.toJSONString(object).getBytes("utf-8");
response.addHeader("Content-Type", "application/json");
response.setHeader("Content-Length", String.valueOf(returnByte.length));
response.getOutputStream().write(returnByte);
response.getOutputStream().flush();
}
}
测试接口
@RequestMapping(value = "/testGlobalException")
public ResponseEntity<Response<Object>> testGlobalException() {
log.info("invoke method testGlobalException");
int a = 1 / 0;
Response<Object> resp = new Response<>(RetCode.SUCCESS.getCode(),RetCode.SUCCESS.getDesc());
return new ResponseEntity<>(resp, HttpStatus.OK);
}
测试结果
{"code":"999999","desc":"error"}