淺談SpringBoot如何封裝統一響應體
一、前言
在上一篇 SpringBoot 參數校驗 中我們對參數校驗添加瞭異常處理,但是還是有不規范的地方,沒有用統一響應體進行返回,在這篇文章中介紹如何封裝統一響應體。
關於統一響應體的封裝,沒有一個標準答案,我在各種技術社區看瞭一遍,匯總瞭一個復用性比較好的方案。
二、添加結果類枚舉
在項目目錄下面建一個 responseEntity
的 package,然後在裡面建一個 ResultEnum
枚舉類,添加如下代碼:
這邊介紹一下枚舉類的用法。枚舉類的作用實際上就是定義常量,如果不使用枚舉類,通常采用靜態常量來表示:
public static final Integer OK_CODE = 200; public static final String OK_MESSAGE = "成功"; public static final Integer BAD_REQUEST_CODE = 400; public static final String BAD_REQUEST_MESSAGE = "參數錯誤";
這樣的話存在一些問題,一是字段表意不明,特別是看別人的代碼時,會很懵逼;第二當業務規模增大之後,可能要維護成百上千的靜態常量,如果都寫在一個文件裡面,容易造成命名混淆,閱讀也比較麻煩。
然後使用枚舉類定義常量就比較方便,相當於一個接口,使用時隻需要封裝內部的數據類型,並且限定數據域。而且對於不同的枚舉變量,可以調用不同的處理方法(實現枚舉類的抽象方法可以做到這一點)。關於枚舉類的一些知識點匯總如下:
- 使用enum定義的枚舉類默認繼承瞭java.lang.Enum,實現瞭java.lang.Comparable接口,且不能繼承其他類,也不可以被繼承。但枚舉類可以實現一個或多個接口;
- 枚舉類的所有實例必須放在第一行顯示,不需使用new,不需顯示調用構造方法,每個變量都是public static final修飾的,最終以分號結束。在之後的反編譯中,我們就可以理解枚舉類其實也是顆語法糖;
- 枚舉類的構造方法是私有的,默認的就是 private,所以不用再添加 private;
枚舉類內部常用的方法:
valueOf()
:返回當前枚舉類的name屬性,如果沒有,則throw new java.lang.IllegalArgumentException();values()
:是編譯器自動生成的方法,Enum中並沒有該方法,返回包括所有枚舉變量的數組;toString()
和name()
:兩個方法一樣,返回當前枚舉類變量的name屬性,如果覺得不夠用,可以覆蓋默認的toString
,結合SWITCH CASE
來靈活的實現toString()
方法;ordinal()
:枚舉類會給所有的枚舉變量一個默認的次序,該次序從0開始,是根據我們定義的次序來排序的。而ordinal()方法就是獲取這個次序(或者說下標);compareTo()
:比較的是兩個枚舉變量的次序,返回兩個次序相減後的結果;
定義瞭枚舉類之後,在類的上面添加 lombok 的 @Getter
註解,給對象的每個屬性添加 getter 方法,方便後面獲取常量。例如要獲取 OK
的狀態碼,就可以這樣寫:
ResultEnum.OK.getCode()
這邊再解釋下 @Data
、@Getter
和 @Setter
的區別:
@Data
:註解在類上;提供類所有屬性的 getter 和 setter 方法,此外還提供瞭equals、canEqual、hashCode、toString 方@Getter
:註解在屬性上:為屬性提供 getter 方法;註解再類上表示當前類中所有屬性都生成getter方法@Setter
:註解在屬性上:為屬性提供 setter 方法;註解再類上表示當前類中所有屬性都生成setter方法
三、添加統一結果類
還是在 responseEntity
目錄下面,建一個 ServerResponse
類,添加如下代碼:
@Data public class ServerResponse { private Boolean success; private Integer code; private String message; private Object data; // 構造方法設為私有 private ServerResponse() {} public static ServerResponse ok(Object params) { ServerResponse serverResponse = new ServerResponse(); serverResponse.setSuccess(ResultEnum.OK.getSuccess()); serverResponse.setCode(ResultEnum.OK.getCode()); serverResponse.setMessage(ResultEnum.OK.getMessage()); // 成功展示默認提示信息 serverResponse.setData(params); // 返回傳入的參數 return serverResponse; } public static ServerResponse badRequest(@Nullable String message) { ServerResponse serverResponse = new ServerResponse(); serverResponse.setSuccess(ResultEnum.BAD_REQUEST.getSuccess()); serverResponse.setCode(ResultEnum.BAD_REQUEST.getCode()); serverResponse.setMessage(message != null ? message : ResultEnum.BAD_REQUEST.getMessage()); // 校驗失敗傳入指定的提示信息 serverResponse.setData(null); // 校驗失敗不返回參數 return serverResponse; } }
在上面的代碼中,成員變量和構造方法都是私有的,隻有靜態方法向外暴露。然後處理成功的方法,message
展示默認提示信息,即定義在枚舉類裡面的常量,data
是需要傳給前端的 JSON 參數;處理參數錯誤的方法,message
展示傳進去的錯誤信息,如果傳的是 null
,則展示默認提示信息,即定義在枚舉類裡面的常量,data
是傳給前端的參數,但是在參數錯誤的情況下就不需要傳瞭,因此是 null
。
這邊有一個問題,暫時不清楚 Java 是否支持函數參數可選,本人測試發現定義函數的時候有參數,但是調用的時候不傳,IDE 會給錯誤提示,因此這裡通過傳 null 來解決
四、控制層返回
在定義瞭統一結果類之後,就可以在接口中使用瞭。還是用之前那個方法,通過 POST 請求獲取用戶信息,再原封不動返回過去:
@PostMapping("validateUser") public ServerResponse userValidate(@RequestBody @Validated UserDTO userDTO) { return ServerResponse.ok(null); }
這邊先給參數傳 null
,不給前端進行返回,看一下響應的結果:
然後傳遞參數:
@PostMapping("validateUser") public ServerResponse userValidate(@RequestBody @Validated UserDTO userDTO) { return ServerResponse.ok(userDTO); }
看一下響應的結果:
五、異常處理類使用統一響應體
然後我們給異常處理的方法也添加統一響應體:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = MethodArgumentNotValidException.class) public ServerResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { // 註意傳進去的表達式有可能是 null // 因此在 ServerResponse 對 message 是否為 null 進行瞭判斷 // 如果是 null 就展示默認的提示內容 return ServerResponse.badRequest(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()); } // 其他異常處理方法 }
然後我們模擬一下參數異常的情況:
這樣看起來是正常瞭,但是存在一個問題,後端判斷參數異常的時候,因為我們捕獲瞭異常,所以返回給前端的狀態碼還是 200 ,如何讓狀態碼改為 400 呢?在 SpringBoot 中指定 HTTP 狀態碼主要有三種方式:
HttpServletResponse
@ResponseStatus
ResponseEntity
這邊使用第三種方式,具體的用法看一下代碼應該就明白瞭。我們把剛才統一結果類的方法修改下:
public static ResponseEntity<ServerResponse> badRequest(@Nullable String message) { ServerResponse serverResponse = new ServerResponse(); serverResponse.setSuccess(ResultEnum.BAD_REQUEST.getSuccess()); serverResponse.setCode(ResultEnum.BAD_REQUEST.getCode()); serverResponse.setMessage(message != null ? message : ResultEnum.BAD_REQUEST.getMessage()); // 校驗失敗傳入指定的提示信息 serverResponse.setData(null); // 校驗失敗不返回參數 return new ResponseEntity<>(serverResponse, HttpStatus.BAD_REQUEST); // 使用 ResponseEntity 對象設置響應狀態碼 }
可以看到我們用一個 ResponseEntity
對象包裹瞭我們封裝的響應體,然後返回瞭這個對象。其中第二個參數就是狀態碼,HttpStatus.BAD_REQUEST
就代表 400 。然後我們還要修改下異常處理類的返回類型:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = MethodArgumentNotValidException.class) public ResponseEntity<ServerResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { return ServerResponse.badRequest(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()); } // 其他異常處理方法 }
再來調試一下,這下狀態碼正常瞭:
在統一結果類中所有的方法都根據上面的示例進行修改即可,我這邊添加瞭幾個方法,給各位參考下,具體可以根據業務場景進行添加:
@Data public class ServerResponse { private Boolean success; private Integer code; private String message; private Object data; // 構造方法設為私有 private ServerResponse() {} /** * 200 請求成功 * @param params 傳給前端的參數 * @return ResponseEntity<ServerResponse> */ public static ResponseEntity<ServerResponse> ok(Object params) { ServerResponse serverResponse = new ServerResponse(); serverResponse.setSuccess(ResultEnum.OK.getSuccess()); serverResponse.setCode(ResultEnum.OK.getCode()); serverResponse.setMessage(ResultEnum.OK.getMessage()); // 成功展示默認提示信息 serverResponse.setData(params); // 返回傳入的參數 return new ResponseEntity<>(serverResponse, HttpStatus.OK); } /** * 201 創建成功 * @return ResponseEntity<ServerResponse> */ public static ResponseEntity<ServerResponse> created() { ServerResponse serverResponse = new ServerResponse(); serverResponse.setSuccess(ResultEnum.CREATED.getSuccess()); serverResponse.setCode(ResultEnum.CREATED.getCode()); serverResponse.setMessage(ResultEnum.CREATED.getMessage()); // serverResponse.setData(null); return new ResponseEntity<>(serverResponse, HttpStatus.CREATED); } /** * 204 請求成功,沒有響應體 * @return ResponseEntity<ServerResponse> */ public static ResponseEntity<ServerResponse> noContent() { return new ResponseEntity<>(null, HttpStatus.NO_CONTENT); } /** * 400 參數錯誤 * @param message 自定義錯誤信息 * @return ResponseEntity<ServerResponse> */ public static ResponseEntity<ServerResponse> badRequest(@Nullable String message) { ServerResponse serverResponse = new ServerResponse(); serverResponse.setSuccess(ResultEnum.BAD_REQUEST.getSuccess()); serverResponse.setCode(ResultEnum.BAD_REQUEST.getCode()); serverResponse.setMessage(message != null ? message : ResultEnum.BAD_REQUEST.getMessage()); // 校驗失敗傳入指定的提示信息 // serverResponse.setData(null); // 校驗失敗不返回參數 return new ResponseEntity<>(serverResponse, HttpStatus.BAD_REQUEST); // 使用 ResponseEntity 對象設置響應狀態碼 } }
此外,@ResponseStatus
也是一種設置狀態碼常用的方法,隻需要在 Controller 方法中加一個註解就可以:
@PostMapping("validateUser") @ResponseStatus(code=HttpStatus.BAD_REQUEST, reason="參數異常") public ServerResponse userValidate(@RequestBody @Validated UserDTO userDTO) { return ServerResponse.ok(userDTO); }
到此這篇關於淺談SpringBoot如何封裝統一響應體的文章就介紹到這瞭,更多相關SpringBoot封裝統一響應體內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- SpringBoot 統一公共返回類的實現
- Springboot處理異常的常見方式
- SpringBoot參數校驗的方法總結
- springboot 實戰:異常與重定向問題
- 如何設計一個安全的API接口詳解