SpringBoot 統(tǒng)一異常處理及延伸

為什么需要統(tǒng)一異常處理

在日常開發(fā)中,前后端需要規(guī)約一個清晰的規(guī)則,比如返回什么code是正常返回,什么code表示用戶未登錄,什么code是后端報錯,這樣后端返回了相應的code,前端會對不同的code做一些不同的處理,比如返回的code表示用戶未登錄,則前端直接強制用戶去登錄??墒侨绻看螜z測到用戶未登錄,則手工設置code = 1XXX,然后返回給前端,這樣做很明顯不太好,說不準哪次就設置錯了,就算每次都設置是對的,也會導致大量重復無用的代碼,很不優(yōu)雅。

解決方案

利用Spring的統(tǒng)一異常處理,其實可以很優(yōu)雅的解決這個問題,這里只說一下我司處理的方式,雖然未必有多好,但起碼項目跑了大半年,也沒因為這個出啥問題。

  1. 首先定義一個通用返回的基類

public class BaseResponse {

   // 正常返回
   public static int CODE_SUCCESS = 1000;
   // 用戶未登錄
   public static int CODE_NOT_LOGIN = 1001;
   // 未定義的錯誤,比如 某個地方空指針導致整個接口掛了
   public static int CODE_ERROR = 1100;

   protected int code = CODE_SUCCESS;
   // 錯誤提示
   protected String 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;
       this.code = CODE_ERROR;
   }

}

這里只是簡單的定義了3種code,正常返回、未登錄和未定義錯誤。
再定義一個一般接口返回的類


public class CommonResponse<T> extends BaseResponse {

   public CommonResponse() {
   }

   public CommonResponse(int code, String msg) {
       this.code = code;
       this.msg = msg;
   }

   public CommonResponse(T result) {
       this.result = result;
       this.code = BaseResponse.CODE_SUCCESS;
   }

   private T result;

   public T getResult() {
       return result;
   }

   public void setResult(T result) {
       this.result = result;
   }

}

這樣的返回類可以應對大部分的接口返回,當然,有些特殊的接口返回可以再寫一個繼承自 BaseResponse 的返回。但無論如何,只要返回給前端的Response,都必須繼承自 BaseResponse,這樣前端就可以先去判斷code,再根據(jù)code的值做下一步操作。

  1. 定義一個自定義的異常

public class LindianException extends Exception {

    // 未登錄
    public static final int ERROR_SESSION_NOT_FOUND = 1001;

    public LindianException(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    private int code;
    private String 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;
    }

}

這里定義一個自定義異常,做個簡單的例子,只定義一個未登錄異常。

  1. 接下來就是定義全局異常

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    private final static Logger logger = Logger.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(value = LindianException.class)
    public CommonResponse catchLindianException(LindianException exception) throws Exception {
        CommonResponse commonResponse;
        int errorCode = exception.getCode();
        switch (errorCode) {
            case LindianException.ERROR_SESSION_NOT_FOUND:
                commonResponse = new CommonResponse(BaseResponse.CODE_SESSION_NOT_FOUND, exception.getMsg());
                break;
            default:
                commonResponse = new CommonResponse(BaseResponse.CODE_ERROR, "未知錯誤");
        }
        return commonResponse;
    }

    @ExceptionHandler(value = RuntimeException.class)
    public CommonResponse catchRunTimeException(RuntimeException exception) throws Exception {
        logger.error("controller-error: ", exception);
        CommonResponse commonResponse = new CommonResponse(BaseResponse.CODE_ERROR, exception.getMessage());
        return commonResponse;
    }

}

首先捕捉上面自定義的 LindianException,當捕捉到 LindianException,判斷相應的錯誤信息,返回對應的 CommonResponse。
除了捕捉 LindianException 以外,接口中拋出的 RuntimeException 也在此捕獲到,如果不在此捕捉的話,返回的接口的http狀態(tài)即為500,這肯定是我們不想看見的,這樣捕捉的話,我們就能控制返回給前端一個正確的json串,以便于前端做統(tǒng)一的處理。

  1. 定義基類Controller

public class BaseController {

    protected WxUserInfo getLoginUser() throws LindianException {
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
            String rdSession = request.getHeader("rd-session");
            if (StringUtils.isBlank(rdSession)) {
                throw new LindianException(LindianException.ERROR_SESSION_NOT_FOUND, "用戶未登錄");
            }
            WxUserInfo userInfo = WxStore.getInstance().getUserInfo(rdSession);
            if (userInfo == null) {
                throw new LindianException(LindianException.ERROR_SESSION_NOT_FOUND, "用戶未登錄");
            } else {
                return userInfo;
            }
        } else {
            return null;
        }
    }

}

我司登錄和獲取用戶信息是使用redis,登錄完成返回給前端一個 token,并以此為redis的鍵值,前端在之后的http請求時在header中帶上這個token表明他的身份。
WxUserInfo是我們定義的一個用戶身份model,里面有用戶id等信息,當請求過來的時候,查到請求的header并無token,或者該token在redis中并無對應的用戶信息,則直接拋出 LindianException(LindianException.ERROR_SESSION_NOT_FOUND, "用戶未登錄"),在上文的 GlobalExceptionHandler 中則可以直接將其捕獲到,并返回相應帶有錯誤code的response返回給前端。
其余的Controller都繼承自這個 BaseController,則在任何一個地方,只要方便的使用 getLoginUser() ,就可以獲得用戶信息,可以極大的方便代碼的書寫。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容