使用Spring MVC的@ControllerAdvice注解做Json的異常處理


一,本文介紹Spring MVC的自定義異常處理,即在Controller中拋出自定義的異常時,客戶端收到更友好的JSON格式的提示。而不是常見的報錯頁面。

二,場景描述:實現公用API,驗證API key的邏輯,放在攔截器中判斷(等同於在Controller中)並拋出異常,用戶收到類似下圖的提示:

其中,Http狀態Code也能自由控制。

 

三,解決方案:

1,在RateLimitInterceptor.java攔截器中拋出異常:

 1 public class RateLimitInterceptor extends HandlerInterceptorAdapter{
 2  
 3     @Autowired private RedisService rs;
 4  
 5     /**
 6      * 流量控制檢查入口
 7      */
 8     @Override
 9     public boolean preHandle(HttpServletRequest request,
10             HttpServletResponse response, Object handler) throws RequiredParameterException, SignException, RateLimitException,Exception {
11         super.preHandle(request, response, handler);
12         String appKey = request.getParameter("appKey");
13         //判斷appKey是否為空或是否合法
14         if(appKey == null){
15             throw new RequiredParameterException("");
16         }else if(AppKeyUtils.isFormatCorrect(appKey) || !rs.isExist(appKey)){
17             
18             throw new SignException();
19             
20         }else {
21             try {
22                 AppCall appCall = AppCall.create(appKey, AppKeyUtils.getPlanDetails(appKey));
23                 appCall.decrease();
24                 rs.save(appCall);
25                 System.out.println("RateLimitInterceptor pass.");
26             } catch (RateLimitException e) {
27                 //拋出超限異常
28                 throw new RateLimitException();
29             }
30         }
31         return true;
32     }
33  
34 }

當代碼走到(具體怎樣走到,需考慮具體業務邏輯,上述代碼使用AppCall類來封裝,這是后話)

1 throw new SignException();

時,Spring將自動捕獲這個異常。然后做一些處理。這是正常的流程。那么Spring如何自動不火

2,使用Spring MVC的@ControllerAdvice,在GlobalExceptionHandler.java類中實現全局的異常處理類:

 1 @ControllerAdvice
 2 public class GlobalExceptionHandler {
 3  
 4     @ExceptionHandler(SQLException.class)
 5     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
 6     @ResponseBody
 7     public ExceptionResponse handleSQLException(HttpServletRequest request, Exception ex) {
 8         String message = ex.getMessage();
 9         return ExceptionResponse.create(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
10     }
11      
12     @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="IOException occured")
13     @ExceptionHandler(IOException.class)
14     @ResponseBody
15     public void handleIOException(){
16         //returning 404 error code
17     }
18     
19     @ResponseStatus(HttpStatus.BAD_REQUEST)
20     @ResponseBody
21     @ExceptionHandler(SignException.class)
22     public ExceptionResponse signException(SignException ex) {
23         return ex.getEr();
24     }
25  
26 }

在方法的頭上注解:

1 @ExceptionHandler(SignException.class)

即表示讓Spring捕獲到所有拋出的SignException異常,並交由這個被注解的方法處理。

注解:

1 @ResponseBody

即表示返回的對象,Spring會自動把該對象進行json轉化,最后寫入到Response中。

注解:

1 @ResponseStatus(HttpStatus.BAD_REQUEST)

表示設置狀態碼。如果應用級別的錯誤,此處其實永遠返回200也是可以接受的,但是要在你自定義的異常串和異常碼中做好交代。

本文的方案自定義了一個ExceptionResponse.java類,如下:

 1 /**
 2  * 返回的json數據
 3  * @author craig
 4  *
 5  */
 6 public class ExceptionResponse {
 7     
 8     private String message;
 9     private Integer code;
10     
11     /**
12      * Construction Method
13      * @param code
14      * @param message
15      */
16     public ExceptionResponse(Integer code, String message){
17         this.message = message;
18         this.code = code;
19     }
20     
21     public static ExceptionResponse create(Integer code, String message){
22         return new ExceptionResponse(code, message);
23     }
24     
25     public Integer getCode() {
26         return code;
27     }
28     public String getMessage() {
29         return message;
30     }
31     
32 }

如你所知,這個類就是最后傳到用戶面前的一個異常類,code和message根據業務定義並讓用戶知曉。而自定義的SignException.java實際上起到了一個橋梁的作用。Spring把SignException對象捕獲到,轉成相應的ExceptionResponse對象,剩下的就是如何優雅實現的問題了。 如下是SignException.java的實現:

 1 /**
 2  * 簽名異常
 3  * @author tuxiao.czz
 4  *
 5  */
 6 public class SignException extends Exception {
 7  
 8     private static final long serialVersionUID = 4714113994147018010L;
 9     private String message = "AppKey is not correct, please check.";
10     private Integer code = 10002;
11         
12     private ExceptionResponse er;
13     
14     public SignException() {
15         er = ExceptionResponse.create(code, message);
16     }
17     
18     public ExceptionResponse getEr() {
19         return er;
20     }
21     
22 }

所有關於這個異常的code和message都寫在這個類里,個人感覺還是可以接受。當然還有另外一種實現,就是只攔截、定義一種Exception類,然后傳不同的code和message進去,然后做相應的處理。這些都是比較靈活的。

以上便是實現“自定義異常json化處理”的相關代碼和說明。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM