微服務後端接口開發及返回值規範札記

API設計規範

1. 原則

    原則是規範的基本設計思路, 在規範中無法找到相應的設計細節時,則應該按照設計的初衷,思路或者原則來判斷應該如何進行設計

  • 接口應該儘量的少, 後端接口不應該因前端的簡單格式調整或者查詢字段的多少有多調整,請注意一個後端接口不應該僅僅服務於一個前端需求,而應該是服務於一堆類似的各種可能的前端資源請求需求
    理由:因前端需求變更通常大於後端變更,接口設計時應考慮多種設計需求,後端接口儘量不受前端需求變更影響
  • 出於前端或後端性能考慮而增加冗餘接口是允許的
  • 規範爲避歸這種問題而設計, 很理想, 各個項目可根據自身實際情況和綜合考慮成本與可行性進行適當的裁減或部分採納甚至修改,但建議儘量多的遵從

2. API命名

2.1 API命名風格的選擇

    關於接口風格的設計一直爭論不休, 請看這裏 https://www.zhihu.com/question/28570307

    根據接口設計目標的不同, 可以主要分成以下兩類:

  • 給項目內部前端開發調內部接口
    設計目標: 部分接口對性能要求高, 接口邏輯場景複雜
    建議設計: 注意區分資源型接口與非資源型接口, 建議優先採用Restful設計, 但注意不要被Restful束縛, 性能是最重要的
    設計理由: Restful有太多的優點, 可以促進對系統設計的深入分析, 可以方便的生成文檔, 更加具體的規範和約束, 有利於接口的一致性, 方遍形成外部接口, 但因它的設計並未優先考慮性能, 所以需要有所取捨和折中,不要爲了Rest而Rest
  • 給第三方調用的外部開放接口
  • 服務間調用的內部接口

2.2 路徑名

  • 路徑名稱不允許使用/結尾(需注意檢查/結尾的路徑是否是有效的)
    理由:搜索引擎優化角度考慮,一個同樣的頁面不應該有兩個地址,會被搜索引擎認爲是作弊,搜索引擎對無/的地址支持更加友好

  • 蛇底式命名風格(snake_case)
    關於分割 分隔符"-“與”_"的選擇:

    1. -分隔符從大小寫的鍵盤錄入的角度屬於小寫(因不需要按Shift鍵),與URL中保持小寫原則相一致(url的特定部分不區分大小寫,且小寫字母更容易錄入)【缺點】
      -其實更容易識別 _會與下劃線格式混淆(尤其在doc中書寫地址的時候)【缺點】
    2. -用於連接和_用於連接語義上有區別,_通常用於連接含義相關的部分,而-用於連接相互較爲獨立的部分,這樣看起來_語義上更接近 【優點】
      2.1 Google SEO 遵循這個語義 把-當空格處理,認爲是三個關鍵詞 會把_直接去掉,認爲整體是一個單詞
      2.2 大部分文本編輯器也遵循這個語義,雙擊選擇時 _連接會認爲是同一個單詞同時選擇,而-分隔的兩個單詞不會同時選中
      2.3 鑑於get傳參與post傳參的一致性,post body中的json也建議採用同樣的命名規則。便於程序處理【可考究】
    3. 目前蛇底式與駝峯式較爲常見,而烤串式不常見【優點】

    snake_case: 微信開放平臺,騰訊開放平臺,開源中國 百度,阿里雲 gitee.com
    camelCase: 京東,百度,愛奇藝
    小寫無連字符:百度百科

2.2.1 資源類接口(resource) 【RestFul風格】

    資源名(resources_name)既爲接口名稱,名詞複數形式(批量獲取時語義更加),儘量一個單詞(簡潔) ;

    RestFul風格, 基於資源的設計,HTTP Method作爲動詞,表示要對資源執行的動作

  • 單資源怎刪改查
