先後端接口規範

先後端接口規範  來源: https://github.com/f2e-journey/treasure/blob/master/api.md

隨着先後端分離愈來愈廣泛, 後端接口規範也就愈來愈重要了. 一套良好的接口規範能夠提高工做效率, 減小溝通障礙.html

一般咱們都會採用 REST 方式來提供接口, 使用 JSON 來傳輸數據.前端

名詞 含義
前端 Web前端, APP端, 桌面端等一切屬於用戶界面的這一層
後端 即服務器端, 指一切屬於用戶界面之下的這一層
先後端接口 前端與後端進行數據交互的統稱, 也叫作數據接口, 屬於一種遠程調用, 通常指前端經過HTTP(ajax)請求獲取到的數據或者執行的某項操做. 爲確保先後端(工程師)的協做溝通, 通常由前端和後端一塊兒來定義接口的規範, 規範的內容通常包含接口的地址, 接口的輸入參數和輸出的數據格式(結構), 最終由後端來實現這些規範, 爲前端提供符合規範的接口
[前端] 
--------
   ^
   |
   |
先後端接口
   |
   |
--------
 [後端]

先後端接口協做流程

在開發以前必定要先定義好接口規範, 至於接口應該由前端來定仍是後端來定, 這個還得看公司的具體狀況, 但必定要讓先後端都確認無誤, 特別是接口協商要點.java

以避免出現先後端分離以後最容易出現的扯皮現象. 特別是當你碰到作事不主動(無責任感)的後端, 什麼都要前端來催. 好比什麼接口又缺了一個字段沒有提供啦, 什麼又少了一個接口啦, 等等諸如此類. 後端不去熟悉業務, 也不看界面原型和需求, 只管把接口作完, 任務完成就萬事大吉了, 天天除了等前端通知哪裏要修改, 本身就像沒事人同樣.node

因此說定好接口, 先後端一塊兒來確認好接口是多麼的重要, 否則你就等着乾着急吧. 固然了, 想一次性完美地將全部接口都定義出來, 有點不太現實, 須要調整的狀況在所不免, 因此仍是但願後端可以主動一點, 先後端溝通的時候就輕鬆得多, 你們的效率就都提升了.android

準備環境

接口規範

由前端(APP端)和後端一塊兒協定接口規範的內容, 肯定每個接口的地址(URL), 輸入(request)和輸出(response), 必要的時候詳細註釋每個字段的含義和數據類型.git

具體須要定義哪些接口, 能夠按照下面的思路來整理github

  • 資源接口: 系統涉及到哪些資源, 按照 RESTful 方式定義的細粒度接口
  • 操做接口: 頁面涉及到哪些操做, 例如修改購物車中商品的數量, 更換優惠券等等, 也可使用 RESTful 方式來定義
  • 頁面接口: 頁面涉及到太多接口, 若是是一個個地調用, 會須要不少次請求, 有能夠影響到前端的性能和用戶感知(特別是首屏的體驗), 所以可能須要將這些接口的數據合併到一塊兒, 做成一個聚合型接口提供給前端來使用

