最近發現了使用標準的HTTP
狀態碼出現了沒法準確表達業務的問題。java
登陸狀態,一樣是401
未受權,須要表達用戶名或密碼錯誤、驗證碼錯誤等多種場景,歷史的處理方式沒法知足場景。算法
考慮參照各大廠商規範,制定新的接口響應規範。json
以國內騰訊、阿里、京東、微博爲首的API
響應規範以下:小程序
在《阿里巴巴開發手冊(泰山版)》中已經給出了通用的業務code
規範。微信小程序
{ "code": 0, "message": "OK", "data": {} }
全部接口的狀態碼都是200
,經過code
區分業務。在微信小程序裏尤甚,wx.request
的響應,401
、500
也算success
,只有無響應(數據包丟了)才走error
。api
最初設計嘗試使用自定義code
的形式,須要從原響應架構遷移,爲了下降遷移成本,設計方案以下(爲了讓圖片看起來更清晰,換成了白色主題):微信
指望結果,在原API
不變的狀況下:架構
@GetMapping public List<Student> getAll() { }
返回結果從:app
[{ "id": 1, "name": "Hello Kitty" }, { "id": 2, "name": "史努比" }]
變動爲:框架
{ "code": 0, "message": "OK", "data": [{ "id": 1, "name": "Hello Kitty" }, { "id": 2, "name": "史努比" }] }
其餘code
不爲0
的狀況,統一在異常處理中進行變動。
通過一系列的DEBUG
嘗試:
框架調用API
方法處理HTTP
請求,返回結果須要經過實現HandlerMethodReturnValueHandler
接口的相關類中的handleReturnValue
方法進行處理。Spring Boot
中有諸多響應值處理器,框架根據當前返回的是頁面、普通對象仍是響應式對象來調用相關的處理器。
核心思想就是自定義處理器,將返回值包裝爲code
格式的類型。
在WebMvcConfigurer
接口中已經定義過相關添加返回值處理器的方法addReturnValueHandlers
:
void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) { }
經測試無效,新加的handler
添加到List
中後,Spring
其他的handler
會添加到自定義handler
的前面,因Spring
選擇處理器是根據最早匹配算法進行選擇,因此選擇不到自定義的處理器。
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } if (handler.supportsReturnType(returnType)) { return handler; } } return null; }
Spring Boot
默認存在15
個返回值處理器,而處理普通對象json
響應的處理器爲RequestResponseBodyMethodProcessor
,另外一種實現方案就是替換掉默認的RequestResponseBodyMethodProcessor
。
核心思想是使用裝飾器與RequestMappingHandlerAdapter
進行實現,具體代碼請參考https://www.tuicool.com/artic...
注:我的以爲代碼寫得不規範,注意適當參考,使用ListIterator
與ApplicationListener
實現會更優雅。
在綜合參考了Google
、Microsoft
、Twitter
的接口設計後,決定仍是放棄國內的API
形式,跟隨Google
的設計規範,仍然沿用狀態碼的形式。(有人說Microsoft
的規範更好,可是我沒太看懂)
Google API
設計規範:https://cloud.google.com/apis...
請求成功(狀態碼爲200
時)的設計不變:
[{ "id": 1, "name": "Hello Kitty" }, { "id": 2, "name": "史努比" }]
失敗時添加state
狀態與message
錯誤詳細信息:
{ "state": "INVALID_ARGUMENT", "message": "密碼不能爲空" }
HTTP 狀態碼 | 狀態 | 錯誤詳細信息 |
---|---|---|
400 | INVALID_ARGUMENT |
密碼不能爲空 |
401 | CODE_INVALID |
驗證碼無效 |
401 | CREDENTIALS_INVALID |
用戶名或密碼錯誤 |
控制器負責拋出攜帶詳細錯誤信息的異常,異常處理器根據異常類型肯定返回的狀態碼與業務狀態。
@ExceptionHandler(InvalidArgumentException.class) public HttpErrorResponse invalidArgumentExceptionHandler(InvalidArgumentException exception, HttpServletResponse response) { return this.error(HttpErrorType.INVALID_ARGUMENT, exception, response); } private HttpErrorResponse error(HttpErrorType type, RuntimeException exception, HttpServletResponse response) { response.setStatus(type.getCode()); return new HttpErrorResponse(type.getValue(), exception.getMessage()); }
除非特殊要求,正常狀況下,優先推薦選擇標準HTTP
狀態碼形式設計API
,使用state
區分業務狀態。