【GET】          /users/1           # 查看某個用戶信息
【POST】         /users             # 新建用戶信息【非冪等, 多次執行可能會新建多個用戶】
【PUT】          /users/1           # 更新用戶信息【冪等, 同一請求多次執行結果一定相同】, 局部替換或追加資源的字段, PUT也可以用於新增,但需是由客戶端能夠確定唯一主鍵的場景
【PATCH】        /users/1           # 對用戶信息執行某一個動作【非冪等, 同一請求多次執行可能獲得不同結果】, 例如登錄錯誤計數+1 多次執行資源產生的結果不同
【DELETE】       /users/1           # 刪除用戶信息

    設計理由: PUT用於局部更新並不符合HTTP1.1和RFC5789規範,但依然這樣設計,理由如下

  • GITLAB這樣去實現, 雖然它不符合規範, Kuberenetes API則遵循了標準

  • 如果PUT只能用於全部替換, 那這個動詞似乎沒什麼意義,而只是PATCH的一個特例

  • 簡單批處理請求 (batch)

    同時處理多個資源, 但對每個資源的處理是一致的,比如更新同樣的資源

【GET】          /users             # 查詢用戶信息列表
【PUT】          /users/1,2,3       # 更新用戶信息【冪等, 同一請求多次執行結果一定相同】, 局部替換資源的字段,
【PATCH】        /users/1,2,3       # 對用戶信息執行某一個動作【非冪等, 同一請求多次執行可能獲得不同結果】, 例如登錄錯誤計數+1 多次執行資源產生的結果不同
【DELETE】       /users/1,2,3       # 刪除用戶信息
  • 複雜批處理請求/混合請求(mixed)

    使用RFC6902 JSON Patch規範完成批量處理請求 https://tools.ietf.org/html/rfc6902

    原理:同時處理多個資源,但對每個資源的處理是不一致的,更新不一樣的字段,甚至處理的動作也是不一致的
建議將動作封裝到body中並組合成一個新的body發送給後端
PATCH JSON Patch規範進行批量操作:

  • 聚合請求/頁面級請求

    用於首頁聚合顯示,展板展廳類頁面
待研究… 目前建議後端封裝專用接口,前端一次請求

2.2.2 非資源類接口(non-resource)【JSON-RPC風格】

禁止使用其它HTTP Method
POST  數據提交(意圖修改服務器端的數據)
GET   數據獲取  

    接口名使用動賓結構 通常接口對應一個動作或狀態而不是操作一個資源, (例如:健康檢查, 日誌查看,附件上傳)
可考慮名稱細節繼續規範化

2.2.3 資源間邏輯關係

    兩個資源之間可能存在關係,例如班級的學生,老師的學生,用戶的角色
    目前在資源從屬關係上沒有太多的實踐經驗,建議參考gitlab資源接口設計,gitlab從存在較爲複雜的邏輯關係

通常建議:

  • 具有明確從屬關係資源且不具備全局特點的接口,可以設計爲類似於resource/subsource的形式
  • 具有全局特點的資源優先實現頂層接口, 優先設計全局接口resouces1, 可選設計爲resource2/subsource1形式,用於應對多對多關係,後者只返回具有從屬關係的部分數據
  • 建議設計爲子資源而不是資源的一個屬性(可以是數組類型的屬性),應考慮子資源是否有一定的複雜性(具備多個字段的對象),應考慮子資源是否需要被獨立檢索

2.3 擴展名

  • 接口擴展名不允許帶有框架相關信息
    例如: .action .do
    理由: 安全角度考慮,將保留後臺實現方案,黑客可根據框架日誌漏洞進行鍼對性攻擊
  • 接口擴展名可以使用返回值格式作爲擴展名或無擴展名實現, 建議無擴展名是默認返回格式爲json
    例如: .json .xml
    理由: 前端語言對json最爲友好,畢竟JSON是JavaScript Object Notation

2.4 傳參規範