接口協商要點

  • 接口必須返回統一的數據結構, 參考後端接口通用規範中接口返回的數據結構
  • 接口查詢不到數據時, 即空數據的狀況下返回給前端怎樣的數據
    • 建議返回非 null 的對應數據類型初始值, 例如對象類型的返回空對象({}), 數組類型的返回空數組([]), 其餘原始數據類型(string/number/boolean...)也使用對應的默認值
    • 這樣能夠減小前端不少瑣碎的非空判斷, 直接使用接口中的數據
    • 例如: result.fieldName
    • 若是 result 爲 null, 可想而知會報錯 Uncaught TypeError: Cannot read property 'fieldName' of null
  • 調用接口業務失敗的經常使用錯誤碼, 例如未受權時調用須要受權的接口返回 "status": 1
  • 接口須要登陸時如何處理, 特別是同時涉及到 Web 端/微信端/App 端, 須要前端針對運行環境判斷如何跳轉到登陸頁面
  • 返回數據中圖片 URL 是完整的仍是部分的
    • http://a.res.com/path/to/img.png 這就是完整的, 前端直接使用這個 URL
    • /path/to/img.png 這就是部分的, 通常省略域名部分, 前端須要本身拼接後才能使用 'http://a.res.com' + '/path/to/img.png'
  • 返回數據中頁面跳轉的 URL 是給完整的仍是部分的
    • 內部頁面返回部分的, 或者只給ID, 由前端本身拼接, 例如只給出商品ID, 讓前端本身拼接商品詳情頁的 URL
    • 外部頁面返回完整的, 例如廣告位要跳轉去谷歌
  • 返回數據中日期的格式, 是使用時間戳仍是格式化好的文字
    • 對於須要前端再次處理的日期值(例如根據日期計算倒計時), 可使用時間戳(簡單暴力), 例如: 1458885313711, 或者參考 Date.prototype.toJSON 提供 ISO 標準格式(例如須要考慮時區時)
    • 對於純展現用的日期值, 推薦返回爲格式化好的文字, 例如: 2017年1月1日
  • 對於大數字(例如 Java 的 long 類型), 返回給前端時須要設置爲字符串類型, 不然 JavaScript 會發生溢出, 形成獲得的數值錯誤
    • 例如: 返回 JSON 數據 {"id": 362909601374617692} 前端拿到的值倒是: 362909601374617660
  • 分頁參數和分頁信息
    • 如何限制只返回 N 條數據(limit 參數)
    • 如何控制每頁的數據條數(pageSize 參數)
    • 如何加載某一頁的數據(page 參數)
      • 第一頁是從 0 開始仍是從 1 開始
    • 如何避免無限滾動加載可能出現的重複數據(採用 lastId 分頁方式, 來避免傳統分頁方式的弊端)
      • 假設數據是按照新增時間倒序排列的
      • 首先加載 2 頁的數據
      • 等了好久
      • 期間新增了不少數據
      • 再獲取第 3 頁數據
      • 此時就可能出現重複數據的狀況, 由於新增的數據都排在最前面, 後面會接着已經加載過數據
    • 分頁信息包含什麼(total, page, pageSize)
    • 分頁信息什麼時候代表已是最後一頁了
      • 請求某頁數據時返回的數據條數 < pageSize
      • 請求某頁數據時返回的數據條數 = 0
      • 若是碰巧最後一頁有 pageSize 條數據, 前端沒法經過數據條數來判斷已經處於最後一頁了

接口定義

全部的接口定義在項目前端靜態文件目錄的 _mockserver.json 文件中, 啓動 puer-mock 服務, 便可使用這些接口得到符合規範的假數據, 也能夠查看接口文檔.web

具體 puer-mock 的詳細使用手冊和 _mockserver.json 如何配置接口請參考 puer-mock 項目, 或者參考項目中已經配置好的其餘接口.ajax

接口協做

因爲接口規範的定義和接口的實際實現是分開的兩個部分, 並且涉及到多人協做, 所以在開發過程當中可能出現接口規範與實現不一樣步, 最終形成實際的接口不符合規範的定義, 接口規範就會慢慢失去存在的意義.json

爲了儘可能避免這種問題, 後端在實現接口的過程當中應該確保與接口規範保持一致, 一旦出現分歧, 必須同步修改接口規範, 儘量保持溝通.

接口文檔(示例)

puer-mock-api-doc-html

後端接口通用規範

接口地址和請求方式

接口根路徑 - Root Endpoint 推薦爲: http://api.yourdomain.com 或者 http://yourdomain.com/api

接口地址即接口的 URL, 定義時使用相對路徑(即不用帶上域名信息), 建議分模塊來定義, 推薦 REST 風格, 例如

  • GET /user/:id 表示獲取用戶信息
  • POST /user 表示新增用戶

接口參數

向接口傳遞參數時, 若是是少許參數能夠做爲 URL query string 追加到接口的 URL 中, 或者做爲 Content-Type: application/x-www-form-urlencoded 放在請求體(body)中(即表單提交的方式)

對於複雜的接口參數(例如嵌套了多層的數據結構), 推薦在 HTTP 請求體(body)中包含一個 JSON 字符串做爲接口的參數, 並設置 Content-Type: application/json; charset=utf-8.

例如

查詢 VIP 用戶的接口

