1. 程式人生 > >測試開發專題:spring-boot自定義異常返回

測試開發專題:spring-boot自定義異常返回

上文[測試開發專題:spring-boot統一異常捕獲](https://www.immortalp.com/articles/2020/05/09/1589015201069.html)我們討論了java異常以及如何使用Spring-Boot捕獲異常,但是沒有去說捕獲異常後該如何進一步處理,這篇文章我們將對這個遺留的問題進行討論. ### 統一錯誤響應定義 我們希望在程式發生異常的時候,能夠給使用者返回一個比較友好且明確的資訊,對於api介面來說,一種比較好的格式是json,類似於下面這種格式 ```json { "code": "10001", "message": "訊息", "uri":"Get /v2/banner" } ``` 所以需要一個物件來描述這種資料格式: ```java public class UnifyResponse { private int code; private String message; private String requestUri; public UnifyResponse(int code, String message, String requestUri){ this.code = code; this.message = message; this.requestUri = requestUri; } } ``` ### 錯誤響應處理 上文我們談到從開發者的角度來說,異常分為已知異常和未知異常,針對不同的異常使用不同的異常處理函式進行處理,我們之前定義兩個處理函式 ```java @ControllerAdvice public class GlobalExceptionAdvice { /** * 處理未知異常 * @param req * @param ex */ @ExceptionHandler(value = Exception.class) public void handleHttpException(HttpServletRequest req, Exception ex){ System.out.println("發生異常了"); } /** * 處理已知異常 * @param req * @param ex */ @ExceptionHandler(value = HttpException.class) public void handleHttpException(HttpServletRequest req, HttpException ex){ System.out.println("發生了 HttpException"); } } ``` 當未知異常發生時,需要將異常資訊儲存進 `UnifyResponse`,序列化後返回給使用者 ```java @ExceptionHandler(value = Exception.class) public UnifyResponse handleHttpException(HttpServletRequest req, Exception ex){ String uri = req.getRequestURI(); String method = req.getMethod(); System.out.println(ex.getMessage()); return new UnifyResponse(9999, "伺服器錯誤", method + " " + uri); } ``` 對於未知異常我們也不知道發生了什麼,所以這裡的code碼就定義一個通用的,雖然Exception裡面有message,但是這裡不建議將這個異常裡的message返回給使用者,這位可能涉及到程式碼結構的一些東西,而且即使將這個資訊返回給前端,他也不知道是啥問題,沒什麼意義,所以可以將這個message寫到日誌裡,方便後面問題查詢。 返回給使用者的message可以自定義一個通用的,比如伺服器錯誤什麼的。 我們來測試一下,在Controller裡丟擲一個Exception: ```java @RequestMapping(value = "/v2/banner", method = {RequestMethod.GET}) public String test() throws Exception{ throw new Exception("我丟擲來的"); } ``` 然後再瀏覽器裡訪問,發現出錯了 ![image-20200510110158872](https://img2020.cnblogs.com/other/1960827/202005/1960827-20200510155003017-1865663495.png) 這裡的這個異常,看不太懂,回到異常處理方法當中去,我們直接是返回UnifyResponse物件,如果這裡返回的是一個字串,那會不會出錯呢,再是試一下看看,結果還是會報這個錯,也就是說無論這裡返回自定義物件還是字串,都會出現問題,那就是說spring-boot壓根兒就可能不識別我們返回的東西。 在spring-boot裡有一個註解@ResponseBody,可以將我們的返回值,繫結的響應的body上,我們來試一下看看能否解決這個問題。 會發現,上面改的返回String是可以成功的,但是返回UnifyResponse物件還是報錯,而且報錯和之前的還不一樣 ![image-20200510114815064](https://img2020.cnblogs.com/other/1960827/202005/1960827-20200510155003795-2018587820.png) 剛才報的還是404的錯誤,現在變成了500,哪裡錯了呢。 我們來看一下UnifyResponse的定義,我們定義了三個私有的成員變數,但是確沒有定義getter方法,那在序列化的時候是無法獲取到成員變數的值的,所以報錯,這裡我們加上: ![image-20200510152644882](https://img2020.cnblogs.com/other/1960827/202005/1960827-20200510155004142-1128035191.png) 然後在執行程式,訪問路由: ![image-20200510115919104](https://img2020.cnblogs.com/other/1960827/202005/1960827-20200510155004433-438900748.png) 返回的響應就和我們預期的一樣了,但是從上面的圖中,看到返回的狀態碼是200,這顯然是不對的,因為伺服器已經出錯了,狀態碼應該是500,所以這裡要對狀態碼進行自定義。 ### 自定義狀態碼 spring-boot提供了兩種可以自定義狀態碼的方式: ##### 註解 直接在異常處理函式上標記一個叫做 `@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)`並制定狀態碼的列舉值 ![image-20200510152620279](https://img2020.cnblogs.com/other/1960827/202005/1960827-20200510155005188-1797496579.png) 重啟程式,訪問路由: ![image-20200510121218008](https://img2020.cnblogs.com/other/1960827/202005/1960827-20200510155005635-519327577.png) 可以看到狀態碼已經變成500了。 上面使用註解的的形式雖然能夠實現我們的目的,但是這種方式不太靈活,,這裡很多的response的設定都是spring-boot幫我們做了,如果需要做一些自定義就不是太方便,接下來的這種方式確可以讓我們通過程式碼靈活的進行控制。 ##### ResponseEntity ResponseEntity是一個泛型類,是可以直接return回去的,可以設定很多屬性,包括status、headers、body等。 用ResponseEntity來自定義已知異常處理方法的返回資訊: ![image-20200510152555316](https://img2020.cnblogs.com/other/1960827/202005/1960827-20200510155006295-585816954.png) 然後再Controller裡丟擲一個NotFoundException,重新執行程式,訪問路由: ![image-20200510152800514](https://img2020.cnblogs.com/other/1960827/202005/1960827-20200510155006674-1485647002.png) 可以看到也能夠返回正確的狀態碼。 ### 總結 **本篇文章我們介紹了,定義錯誤響應以及如何返回自定義的錯誤資訊,多種方式進行定製狀態碼,但是我們在文章的錯誤資訊都是硬編碼在程式碼裡的,這樣很不好管理,所以下篇文章我們將介紹如何對錯誤資訊管理,敬請關注!!!** > 本文連結:https://www.immortalp.com/articles/2020/05/10/1589096782703.html > 歡迎大家去 [我的部落格](https://www.immortalp.com) 瞅瞅,裡面有更多關於測試實戰的內容