2.4.1 參數位置

  1. 用於排序,搜索,過濾等請求條件參數應放在請求字符串列表,不允許放在body中(尤其是post請求)
    理由:便於用戶視覺識別或快速的手動在地址欄修改
  2. 用戶排序,搜索,過濾等請求條件參數應在瀏覽器地址欄中顯示(尤其適用react-route技術實現的應注意)
    理由:便於用戶對連接進行分享
  3. 涉及用戶敏感隱私的字段不允許出現在瀏覽器地址欄中,應放在請求body中
    例如:密碼,Token
    理由:防止被用戶身旁的人意外觀察而被記憶

2.4.2 參數名稱

  • 分頁及排序參數

    參數規範名 參數作用 位置 默認值 備註
    page 當前頁碼 地址欄 1
    per_page 每頁條目數 地址欄 20 這個值必須有,用以計算需要返回的總條目數
    limit 單次請求總條目數限制 地址欄 100 防止意外查詢過量數據, 後臺也應做最大值限制
    sort 升序或降序 地址欄 desc 降序通常常用,更關心最近操作的條目
    order_by 排序列 地址欄 id 建議不要亂序或隨機, 更關心最近插入的條目

理由:
    sort與order_by的參考gitlab api設計 order_by後面加上by可以避免與sort之間發生歧義 明確表示是排序字段或者排序列

  • 條目篩選參數(行篩選, 返回滿足條件的行)

    行篩選的篩選參數名應與資源數據返回中的參數名一致,故建議採用蛇底式(snake_case)命名規範

  • 內容篩選參數(列篩選或關係篩選)

    內容篩選性參數均爲布爾型,注意遵循2.3.3中對布爾值,未傳值和傳空值兩種特殊情況的處理規範

    參數規範名 參數作用 位置 備註
    simple 只返回簡要信息 地址欄 參考5.1.1 兩分組列篩選設計規範
    [groupname] 附加分組列信息 地址欄 參考5.1.2 多分組列篩選設計規範

2.4.3 參數值類型

參數類型 取值規範 備註
布爾型 真: true或1 假: false或0 未傳值,按假值處理,傳空值,按真值處理
數組型(GET傳參) 1,2,3 可用於多條目修改刪除, post傳參時按相關content-type格式執行
時間日期型(優先) 11位 GMT/UTC時間戳 不帶有時區信息,防止在多語言操作系統中造成服務器與客戶端理解不一致,防止前端或後端的設計依賴特定的時間格式, 缺點不能飆到1970年前的時間
時間日期型(備選) ISO8601規範 需要表達1970年以前的時間時考慮
枚舉型 枚舉值或枚舉名 是不太判斷得出哪個優先,可實踐後獲得結論
待補充…

3. 請求規範

  • GET請求不允許攜帶Body
    理由: 不符合HTTP規範,有些代理服務器會直接將GET請求的Body丟棄(這些代理服務器或中間服務器不一定在我們能控制的範圍內)

  • 請求接口是應準確攜帶content-type頭部, 用於表徵請求的body中的數據類型,後端需要具此來解析對應body中的數據
    建議: 請求json格式的接口。建議攜帶content-type=application/json

    其它常用content-type值參考下表

    Content-Type:application/json                      (推薦,JsonBody提交, 尤其是參數複雜時, 可清晰的表達嵌套數據,可能是趨勢,案例:京東,csdn,airbnb,bilibili)
    |-如果是base64個是的圖片, 建議圖片編碼爲字符串之後以json格式返回給前端
    Content-Type:application/x-www-form-urlencoded     (推薦,普通表單提交, 目前這種方式仍然居多)
    Content-Type:multipart/form-data                   (文件上傳時, 用於帶有文件上傳的表單提交)
    Content-Type:application/x-protobuf                 Google ProtoBuf格式, 在前端使用protobuf格式目前仍存在爭議, 但仍然可見使用案例(例如: 知乎前端數據分析接口)
    Content-Type:text/plain                             普通文本
    Content-Type:text/html; charset=utf-8               HTML文件
    Content-Type:text/css                               CSS文件
    Content-Type:application/javascript; charset=utf-8  JS文件
    Content-Type:image/png                              PNG圖片
    Content-Type:image/gif                              GIF圖片
    Content-Type:image/x-icon                           ico圖片
    Content-Type:image/webp                             WEBP圖片
    content-type:image/svg+xml                          SVG圖片
    
    