POST /users?limit=10 HTTP/1.1
Content-Type: application/json; charset=utf-8

{
    "name": "hanmeimei",
    "isVip": true
}

接口返回的數據結構

返回的響應體類型推薦爲 Content-Type: application/json; charset=utf-8, 返回的數據包含在 HTTP 響應體中, 是一個 JSON Object. 該 Object 可能包含 3 個字段 datastatusstatusInfo

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
    "data": {},
    "status": 0,
    "statusInfo": {
        "message": "給用戶的提示信息",
        "detail": "用於排查錯誤的詳細錯誤信息"
    }
}
字段名 字段說明
data 業務數據
必須是任意 JSON 數據類型(number/string/boolean/object/array).
推薦始終返回一個 object (即再包一層)以便於擴展字段.
例如: 用戶數據應該返回 {"user":{"name":"test"}}, 而不是直接爲 {"name":"test"}
status 狀態碼
必須是 >= 0 的 JSON Number 整數.
  • 0 表示請求處理成功, 此時能夠省略 status 字段, 省略時和爲 0 時表示同一含義.
  • 非 0 表示發生錯誤時的錯誤碼, 此時能夠省略 data 字段, 並視狀況輸出 statusInfo 字段做爲補充信息
statusInfo 狀態信息
必須是任意 JSON 數據類型.
推薦始終返回一個 object 包含 message 和 detail 字段
  • message 字段做爲接口處理失敗時, 給予用戶的友好的提示信息, 即全部給用戶的提示信息都統一由後端來處理.
  • detail 字段用來放置接口處理失敗時的詳細錯誤信息. 只是爲了方便排查錯誤, 前端無需使用.

例如

  • 接口處理成功時接口返回的數據

    {
        "data": "api result"
        "status": 0
    }
  • 接口處理失敗時接口返回的數據

    {
        "status": 1,
        "statusInfo": {
            "message": "服務器正忙",
            "detail": {
                "exception": "java.util.List"
            }
        }
    }

這樣咱們就能夠很是容易地經過判斷 status 來處理數據了

