API 接口響應規範設計

引言

最近發現了使用標準的HTTP狀態碼出現了沒法準確表達業務的問題。java

登陸狀態,一樣是401未受權,須要表達用戶名或密碼錯誤、驗證碼錯誤等多種場景,歷史的處理方式沒法知足場景。算法

考慮參照各大廠商規範,制定新的接口響應規範。json

接口設計

狀態碼 VS 自定義Code

以國內騰訊、阿里、京東、微博爲首的API響應規範以下:小程序

在《阿里巴巴開發手冊(泰山版)》中已經給出了通用的業務code規範。微信小程序

{
  "code": 0,
  "message": "OK",
  "data": {}
}

全部接口的狀態碼都是200,經過code區分業務。在微信小程序裏尤甚,wx.request的響應,401500也算success,只有無響應(數據包丟了)才走errorapi

最初的實現

最初設計嘗試使用自定義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嘗試:

image.png

image.png

image.png

框架調用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

image.png

核心思想是使用裝飾器與RequestMappingHandlerAdapter進行實現,具體代碼請參考https://www.tuicool.com/artic...

注:我的以爲代碼寫得不規範,注意適當參考,使用ListIteratorApplicationListener實現會更優雅。

新規範

在綜合參考了GoogleMicrosoftTwitter的接口設計後,決定仍是放棄國內的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區分業務狀態。

相關文章
相關標籤/搜索