4. 響應規範

  • 賬戶授權等敏感數據應存儲於Cookie當中, 並設置HTTP-only屬性,用以阻止js腳本讀取權限,禁止存儲於LocalStorage等其它存儲技術
    理由: 目前只有cookie存儲具備安全防禦機制,也就是Http-only, 防禦可能的跨站請求攻擊(風險極大)

  • 響應後content-type必須與響應body的數據類型一致,前端會據此對響應body進行類型轉換
    例如: json的body如果返回text/plain類型 那麼前端接受時將識別body爲字符串而不是json對象
    常用content-type值參考3.

    最合適的Ajax內容編碼類型 https://segmentfault.com/a/1190000006871099

4.1 數據分頁的響應規範

    建議採用 Link Header機制返回分頁元信息, 以此不去破壞返回Body中存儲的是資源內容的restful設計思想
https://git.d.com/help/api/README.md#pagination-link-header

4.2 錯誤碼

4.2.1 返回碼設計

  • 設計目標
    爲了便於分析統計軟件可以快速的統計異常
    爲了快速確定誰要對錯誤進行處理
    爲了快速定位產生異常的模塊甚至代碼位置
    儘量不破壞HTTP返回碼的語義
    前端程序分類處理的便利

    錯誤碼爲7位數據

  • 0表示正常
    理由: 因正常只有一種,而錯誤有很多種, 0爲正常便於程序中用if判斷

  • 第1位表示錯誤來源
    2 客戶端錯誤(用戶自行解決,或管理員協助通過系統配置解決)
    4 客戶端錯誤(需要研發介入,修復需要前端修改代碼)
    5 服務端錯誤(臨時異常,不需要研發介入,修復不需要修改代碼)
    6 服務端錯誤(軟件bug)

    第2-3位表示服務代碼(便於統計)
    第4-7位表示具體錯誤(前端引導提示和後端查詢)

4.2.2 全局結構

{
  "meta":{
    "code":"10000",   
    "msg": "ok";
  }
  "data": {} or [], # 內部格式由具體業務決定
}

4.2.3 異常結構

4.2.3.1 服務端異常結構(僅開發環境)
  • 多行文本類異常信息(僅開發環境)
        有些異常的反饋信息文本有多行,多行文本直接以字符串格式包裝進json的話,前端的顯示非常的醜陋(出現大量的\r\n)符號,因此建議將多行文本封裝爲字符串列表,以優化瀏覽器端顯示

    ...
    "data": {
      "type":"text",
      "ip": "暴異常的結點IP",
      "exception": [
        "line1",
        "line2",
        "line3"
      ];
    }
    ...
    
  • 異常棧類型異常結構(僅開發環境)

    異常棧列表是有序的, 所以後端實現時應注意保序

...
"data": {
  "type":"exceptions",
  "ip": "暴異常的結點IP",
  "exception": {
    "exception_name1": "exception_message1",
    "exception_name2": "exception_message2",
    "exception_nam32": "exception_message3",
  }
}
...
  • 棧追蹤(stacktrace)類型異常結構(僅開發環境)

    ...
    "data": {
      "type":"stacktrace",
      "ip": "暴異常的結點IP",
      "exception": 待定...
    }
    ...
    
  • 其它對象類型異常結構(僅開發環境)

    ...
    "data": {
      "type":"error",
      "ip": "暴異常的結點IP",
      "exception": {
         自定義Object
      }
    }
    ...
    
