spring boot 統一異常處理

不該相遇在秋天發表於2018-07-01

需求源自於任何一個業務的編寫總會有各種各樣的條件判斷,需要時時手動丟擲異常,又希望讓介面返回友好的錯誤資訊。

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;
    }
}
View 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

 

相關文章