上文測試開發專題:spring-boot統一異常捕獲我們討論了java異常以及如何使用Spring-Boot捕獲異常,但是沒有去說捕獲異常后該如何進一步處理,這篇文章我們將對這個遺留的問題進行討論.
統一錯誤響應定義
我們希望在程序發生異常的時候,能夠給用戶返回一個比較友好且明確的信息,對於api接口來說,一種比較好的格式是json,類似於下面這種格式
{
"code": "10001", "message": "消息", "uri":"Get /v2/banner" }
所以需要一個對象來描述這種數據格式:
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; } }
錯誤響應處理
上文我們談到從開發者的角度來說,異常分為已知異常和未知異常,針對不同的異常使用不同的異常處理函數進行處理,我們之前定義兩個處理函數
@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
,序列化后返回給用戶
@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:
@RequestMapping(value = "/v2/banner", method = {RequestMethod.GET}) public String test() throws Exception{ throw new Exception("我拋出來的"); }
然后再瀏覽器里訪問,發現出錯了
這里的這個異常,看不太懂,回到異常處理方法當中去,我們直接是返回UnifyResponse對象,如果這里返回的是一個字符串,那會不會出錯呢,再是試一下看看,結果還是會報這個錯,也就是說無論這里返回自定義對象還是字符串,都會出現問題,那就是說spring-boot壓根兒就可能不識別我們返回的東西。
在spring-boot里有一個注解@ResponseBody,可以將我們的返回值,綁定的響應的body上,我們來試一下看看能否解決這個問題。
會發現,上面改的返回String是可以成功的,但是返回UnifyResponse對象還是報錯,而且報錯和之前的還不一樣
剛才報的還是404的錯誤,現在變成了500,哪里錯了呢。
我們來看一下UnifyResponse的定義,我們定義了三個私有的成員變量,但是確沒有定義getter方法,那在序列化的時候是無法獲取到成員變量的值的,所以報錯,這里我們加上:
然后在運行程序,訪問路由:
返回的響應就和我們預期的一樣了,但是從上面的圖中,看到返回的狀態碼是200,這顯然是不對的,因為服務器已經出錯了,狀態碼應該是500,所以這里要對狀態碼進行自定義。
自定義狀態碼
spring-boot提供了兩種可以自定義狀態碼的方式:
注解
直接在異常處理函數上標記一個叫做
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
並制定狀態碼的枚舉值
重啟程序,訪問路由:
可以看到狀態碼已經變成500了。
上面使用注解的的形式雖然能夠實現我們的目的,但是這種方式不太靈活,,這里很多的response的設置都是spring-boot幫我們做了,如果需要做一些自定義就不是太方便,接下來的這種方式確可以讓我們通過代碼靈活的進行控制。
ResponseEntity
ResponseEntity是一個泛型類,是可以直接return回去的,可以設置很多屬性,包括status、headers、body等。
用ResponseEntity來自定義已知異常處理方法的返回信息:
然后再Controller里拋出一個NotFoundException,重新運行程序,訪問路由:
可以看到也能夠返回正確的狀態碼。
總結
本篇文章我們介紹了,定義錯誤響應以及如何返回自定義的錯誤信息,多種方式進行定制狀態碼,但是我們在文章的錯誤信息都是硬編碼在代碼里的,這樣很不好管理,所以下篇文章我們將介紹如何對錯誤信息管理,敬請關注!!!
本文鏈接:https://www.immortalp.com/articles/2020/05/10/1589096782703.html