if (!response.status) { // status 爲 0 或者沒有 status 字段時表示接口成功返回了數據 console.log(response.data); } else { // 失敗 console.error(response.status, response.statusInfo); // 統一由服務端返回給用戶的提示信息 alert(response.statusInfo.message); }

錯誤碼規範: status 字段該如何取值

採用先後端分離開發模式的項目愈來愈多, 前端負責調用後端的接口來展示界面, 若是有界面顯示異常, 須要有快速方便的手段來排查線上錯誤和定位出職責範圍

綜合了經驗總結和行業實踐, 最簡單有效的手段是制定出一套統一的錯誤碼規範, 協助多方人員來排查出接口的錯誤

例如

  • 用戶發現錯誤, 能夠截錯誤碼的圖, 就可以提供有效的信息幫助開發人員排查錯誤
  • 測試人員發現錯誤, 能夠經過錯誤碼, 快速定位是前端的問題仍是後端接口的問題

所以咱們肯定提示信息規範爲: 當後端接口調用出錯時, 接口提供一個用戶能夠理解的錯誤提示, 前端展現給用戶錯誤提示和錯誤碼, 給予用戶反饋

對於錯誤碼的規範, 參考行業實踐, 大體有兩種方案

具體實踐以下

  • 錯誤碼固定長度, 以區間來劃分錯誤類型(例如 HTTP 的狀態碼)

    例如: 10404 表示 HTTP 請求 404 錯誤, 20000 表示 API 調用失敗, 30000 表明業務錯誤, 31000 表示業務A錯誤, 32000 表示業務B錯誤

  • 錯誤碼可不固定長度, 以首字母來劃分錯誤類型, 可擴展性更好, 但實際運做仍是須要劃分區間

    例如: H404 表示 HTTP 請求 404 錯誤, A100 表示 API 調用失敗, B100 表示業務A錯誤, B200 表示業務B錯誤

關於錯誤分類的原則, 咱們能夠根據發送請求的最終狀態來劃分

  • 發送失敗(即請求根本就沒有發送出去)
  • 發送成功
    • HTTP 異常狀態(例如 404/500...)
    • HTTP 正常狀態(例如 200)
      • 接口調用成功
      • 接口調用失敗(業務錯誤, 即接口規範中 status 非 0 的狀況)

最終規範

錯誤碼可不固定長度, 總體格式爲: 字母+數字字母做爲錯誤類型, 可擴展性更好, 數字建議劃分區間來細分錯誤

例如:

  • A for API: API 調用失敗(請求發送失敗)的錯誤, 例如 A100 表示 URL 非法
  • H for HTTP, HTTP 異常狀態的錯誤, 例如 H404 表示 HTTP 請求404錯誤
  • B for backend or business, 接口調用失敗的錯誤, 例如 B100 業務A錯誤, B200 業務B錯誤
  • C for Client: 客戶端錯誤, 例如 C100 表示解析 JSON 失敗
發送 HTTP 請求
                                                 ┌───────────┴───────────┐
                                              發送成功¹               發送失敗²
                                                 │                       │
                                      ┌──────────┴──────────┐            A 例如: A100
                                得到 HTTP 響應       沒法得到 HTTP 響應³
                                      │                     │
                                 HTTP status                A 例如: A200
                           ┌──────────┴──────────┐
                       HTTP 成功(200-300)     HTTP 異常
                           │                     |
               {data, status, statusInfo}        H${HTTP status} 例如: H404
               ┌───────────┴───────────┐
          接口調用成功(status:0)   接口調用失敗
      ┌────────┴────────┐              |
客戶端處理出錯      客戶端處理正常       B${status}${statusInfo.message} 例如: B100
      |
      C 例如: C100

- 發送成功¹: 服務端收到了 HTTP 請求並返回了 HTTP 響應
- 發送失敗²: HTTP 請求沒有發送出去(例如因爲跨域被瀏覽器攔截不容許發送), 未到達服務端(即服務端沒有收到這個 HTTP 請求)
- 沒法得到 HTTP 響應³: 服務端收到了請求並返回了響應, 但客戶端因爲某些緣由沒法得到 HTTP 響應, 例如請求的超時處理機制

統一錯誤提示

  • 錯誤日誌
    • 接口調用出錯(${錯誤碼}${HTTP 方法} ${HTTP URL} ${請求參數} ${請求選項} ${請求返回結果}
    • 例如: 接口調用出錯(H404) GET https://domain.com {foo: bar} {option1: 'test'} {status: 404}
  • 給用戶的提示消息(參考自 QQ 的錯誤提示消息)
    • 提示消息(錯誤碼: xxx)

      weapp-error-tip weibo-error-tip qq-error-tip

    • 提示消息和錯誤碼之間用換行隔開

    • 錯誤碼整塊內容建議弱化使用灰色字

    • 例如

      mobile-error-code-message pc-error-code-message

規範實現: weapp-backend-api

接口實現建議

  • 接口實現的大方向建議遵循 RESTful 風格
  • HTTP 動詞: 獲取數據用 GET, 新增/修改/發送數據用 POST
    • 例如: 獲取用戶數據的接口用 GET, 修改用戶數據的接口用 POST
  • 對於資源的操做類型, 使用 HTTP 動詞來指定, 減小接口 URL 的數量
    • 例如: GET /contact 獲取聯繫人, POST /contact 新增/修改聯繫人
  • 對外的 ID 字段使用字符串類型
    • 特別核心數據的 ID 字段, 不要使用自增的數字類型, 建議使用無規則的字符串類型(例如UUID), 避免核心數據被輕易抓取
    • 避免使用大數字類型(Long), 由於前端可能承載不了這個精度而溢出獲得另一個數值
    • 例如: Java 中的 Long 類型的數值: 362909601374617692, 做爲 JSON 數據返回給前端, 前端拿到的值變成了 362909601374617660
  • 接口字段建議同時給出 ID 字段和用於顯示字段, 前端提交數據時只提交 ID 字段
    • 例如: {"sex": 1, "sexText": "男"}
  • 圖片的 URL 建議返回完整的 URL
    • 例如: {"pic": "https://domain.com/a.png"}
  • 時間字段建議同時返回時間戳的原始值(或 ISO 標準格式)和用於統一顯示的格式化文本
    • 由後端接口集中控制各端的顯示, 提供的原始值兼顧前端的自定義顯示或者計算(例如倒計時)的需求
    • 避免每一個端(例如H5/APP/小程序)都須要對時間作統一的格式化實現, 一旦須要調整, 須要各個端都調整一遍
    • 例如: {"createTime": 1543195480357, "createTimeText": "2018年11月26日"}
  • 統一分頁的數據格式
    • 分頁請求的參數和分頁結果的數據結構

注意

參考

  • E-JSON數據傳輸標準

  • 有範雲協做 讓項目的協做姿式更有範兒

    • 交互階段說明
      • 交互設計師根據產品方的需求對產品進行行爲設計和界面設計的階段,主要產出物爲交互設計稿
      • 開發工程師須要作的事情是針對產品需求、交互設計稿中的內容進行技術評審,爲產品方、交互設計師提供可行技術實現解決方案,對於多種不一樣解決方案需針對各類解決方案作分析說明,務必準確傳達各類方案的優缺點,並根據需求給出建議方案
    • 系統設計說明
      • 各端開發工程師針對產品需求說明、交互設計稿開始設計系統架構、拆分子系統、劃分子系統模塊、協調端與端之間的接口規範,這個階段各端根據實際狀況輸出若干系統設計說明書等文檔
      • 除此以外更重要的是輸出端與端之間通訊的接口規範,而這個規範則能夠藉助 NEI 平臺 來完成
    • 編碼階段說明
      • 開發工程師根據系統設計階段的輸出,用代碼來實現這樣的系統,包括技術方案的選型、項目框架的搭建、工具及環境的配置等
      • 其中有些工做能夠藉助於有範雲協做提供的自動化工具 NEI-Toolkit 來完成,好比項目的初始結構代碼、在 NEI平臺 上定義好的接口規範等
    • 自測階段說明
      • 各個端的工程師驗證本身編寫的代碼的正確性,按角色不一樣,測試方式也有全部不一樣
      • 對於前端和移動端工程師來講,主要是須要測試各類可能的值會不會影響界面展現
      • 對於服務端工程師來講,主要是測試提供給客戶端工程師使用的接口的正確性,對於不一樣的輸入參數是否返回了預期的結果
    • 聯調階段說明
      • 主要是連測試環境進行測試
      • 對於前端和移動端工程師來講,主要是須要將本地容器提供的接口換成測試環境的接口
    • 測試階段說明
      • 開發工程師開發完成後提測的過程,是產品上線前的最後環節
      • 測試工程師會對接 NEI 平臺生成接口測試用例代碼並集成到自動化測試平臺運行,若是NEI平臺的接口定義與實際提測的項目不符則這次提測失敗,需由開發對照 NEI 平臺檢查接口實現狀況,因此能夠保證 NEI 平臺上的接口定義始終與線上保持一致
  • 客戶端API請求規範

    參數名 說明
    imei 國際移動設備身份碼
    imsi 客戶端用戶標識
    t TIMESTAMP,請求的時間戳
    appkey 由服務端頒發的appkey
    sign md5簽名串。爲了減輕非法惡意請求,每次來自APP的請求都須要對請求參數進行簽名以實現安全認證
    lng 手機上獲取的經度
    lat 手機上獲取的緯度
    ci 渠道標識,格式爲:channelId@應用名平臺客戶端版本,例如:1001@nzaom_android_1.0,其中1001表示應用寶
  • GitHub API | 微博API | 淘寶開放平臺 API

  • JSend | JSON API | JSON Schema | JSON-RPC | JWT | OAuth

    Type Description Required Keys Optional Keys
    success All went well, and (usually) some data was returned. status, data  
    fail There was a problem with the data submitted, or some pre-condition of the API call wasn't satisfied status, data  
    error An error occurred in processing the request, i.e. an exception was thrown status, message code, data
  • Google JSON Style Guide

  • 最佳實踐:更好的設計你的 REST API | RESTful API 設計指南 | Best Practices for Designing a Pragmatic RESTful API | HTTP API Design Guide | The RESTful Cookbook | RESTful API 編寫指南

  • Restlet Studio - Web IDE for API design | Swagger | ReDoc | RAML | API Blueprint

相關文章
相關標籤/搜索