4.2.3.2 參數錯誤異常結構(全部環境)

    錯誤碼: 2000301

    參數錯誤信息反饋在data域, data是一個數組,表示一組參數的錯誤,field字段值應該與參數名稱一致,用於幫助前端實現時定位錯誤的輸入框,message可供前端參考提示,但前端可以選擇提示此信息也可以考慮以更人性化的方式給出合適的提示

...
"data": [{
  "field":"username",
  "message": "用戶名不滿足安全要求";
},{
  "field":"mobile",
  "message": "手機號格式不正確";
}]
...

5. 其它API功能設計

5.1 內容篩選(列篩選或關係篩選)

5.1.1 兩分組列篩選

    接口返回的列字段中,可以分成兩組,一組是簡要信息,另外一組是擴展信息,建議使用布爾型參數simple進行列篩選

  • simple=true時 返回簡要信息
  • simple=false時 返回完整信息(簡要信息+擴展信息)
  • simple未傳參時 返回完整信息(例如,符合語義,未傳參未設置false)

    場景示例: 獲取用戶列表接口, simple=true時只返回用戶信息,simple=false時,返回用戶信息及用戶所屬的資源信息(用戶的項目,用戶的…)

5.1.2 多分組列篩選

    接口返回的列字段中,會被分爲多個分組,爲每個列分組其一個有含義的名稱,當傳參groupname=true或傳遞空值時 ,則返回,未傳參或傳遞false時,不返回屬於此分組的列

例如:
GET users?role=&project=
則返回用戶基本信息,用戶所屬用戶組信息,用戶擁有項目信息

5.1.3 複雜字段篩選

    複雜字段篩選,可能會要求控制粒度到每個字段級別,建議使用GraphQL技術實現

A 返回碼與HTTP狀態值表

0表示正常【可選實現, 不確定這麼做有什麼實質性的好處】
0(200) 通用正常, 如果不知道用什麼, 就是用這個
0(201) 創建成功, 可用於Rest接口的POST新增對象 
0(202) 可用於異步請求, 長任務, 利於短信, 郵件發送, 提示瀏覽器可以不必保持連接
0(204) 服務器成功響應,但是沒有返回數據回, 通常可用於Rest接口的Delete功能
錯誤碼爲7位數據
第1位表示錯誤來源(放置於第一位, 便於程序判斷和統計分類)  
 |-2 客戶端錯誤(用戶自行解決,或管理員協助通過系統配置解決)
 |-4 客戶端錯誤(需要研發介入,修復需要前端修改代碼)
 |-5 服務端錯誤(臨時異常,不需要研發介入,修復不需要修改底阿媽)
 |-6 服務端錯誤(軟件bug)
