測試開發專題:spring-boot自定義異常返回
阿新 • • 發佈:2020-05-10
上文[測試開發專題: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) 瞅瞅,裡面有更多關於測試實戰的內容