title: 一聲笑接口文檔
date: 2019-03-03 21:44:43
tags:
Author:Leguang
E-Mail:langmanleguang@qq.com
目標
- 接口“粒度”爭取設計得足夠小,爭取在業務發生變化后,后臺接口不需要增減,只需要前端組合接口仍然能滿足新的業務需求。
- 任何一個接口都可以獲取到數據,哪怕沒傳參數。
要求
命名規范
一名二姓三風水,四積陰德五讀書,名不正則言不順,言不順則事難成。軟件開發其實就是門命名的藝術,所以首先定義一些規范,提出一些硬性要求,大家在命名的時候盡量多花點心思,多參考優秀的命名風格。
- 強烈推薦參考:參考阿里巴巴Java開發手冊,Android開發規范,iOS開發手冊。
- 一個單詞盡量選擇5--7個字母的,這樣才最優美。
- 首字母縮寫的單詞盡量每個字母都用大寫,例如ID。用個小寫,人家還以為是一個單詞。當然uri、url、urn這種除外,因為大家都知道這個是什么。
- 規范并統一公司的基礎包名與項目的關系。
- 前后端的某些名稱概念要統一用某一個單詞,比如支付的統一訂單,支付寶用的是order,微信用的是unifiedorder,那我們統一對訂單這個概念用order這個詞。再比如主機:后臺用gateway,現在我們統一用host。這單詞不統一很容易分裂。
uri規范
uri 表示資源,資源一般對應服務器端領域模型中的實體類,要求如下:
- 不用大寫。
- 盡量不用橫杠分隔符,萬一要用,請使用中杠“-”不用下杠“_”。
- 參數列表要encode。
- uri中的名詞表示資源集合,使用復數形式。
- 路徑僅表示資源的路徑(位置),以及一些特殊的actions操作。
- 以復數(名詞)進行命名資源,不管返回單個或者多個資源。
- 資源的路徑從父到子依次如:/{resource}/{resource_id}/{sub_resource}/{sub_resource_id}/{sub_resource_property}。
- 使用?來進行資源的過濾、搜索以及分頁等。
- 使用版本號,且版本號在資源路徑之前。
- 優先使用內容協商來區分表述格式,而不是使用后綴來區分表述格式。
- url最好越簡短越好,結果過濾,排序,搜索相關的功能都應該通過參數實現。
- url失效則返回404 not found 或 410 gone;對遷移的API,返回 301 重定向。
JSON規范
暫時只考慮json的數據格式,要求如下:
- 不要使用縮寫。
- 統一用駝峰命名法。
- 不要使用“_”或者“-”。
- 用名詞復數表示集合類型。
- 為了方便以后的擴展兼容,如果返回的是數組,強烈建議用一個包含如items屬性的對象進行包裹。如:
{"items":[{},{}]}
。 - 建議對每個字段設置默認值(數組型可設置為[],字符串型可設置為””,數值可設置為0,對象可設置為{}),這一條是為了方便前端/客戶端進行判斷字段存不存在操作。
- 建議資源使用UUID最為唯一標識。同時建議命名為id或者uid。
- 采用UTF-8編碼。
- 數據應該拿來就能用,不應該還要進行轉換操作。
草稿:JSON返回的格式是分門別類按對象來劃分,還是鋪大餅的形式鋪開,兩者利弊各異,比如url,可能一個接口中返回多個url,如果分json對象裝的話,則key都可以叫url,否則的話key就得命名成xxxUrl。這個有待商議
HTTP部分
使用場景
- App的初始化數據盡量都用http協議獲取。
- 頁面的初始化數據盡量都用http協議獲取。
URL結構
https://{serviceRoot}/{module}/{collection}/{id}
- {serviceRoot} – 域名+端口號 (site URL) + 根目錄
- {module} – 模塊名稱
- {collection} – 要訪問的資源
- {id} – 要訪問的資源的唯一編號
公共請求頭
通過Content-Type指定請求與返回的數據格式有JSON和XML,暫時我們只管json的。其中請求數據還要指定Accept。其中額外添加的請求頭里的參數注意大小寫。
Accept: application/json
Content-Type: application/json;charset=UTF-8
Token: token_G34G34G34G34G35G5
Application: 應用:版本
Platform: Android
Locale: zh
params | 類型 | 是否必要 | 描述 |
---|---|---|---|
Token | String | 是 | 這個不用解釋了吧? |
Application | String | 是 | App應用名稱(名稱有官方指定)與當前版本的版本名稱,中間用半角冒號隔開 |
Platform | String | 是 | Android表示Android平臺,iOS表示iOS平臺 |
Locale | String | 否 | 用于針對接口的國際化,不同的值代表不同的語言,可以不傳,不傳默認表示中文簡體。值和語言如下表所示: |
暫時只考慮4門語言
值 | 意義 |
---|---|
zh | 中文(簡體) |
en | 英文 |
ja | 日文 |
ko | 韓文 |
公共參數(部分公共參數建議公放到請求頭里)
公共參數是指每一個接口都可能有的,為了減少篇幅,我在這里統一定義,同時后端要指定公共參數的默認值,且要保證沒有傳公共參數不會報錯,所以需要一定的容錯性,比如priceDes這個參數值,如果是用的是全部小寫的,只要是不沖突,則可認為是準確的參數并且表達了按價格降序排列這個語意。常用公共參數如下:
params | 類型 | 是否必須 | 描述 |
---|---|---|---|
keyword | String | 否 | 用于檢索,不傳或者傳空,表示默認,默認不檢索該關鍵字 |
sort | String | 否 | 用于對列表排序,不傳則表示默認,默認按配置順序,asc升序,desc降序 |
page | int | 否 | 用于分頁描述,不傳則表示默認,默認是第1頁 |
pageSize | int | 否 | 表示該頁顯示的條數,不傳則表示默認,默認為20條 |
個性參數
個性參數就是除了公共參數之外的,看能否考慮統一用JSON將公共參數和個性參數濃縮成一個參數,把想要表達的參數通過json中的key-value形式傳遞。例如:https://api.xxx.com/both/v1/search/products?params={"extra":"你想填什么呢","token":"token_523523523","data":{"uid":"23523523523523"}} 或者考慮與業務相關的參數就用json形式包裝,而與業務無關的個性參數就還是用傳統的方式另立一個參數。例如:https://api.xxx.com/both/v1/search/products?limit=10&offset=10¶ms={"keyword":"方便面","sort":"des"}
通過商議將個性參數統一按原始的URL傳參形式傳遞,不考慮上面所說的那種方式。
公共響應頭
Content-Type:application/json; charset=utf-8
Status:200 OK
其中狀態碼要與公共響應體里的json中的code字段一樣。
公共響應體
默認會有以下字段,不需要全部都有。
{
"message": "居然被你查詢成功了",
"code": 200,
"page": 0,
"pageSize": 20,
"first": "https://...",
"next": "https://...",
"previous": "https://...",
"last": "https://...",
"data": {
"uid": "6565656565665"
}
}
key | 類型 | 是否必須 | 描述 |
---|---|---|---|
message | String | 是 | 返回給接口調用者的描述,有可能用于顯示到界面上,需要進行國際化處理 |
code | int | 是 | 這個與請求頭中的狀態碼一致,是為了滿足部分開發者的習慣 |
page | int | 否 | 分頁請求中請求的當前頁的頁碼 |
pageSize | int | 否 | 分頁請求中一頁的個數,默認為20 |
first | String | 否 | 分頁請求中第一頁的url ,如果沒有則返回空字符串 |
next | String | 否 | 分頁請求中下一頁的url,如果沒有則返回空字符串 |
previous | String | 否 | 分頁請求中上一頁的url,如果沒有則返回空字符串 |
last | String | 否 | 分頁請求中最后一頁的url,如果沒有則返回空字符串 |
data | object | 否 | 當前接口的具體數據由該json對象承載 |
uid | String | 否 | 對于每一個資源對象,在返回的時候,都應該返回操作這個資源對象的唯一碼 |
HTTP動詞表示操作。
常用的HTTP動詞有下面五個(括號里是對應的SQL命令)。
- GET(SELECT):從服務器取出資源(一項或多項)。
例如:GET /zoos:列出所有動物園。 - POST(CREATE):在服務器新建一個資源。
例如:POST /zoos:新建一個動物園。 - PUT(UPDATE):在服務器更新資源(客戶端提供改變后的完整資源)。
例如:PUT /zoos/ID:更新某個指定動物園的信息(提供該動物園的全部信息)。 - DELETE(DELETE):從服務器刪除資源。
例如:DELETE /zoos/ID:刪除某個動物園。
狀態碼
作為API的設計者,正確的將API執行結果和失敗原因用清晰簡潔的方式傳達給客戶程序是十分關鍵的一步。我們確實可以在HTTP的相應內容中描述是否成功,如果出錯是因為什么,然而,這就意味著用戶需要進行內容解析,才知道執行結果和錯誤原因。因此,HTTP響應代碼可以保證客戶端在第一時間用最高效的方式獲知 API 運行結果,并采取相應動作。下表列出了比較常用的響應代碼。
常用的http狀態碼及使用場景:
響應代碼 | 代碼含義 |
---|---|
200 | 已創建,請求成功且服務器已創建了新的資源。 |
201 | 是否只顯示處于警告狀態的應用實例。 |
301 | 重定向 , 請求的網頁已被永久移動到新位置。服務器返回此響應時,會自動將請求者轉到新位置。 |
302 | 重定向 , 請求的網頁臨時移動到新位置,但求者應繼續使用原有位置來進行以后的請求。302 會自動將請求者轉到不同的臨時位置。 |
304 | 未修改,自從上次請求后,請求的網頁未被修改過。服務器返回此響應時,不會返回網頁內容。 |
400 | 錯誤請求 , 服務器不理解請求的語法。 |
401 | 未授權 , 請求要求進行身份驗證。 |
403 | 已禁止 , 服務器拒絕請求。 |
404 | 未找到 , 服務器找不到請求的網頁。 |
405 | 方法禁用 , 禁用請求中所指定的方法。 |
406 | 不接受 , 無法使用請求的內容特性來響應請求的網頁。 |
408 | 請求超時 , 服務器等候請求時超時。 |
410 | 已刪除 , 如果請求的資源已被永久刪除,那么,服務器會返回此響應。 |
412 | 未滿足前提條件 , 服務器未滿足請求者在請求中設置的其中一個前提條件。 |
415 | 不支持的媒體類型 , 請求的格式不受請求頁面的支持。 |
500 | 內部服務器錯誤。 |
分頁
分頁適用于GET類型且返回集合數據的請求,根據如下參數進行分頁操作。分頁返回的數據見公共響應體。
{
"extra": "你想填什么就填什么",
"page": 0,
"pageSize": 20,
}
params | 類型 | 是否必須 | 描述 |
---|---|---|---|
page | int | 否 | 同上 |
pageSize | int | 否 | 同上 |
錯誤/異常處理
- 不要發生了錯誤但給2xx響應,客戶端可能會緩存成功的HTTP請求;
- 正確設置http狀態碼,不要自定義;
- Response body 提供 1) 錯誤的代碼(日志/問題追查);2) 錯誤的描述文本(展示給用戶)。
對第三點的實現稍微多說一點:
Java 服務器端一般用異常表示 RESTful API 的錯誤。API 可能拋出兩類異常:業務異常和非業務異常。業務異常由自己的業務代碼拋出,表示一個用例的前置條件不滿足、業務規則沖突等,比如參數校驗不通過、權限校驗失敗。非業務類異常表示不在預期內的問題,通常由類庫、框架拋出,或由于自己的代碼邏輯錯誤導致,比如數據庫連接失敗、空指針異常、除0錯誤等等。
業務類異常必須提供2種信息:
- 如果拋出該類異常,HTTP響應狀態碼應該設成什么;
- 異常的文本描述;
錯誤描述
{
"message": "特么的又錯了",
"code": 500,
"document": "https://developer.xxx.com/v1/errors/500",
"exception": [
{
"code": "NullValue",
"target": "PhoneNumber",
"message": "Phone number must not be null"
},
{
"code": "NullValue",
"target": "LastName",
"message": "Last name must not be null"
},
{
"code": "MalformedValue",
"target": "Address",
"message": "Address is not valid"
}
]
}
錯誤請求頭
{
"date": "Mon, 08 Jan 2018 03:07:08 GMT",
"server": "nginx/1.10.3 (Ubuntu)",
"connection": "keep-alive",
"content-length": "237",
"content-type": "application/json"
}
錯誤狀態碼(非200)
通過分析發現,接口的錯誤無非調用端傳遞給后臺的信息與預期不符,其中包括公共參數和個性參數不符,不符包括缺失和內容錯誤(或格式錯誤等)兩種,還有是部分特殊業務的特殊性質造成非正常結果,如:重復申請、重復簽到等。經商議決定將錯誤碼分為四大類:
錯誤碼 | 類型 | 描述 | 舉例 |
---|---|---|---|
XXX | int | 用于描述缺少參數,同時message做出相應的輔助描述,告訴調用者到底是缺少哪個參數,這些message只有調用者看得到,用戶看不到 | 需要a、b、c三個參數,結果只收到a和b參數,或者收到c參數,但c參數為空字符串,且c參數又是必傳的,因此可返回該錯誤碼,同時messgae中提示:缺少c參數,或者c參數不能為空 |
XXX | int | 用于描述有參數但參數錯誤,可能是內容錯誤或格式錯誤,同時message做出相應的輔助描述,告訴調用者到底是哪個參數錯誤,這些message只有調用者看得到,用戶看不到 | 需要傳a參數為String類型的11位手機號碼,結果收到的是long類型,或只有10位的String,則可返回該錯誤碼,同時message提示a參數錯誤 |
XXX | int | 描述某些特定業務場景,比如重復申請、重復簽到等,每一個場景對應一個錯誤碼,同時message會提示相應錯誤場景,這些message會給用戶看到的 | 比如重復申請注冊獎勵,返回特定碼,前端做出相應顯示,或者跳轉,或者隱藏即可 |
XXX | int | 描述某些公共功能,調用者會做出相應公共處理 | 比如token失效,直接跳轉到登錄界面 |
以上友好的message提示方便了我們,同時也方便了非法調用者,為阻礙非法調用,決定設置一個開關,該開關是系統自帶的,根據編譯環境變更,在調試階段就用以上友好提示,在正式部署上線后就改成模糊提示。
url失效
隨著系統發展,總有一些API失效或者遷移,對失效的API,返回404 not found 或 410 gone;對遷移的API,返回 301 重定向。
對于后臺文檔的要求
文檔要求描述詳盡,盡可能的引導接口使用者理解接口設計,這樣才能減少接口的改動,又能適應多變的業務。
容錯性(健壯性)
考慮到如果測試或者運營中有變動,要求每一個Web頁面得有容錯機制,比如關停該功能,則web必須的有相應的頁面展示。
國際化
此處的國際化是指語言的國際化,因此前后端都要考慮在界面的文字提示上要做國際化處理,暫時我們只做4種文字的國際化:中文(簡體)、英文、日文和韓文,經商議,通過兩方面來傳遞國際化信息:
- 專門的locale設置接口
- 請求頭中多一個locale字段
由于有兩個入口設置語言,因此有優先級處理,請求頭中locale字段優先級大于locale設置接口,兩者不傳,默認都表示簡體中文。locale接口默認為簡體中文,如果有設置其他語言,則為其他語言,請求頭里如果沒有locale字段,則依據locale接口,如果請求頭里帶了locale字段,則依據請求頭為準。