使用Spring MVC的@ControllerAdvice註解做Json的異常處理
阿新 • • 發佈:2019-01-27
一,本文介紹Spring MVC的自定義異常處理,即在Controller中丟擲自定義的異常時,客戶端收到更友好的JSON格式的提示。而不是常見的報錯頁面。
二,場景描述:實現公用API,驗證API key的邏輯,放在攔截器中判斷(等同於在Controller中)並丟擲異常,使用者收到類似下圖的提示:
其中,Http狀態Code也能自由控制。
三,解決方案:
1,在RateLimitInterceptor.java攔截器中丟擲異常:
public class RateLimitInterceptor extends HandlerInterceptorAdapter{ @Autowired private RedisService rs; /** * 流量控制檢查入口 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws RequiredParameterException, SignException, RateLimitException,Exception { super.preHandle(request, response, handler); String appKey = request.getParameter("appKey"); //判斷appKey是否為空或是否合法 if(appKey == null){ throw new RequiredParameterException(""); }else if(AppKeyUtils.isFormatCorrect(appKey) || !rs.isExist(appKey)){ throw new SignException(); }else { try { AppCall appCall = AppCall.create(appKey, AppKeyUtils.getPlanDetails(appKey)); appCall.decrease(); rs.save(appCall); System.out.println("RateLimitInterceptor pass."); } catch (RateLimitException e) { //丟擲超限異常 throw new RateLimitException(); } } return true; } }
當代碼走到(具體怎樣走到,需考慮具體業務邏輯,上述程式碼使用AppCall類來封裝,這是後話)
throw new SignException();
時,Spring將自動捕獲這個異常。然後做一些處理。這是正常的流程。那麼Spring如何自動不火
2,使用Spring MVC的@ControllerAdvice,在GlobalExceptionHandler.java類中實現全域性的異常處理類:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(SQLException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public ExceptionResponse handleSQLException(HttpServletRequest request, Exception ex) { String message = ex.getMessage(); return ExceptionResponse.create(HttpStatus.INTERNAL_SERVER_ERROR.value(), message); } @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="IOException occured") @ExceptionHandler(IOException.class) @ResponseBody public void handleIOException(){ //returning 404 error code } @ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseBody @ExceptionHandler(SignException.class) public ExceptionResponse signException(SignException ex) { return ex.getEr(); } }
在方法的頭上註解:
@ExceptionHandler(SignException.class)
即表示讓Spring捕獲到所有丟擲的SignException異常,並交由這個被註解的方法處理。
註解:
@ResponseBody
即表示返回的物件,Spring會自動把該物件進行json轉化,最後寫入到Response中。
註解:
@ResponseStatus(HttpStatus.BAD_REQUEST)
表示設定狀態碼。如果應用級別的錯誤,此處其實永遠返回200也是可以接受的,但是要在你自定義的異常串和異常碼中做好交代。
本文的方案自定義了一個ExceptionResponse.java類,如下:
/**
* 返回的json資料
* @author craig
*
*/
public class ExceptionResponse {
private String message;
private Integer code;
/**
* Construction Method
* @param code
* @param message
*/
public ExceptionResponse(Integer code, String message){
this.message = message;
this.code = code;
}
public static ExceptionResponse create(Integer code, String message){
return new ExceptionResponse(code, message);
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
如你所知,這個類就是最後傳到使用者面前的一個異常類,code和message根據業務定義並讓使用者知曉。而自定義的SignException.java實際上起到了一個橋樑的作用。Spring把SignException物件捕獲到,轉成相應的ExceptionResponse物件,剩下的就是如何優雅實現的問題了。 如下是SignException.java的實現:
/**
* 簽名異常
* @author tuxiao.czz
*
*/
public class SignException extends Exception {
private static final long serialVersionUID = 4714113994147018010L;
private String message = "AppKey is not correct, please check.";
private Integer code = 10002;
private ExceptionResponse er;
public SignException() {
er = ExceptionResponse.create(code, message);
}
public ExceptionResponse getEr() {
return er;
}
}
所有關於這個異常的code和message都寫在這個類裡,個人感覺還是可以接受。當然還有另外一種實現,就是隻攔截、定義一種Exception類,然後傳不同的code和message進去,然後做相應的處理。這些都是比較靈活的。
以上便是實現“自定義異常json化處理”的相關程式碼和說明。