Restful API設(shè)計(jì)思路及實(shí)踐

記得第一次寫APP的時(shí)候,那時(shí)還完全不知道REST這個(gè)東西,對(duì)Web Service也是一知半解。我和另一個(gè)同學(xué)在討論使用什么協(xié)議來交互時(shí),通過各自充分的調(diào)研之后(其實(shí)就是搜索引擎找一找。。。),一致認(rèn)為,HTTP這個(gè)東西本身就對(duì)帶寬的消耗這么大了,這么多Web Service(當(dāng)時(shí)還是SOAP當(dāng)?shù)溃┻€是基于HTTP之上的,這得浪費(fèi)多少帶寬啊。最后一致決定使用Socket來通信,現(xiàn)在想想當(dāng)時(shí)也是挺不容易的,我們硬是在Socket上搭了一套通信協(xié)議,還發(fā)展到了第二版。

今天在移動(dòng)應(yīng)用普及、前后端分離的大浪潮下,RESTful風(fēng)格的API大行其道,可是因?yàn)樗旧砭褪且粋€(gè)比較模糊且寬泛的概念,所以每個(gè)人對(duì)它的理解都有千差萬別。我覺得我們?cè)诩夹g(shù)選型的時(shí)候,在自己的技術(shù)積累以及參考已有的行業(yè)最佳實(shí)踐的基礎(chǔ)上,應(yīng)當(dāng)首先考慮自身系統(tǒng)的需求,思考「選擇某一種技術(shù)」會(huì)對(duì)系統(tǒng)的開發(fā)和維護(hù)帶來哪些好處與壞處,而不是人云亦云看著別人用什么自己就用什么。而且RESTful API設(shè)計(jì)目前并沒有一個(gè)公認(rèn)的行業(yè)最佳實(shí)踐,故而開發(fā)者在設(shè)計(jì)一個(gè)API系統(tǒng)時(shí),更應(yīng)該根據(jù)自身的情況量身定制,千萬不要說「我照著某某公司的開放API照搬」就好了。 本文將根據(jù)我使用REST的經(jīng)驗(yàn)來總結(jié)一下RESTful API設(shè)計(jì)的一些知識(shí)和經(jīng)驗(yàn),自勉。本文將不討論Oauth等安全問題。

首先理清一些概念:
  • REST(Representational State Transfer)
    定義了一套基于Web的數(shù)據(jù)交互方式的設(shè)計(jì)風(fēng)格。
  • RESTful
    符合REST風(fēng)格的API就可以叫做RESTful API。注意,本文講到的RESTful API設(shè)計(jì)方法將是基于HTTP和JSON實(shí)現(xiàn)方式,但不論HTTP還是JSON都不是REST的標(biāo)準(zhǔn)。REST只是風(fēng)格,沒有標(biāo)準(zhǔn)。
  • 動(dòng)詞、RPC
    在微信里搜索【RESTful API 設(shè)計(jì)】,出來好多文章都是說怎么在RESTful Uri里使用動(dòng)詞等等,這些人除了一部分是把文章拿來抄一抄的,其他的其實(shí)搞混了REST和RPC的概念了,REST強(qiáng)調(diào)資源,RPC強(qiáng)調(diào)動(dòng)作,所以REST的Uri組成為名詞,RPC多為動(dòng)詞短語。然而它們也并不是完全不相關(guān)的兩個(gè)東西,本文中的實(shí)現(xiàn)就會(huì)參考一部分JSON-RPC的設(shè)計(jì)思想。
  • Web Service
    這個(gè)是一個(gè)更古老的概念,有一套它的理論,不過我更傾向于把它理解成任何基于Web提供的服務(wù)。

設(shè)計(jì)方法及原則:

1. 使用HTTP方法:

HTTP1.1的規(guī)范定義了8個(gè)動(dòng)詞,然而HTTP作為一個(gè)規(guī)范并沒有被嚴(yán)格地遵守著,在大多數(shù)情況下POST是可以完成除任何種類的請(qǐng)求,所以現(xiàn)在很多的API設(shè)計(jì)都是只是用GET和POST來調(diào)用API,在這種情況下,一般的做法是使用GET用來獲取資源,其他的行為都是用POST來完成,而為了區(qū)別不同的行為,往往在API的Uri中加入動(dòng)詞,如百度推送的如下API:

[ POST ] /rest/3.0/app/del_tag

功能

刪除一個(gè)已存在的tag

參數(shù)

參數(shù)名 類型 必需 限制 描述
tag string 1~128字節(jié),但不能為‘default’ 標(biāo)簽名稱

