需求源自於任何一個業務的編寫總會有各種各樣的條件判斷,需要時時手動丟擲異常,又希望讓介面返回友好的錯誤資訊。
spring boot提供的幫助是自動將異常重定向到路由為/error的控制器
但是我們又希望手動丟擲的異常與正常的資料返回為同一型別
所以我的解決方案由三個步驟組成:
1.一個異常列舉類 StatusCodeEnum.java
public enum StatusCodeEnum implements Serializable { SUCCESS(0, "成功"), ERROR(-1, "失敗"), USER_INVALID(60000, "無效使用者"), SYS_ARG_INVALID(11000, "無效引數"), ; private static final long serialVersionUID = 1L; private int code; private String msg; StatusCodeEnum(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } //根據code獲取對應列舉 public static StatusCodeEnum getByCode(int code) { StatusCodeEnum[] values = StatusCodeEnum.values(); for (StatusCodeEnum bizStatusCodeEnum : values) { if (bizStatusCodeEnum.code == code) { return bizStatusCodeEnum; } } return null; } }
2.一個異常呼叫類 BaseException.java
因為丟擲異常只能丟擲字串 所以這裡使用了com.alibaba.fastjson.JSON包
public class BaseException { private int code; private String message; public static void error(StatusCodeEnum statusCodeEnum) throws Exception { error(statusCodeEnum.getCode(),statusCodeEnum.getMsg()); } public static void error(String message) throws Exception { error(-1,message); } public static void error(int code,String message) throws Exception { BaseException baseException = new BaseException(); baseException.setCode(code); baseException.setMessage(message); throw new Exception(JSON.toJSONString(baseException)) ; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "BaseException{" + "code=" + code + ", message='" + message + '\'' + '}'; } }
3.異常處理控制器 ErrorController.java
@Controller public class ErrorController extends AbstractErrorController{ @Autowired ObjectMapper objectMapper; public ErrorController() { super(new DefaultErrorAttributes()); } @Override public String getErrorPath() { return null; } @RequestMapping("/error") @ResponseBody public BaseRs getErrorPath(HttpServletRequest request, HttpServletResponse response) { Map<String,Object> model = Collections.unmodifiableMap(getErrorAttributes(request,false)); //獲取異常 可將異常列印到日誌 Throwable cause = getCause(request); int status = (Integer)model.get("status"); //自定義友好錯誤資訊 String msg = (String)model.get("message"); JSONObject object = JSONObject.parseObject(msg); int code = object.getInteger("code"); String message = object.getString("message"); return new BaseRs(code,message); } protected Throwable getCause(HttpServletRequest request) { Throwable error = (Throwable)request.getAttribute("javax.servlet.error.exception"); if(null == error){ //MVC有可能會封裝異常成ServletException ,需要呼叫getCause獲取真正的異常 while (error instanceof ServletException && error.getCause() != null){ error = ((ServletException) error).getCause(); } } return error; } }
以上三個檔案為異常處理的核心
其中的BaseRs類是統一資料返回
public class BaseRs<T> implements Serializable { private int code; private String message; public BaseRs() { } /** * 返回內容 */ private T content; public int getCode() { return code; } public String getMessage() { return message; } public T getContent() { return content; } public BaseRs(int code, String message) { this.code = code; this.message = message; } public BaseRs(int code, String message, T content) { this.code = code; this.message = message; this.content = content; } public BaseRs(StatusCodeEnum status) { this.code = status.getCode(); this.message = status.getMsg(); } public BaseRs(StatusCodeEnum status, T content) { this.code = status.getCode(); this.message = status.getMsg(); this.content = content; } public static <V> BaseRs ok(V content) { return new BaseRs(StatusCodeEnum.SUCCESS, content); } public static BaseRs ok() { return new BaseRs(StatusCodeEnum.SUCCESS); } public static BaseRs error(StatusCodeEnum error) { return new BaseRs(error); } public void setCode(StatusCodeEnum status) { this.code = status.getCode(); } public void setMessage(String message) { this.message = message; } public void setContent(T content) { this.content = content; } @Override public String toString() { return "BaseRs{" + "code=" + code + ", message='" + message + '\'' + ", content=" + content + '}'; } public void setCode(int code) { this.code = code; } }
最後的控制層程式碼以最簡潔的方式呼叫即可:
@Controller public class IndexController { @RequestMapping("/a") @ResponseBody public BaseRs a() throws Exception{ boolean s = false; if(!s){ BaseException.error(StatusCodeEnum.USER_INVALID); } return new BaseRs(StatusCodeEnum.SUCCESS); } }
本篇部落格的碼雲地址: https://gitee.com/zhao-baolin/springboot_error