第2-3位表示服務代碼  
 |-00 系統錯誤(一般爲後端bug或故障導致,或多個服務通用的錯誤)
   |-400xxxx         客戶端錯誤(返回結構中給出錯誤細節)  
     |-4000xxx(4xx)    HTTP客戶端標準錯誤映射(一般未請求格式錯誤,需研發介入修正)
       |-4000400(400)    其它未細分的請求格式不正確(需研發介入修正)
       |-4000404(404)    請求資源【接口】不存在(通常需要研發或運維介入修正, 注意與數據不存在區分)
       |-4000405(405)    不支持的http-method(method錯誤一定是研發疏忽,需要研發介入修正)
       |-4000415(415)    不支持的Content-Type(需要介入修正)
       |-4000601(400)    Json語法不正確(需前端研發接入修正)
   |-200xxxx         用戶可自行修正的系統公共錯誤(考慮前端處理便利, 根據所需處理進行細分,可能存在不同邏輯用返回值區分,相同邏輯不同提示用錯誤細節區分)
     |-20001xx(401)    認證類問題(表達用戶提供的身份信息的異常)
       |-2000100(401)    其它位置的認證類問題
       |-2000101(401)    需要認證類接口但未提供任何認證信息, 但不確定登錄後是否有接口權限或數據權限,前端應根據情況適當進行登錄引導  
       |-2000102(401)    認證信息已失效(例如Token)
       |-2000103(401)    認證信息不可用(例如Token)
     |-20002xx(200)    鑑權類問題(表達用戶提供合理身份信息後對接口和數據的所有權限異常)
       |-2000201(200)    請求的【數據】無權限訪問,但接口有權限,無法通過登錄來解決問題,用戶只能放棄訪問,或聯繫管理員授權
       |-2000202(200)    請求的【接口】無權限訪問,通常無權限的接口不應引導調用,但也可能引導用戶申請權限  
     |-20003xx         請求數據的格式類問題
       |-2000301(200)    其它非細分的請求格式或參數不正確
       |-2000304(200)    請求的【數據】不存在,注意與接口不存在相區分,接口不存在需要研發介入,數據不存在用戶可自行修正
   |-500xxxx   服務端錯誤(返回結構中開發環境給出細節,生產環境之給響應值,不給細節)
     |-50001xx(503)  數據庫相關網絡異常
       |-5000100(503)  數據庫連接異常(配置錯誤,網絡異常等)
       |-5000101(503)  數據庫連接異常(數據庫被正常關閉)
       |-5000102(503)  數據庫連接異常(數據庫異常退出)
     |-5000200(503)  負載均衡結點(無可用結點,結點配置錯誤或網絡連接異常)
     |-50005xx(503)  服務端HTTP網絡異常(臨時網絡故障[可能自動修復], 配置錯誤[不可自動修復])
       |-5000502(503)  HTTP協議網絡連接異常 對應HTTP502
       |-5000503(503)  HTTP協議網絡連接異常 對應HTTP503 與之上的區別是tcp可以是否可以連通
       |-5000504(503)  HTTP協議網絡連接異常 對應HTTP504 (連接超時)
   |-600xxxx(500)   初步識別的軟件bug(需程序員介入處理)
     |-6000000(500)   服務端未知錯誤(軟件中未識別和處理的軟件bug)
       |-6000001(500)   空指針異常
     |-60001xx(500)     數據庫相關錯誤(未判斷出來的)
       |-6000101(500)     SQL語法錯誤
       |-6000102(500)     數據庫返回值不符合預期(SelectOne時返回多條記錄)

 |-01 用戶權限系統(非通用部分)
 |-02 文件管理/上傳下載系統
 |-xx 其它服務代碼(一般爲用戶參數輸入錯誤,業務系統建議使用30以上代碼)
第4-7位表示具體錯誤 
 |-xx 具體的錯誤代碼(由具體服務定義)

B 參考文獻

錯誤碼設計 https://blog.csdn.net/yzzst/article/details/54799971
聊聊RESTful https://howardwchen.com/2017/09/18/talk-about-restful-popular-api-design-1/
Kubernetes API規約 https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#patch-operations
【主要參考】Restful風格的接口設計 https://juejin.im/entry/59b8d34c6fb9a00a4455dd04
騰訊開放平臺 http://open.qq.com/
URI設計原則 https://stackoverflow.com/questions/1619152/how-to-create-rest-urls-without-verbs/1619677#1619677
Gitlab API V3 https://link.zhihu.com/?target=https%3A//developer.github.com/v3/
返回值的處理 https://www.v2ex.com/t/340607?p=2
RESTful API定義及使用規範 https://zhuanlan.zhihu.com/p/31298060
HTTP狀態值https://zhuanlan.zhihu.com/p/31298060
http://wiki.open.qq.com/wiki/v3/user/get_info open.weibo.com
http://wiki.open.qq.com/wiki/website/%E5%BE%AE%E5%8D%9A%E7%A7%81%E6%9C%89%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E
https://docs.open.alipay.com/common/105806
http://open.weibo.com/wiki/Error_code
https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318634&token=&lang=zh_CN
http://lbs.amap.com/api/webservice/info/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章