返回值

名稱 類型 描述
tag string 標(biāo)簽名稱
result number 狀態(tài) 0:創(chuàng)建成功; 1:創(chuàng)建

更清晰API設(shè)計(jì)的可能會(huì)使用GET POST PUT DELETE四種方法分別代表“查詢、添加、更新、刪除”等四個(gè)動(dòng)作,這在概念上是符合HTTP規(guī)范的,如Google的如下API:

Request

DELETE https://www.googleapis.com/bigquery/v2/projects//datasets/?key={YOUR_API_KEY}

Response

404 Not Found

– Show headers –

Not Found

在我看來,沒有絕對(duì)的好與不好。如果使用第一種方法,那么只要保證Uri的語義清晰,其實(shí)和使用第二種方法沒有太大的區(qū)別。

2. Uri格式:

Uri在REST中標(biāo)識(shí)了一個(gè)資源,但是在具體的API設(shè)計(jì)中,往往不能做到完全的對(duì)于資源的映射,本文中的設(shè)計(jì)將參考比較流行的Uri設(shè)計(jì),大致有這么幾條:

  • Uri的根(root, /)應(yīng)當(dāng)能夠標(biāo)識(shí)這是一個(gè)RESTful API,以與同目錄下其他可能存在的資源進(jìn)行區(qū)分。
  • 緊接著Uri的根,應(yīng)當(dāng)標(biāo)識(shí)當(dāng)前API的版本號(hào)。
  • 如果方法是POST或者PUT,盡量避免使用URL編碼的參數(shù),盡量保持Uri的干凈。
  • 如果方法是DELETE,Uri應(yīng)當(dāng)完全標(biāo)識(shí)了需要?jiǎng)h除的對(duì)象或者對(duì)象的集合,避免在DELETE的請(qǐng)求中使用其他參數(shù),因?yàn)槟承┓?wù)器可能會(huì)丟棄伴隨著DELETE發(fā)送的內(nèi)容。

這里再次拿行業(yè)標(biāo)桿Google的開放API來舉例:

POST https://www.googleapis.com/books/v1/mylibrary/annotations

PUT https://www.googleapis.com/bigquery/v2/projects/p1/datasets/p2

DELETE https://www.googleapis.com/bigquery/v2/projects/{project-parameter}/datasets/{datasets-parameter}

3. 固定返回碼

REST的大部分實(shí)現(xiàn)都是一個(gè)基于HTTP的,那么自然而然就少不了與返回碼打交道,然而不幸的是,HTTP的返回碼定義的看起來十分隨意,很多錯(cuò)誤信息語焉不詳,而且在實(shí)際的開發(fā)中,API的使用者需要處理鏈路的問題(如超時(shí)等)、種類繁多的HTTP返回碼、和實(shí)際的返回內(nèi)容,不堪其繁瑣。更嚴(yán)重的是,這些返回碼大多最終依賴于服務(wù)端開發(fā)者的具體實(shí)現(xiàn),而這種看似約定的東西分別在客戶端和服務(wù)端開發(fā)者眼中的含義可能相去甚遠(yuǎn)。

那么從需求入手,我們?cè)谑褂?code>RESTful API時(shí)需要使用返回碼的原因大致是這樣的:客戶端在調(diào)用一個(gè)API之后,需要在接收到的反饋必須要能夠標(biāo)識(shí)這次調(diào)用是否成功,如果不成功,客戶端需要拿到失敗的原因。我們可以在API設(shè)計(jì)時(shí)作一個(gè)小小的約定,就能完美的滿足以上需求了。

服務(wù)端在成功接收到客戶端的請(qǐng)求之后,永遠(yuǎn)返回200,具體成功與否及進(jìn)一步的信息放入返回的內(nèi)容。

在這個(gè)場景中,如果是鏈路出了問題或者服務(wù)器錯(cuò)誤等(返回碼不等于200),客戶端很容易就能捕獲這個(gè)錯(cuò)誤,如果鏈路沒問題,那么出錯(cuò)與否在獲取到的反饋內(nèi)容中會(huì)有詳細(xì)的描述。

4. 固定返回結(jié)構(gòu)

現(xiàn)在越來越多的API設(shè)計(jì)會(huì)使用JSON來傳遞數(shù)據(jù),本文中的設(shè)計(jì)也將使用JSON。JSON-RPC是一個(gè)基于JSON的廣為人知的設(shè)計(jì)簡潔的RPC規(guī)范,本文將借鑒JSON-RPC的響應(yīng)對(duì)象的設(shè)計(jì)。

JSON-RPC中服務(wù)端響應(yīng)對(duì)象的設(shè)計(jì)的基本理念是,只要調(diào)用成功,服務(wù)端必須響應(yīng)數(shù)據(jù)(如在#3中討論的那樣),而響應(yīng)數(shù)據(jù)的格式在任何情況下都應(yīng)當(dāng)是一致的,JSON-RPC的響應(yīng)格式是這么設(shè)計(jì)的:

{"jsonrpc": "2.0", "result": 19, "id": 1}

{
    "jsonrpc": "2.0", 
    "error": 
        {
            "code": -32600, 
            "message": "Invalid Request"
        }, 
    "id": null
}
jsonrpc

A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0".

result

This member is REQUIRED on success.

This member MUST NOT exist if there was an error invoking the method.

The value of this member is determined by the method invoked on the Server.

error

This member is REQUIRED on error.
This member MUST NOT exist if there was no error triggered during invocation.

The value for this member MUST be an Object as defined in section 5.1.

id

This member is REQUIRED.

It MUST be the same as the value of the id member in the Request Object.

If there was an error in detecting the id in the Request object (e.g. Parse error/Invalid Request), it MUST be Null.

由于JSON-RPC的目標(biāo)是建立一個(gè)通用的規(guī)范,所以響應(yīng)格式的設(shè)計(jì)還是有些復(fù)雜,我們可以只取其中它對(duì)于error對(duì)象的設(shè)計(jì),所有返回的格式必須是這樣的:

{
    "code": -32600, 
    "message": "Invalid Request”, 
    “data”:{ }
}

這種格式的設(shè)計(jì)在許多大公司的開放API中也較為常見,比如作為行業(yè)標(biāo)桿的Google,在調(diào)用Google開放平臺(tái)的某API后獲取到的錯(cuò)誤數(shù)據(jù)如下,其設(shè)計(jì)思想與本文討論的這種返回格式的思想如出一轍。

{"error": {
    "errors": [
            {
                "domain": "global",
                "reason": "required",
                "message": "Login Required",
                "locationType": "header",
                "location": "Authorization"
            }
        ],
    "code": 401,
    "message": "Login Required"
    }
}
綜上所述,本文所探討的API設(shè)計(jì)是這樣的:
  1. 所有API的Uri為基于HTTP的名詞性短語,用來代表一種資源。

  2. Uri格式如文中所述。

  3. 使用GET POST PUT DELETE四種方法分別代表對(duì)資源的“查詢、添加、更新、刪除”。

  4. 服務(wù)端接收到客戶端的請(qǐng)求之后,統(tǒng)一返回200,如果客戶端獲取到的返回碼不是200,代表鏈路上某一個(gè)環(huán)節(jié)出了問題。

  5. 服務(wù)端所有的響應(yīng)格式為:

     {   
         “code”: -32600, 
         “message”: “Invalid Request”, 
         “data”:{ }
     }
    

    他們的含義分別代表:

    • code為0代表調(diào)用成功,其他會(huì)自定義的錯(cuò)誤碼;
    • message表示在API調(diào)用失敗的情況下詳細(xì)的錯(cuò)誤信息,這個(gè)信息可以由客戶端直接呈現(xiàn)給用戶,否則為空;
    • data表示服務(wù)端返回的數(shù)據(jù),具體格式由服務(wù)端自定義,API調(diào)用錯(cuò)誤為空
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評(píng)論 19 139
  • 一說到REST,我想大家的第一反應(yīng)就是“啊,就是那種前后臺(tái)通信方式。”但是在要求詳細(xì)講述它所提出的各個(gè)約束,以及如...
    時(shí)待吾閱讀 3,601評(píng)論 0 19
  • 從今天開始,我開始學(xué)習(xí)Retrofit,整體Retrofit內(nèi)容如下: 1、Retrofit解析1之前哨站——理解...
    隔壁老李頭閱讀 6,302評(píng)論 4 46
  • 越來越頻繁的爭吵,從幾句話,到陌生的人,幾乎沒有什么不是爭吵的原因。一個(gè)人窩在小旅館,沒有以前的整夜流淚,只有...
    Gorgia閱讀 294評(píng)論 0 1
  • 南山崔崔,雄狐綏綏。魯?shù)烙惺?,齊子由歸。 一 “啪!”一筒竹簡被狠狠摔在地上,跪伏在地的人低垂著頭,不敢看那個(gè)發(fā)怒...
    林汐瑯閱讀 595評(píng)論 2 2

友情鏈接更多精彩內(nèi)容