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

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

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

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

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

1. 使用HTTP方法:

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

[ POST ] /rest/3.0/app/del_tag

功能

刪除一個已存在的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è)計的可能會使用GET POST PUT DELETE四種方法分別代表“查詢、添加描焰、更新媳否、刪除”等四個動作,這在概念上是符合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

在我看來篱竭,沒有絕對的好與不好力图。如果使用第一種方法,那么只要保證Uri的語義清晰室抽,其實和使用第二種方法沒有太大的區(qū)別搪哪。

2. Uri格式:

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

  • Uri的根(root, /)應(yīng)當(dāng)能夠標(biāo)識這是一個RESTful API兽泄,以與同目錄下其他可能存在的資源進(jìn)行區(qū)分漓概。
  • 緊接著Uri的根,應(yīng)當(dāng)標(biāo)識當(dāng)前API的版本號病梢。
  • 如果方法是POST或者PUT胃珍,盡量避免使用URL編碼的參數(shù),盡量保持Uri的干凈蜓陌。
  • 如果方法是DELETE觅彰,Uri應(yīng)當(dāng)完全標(biāo)識了需要刪除的對象或者對象的集合,避免在DELETE的請求中使用其他參數(shù)钮热,因為某些服務(wù)器可能會丟棄伴隨著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的大部分實現(xiàn)都是一個基于HTTP的,那么自然而然就少不了與返回碼打交道隧期,然而不幸的是飒责,HTTP的返回碼定義的看起來十分隨意,很多錯誤信息語焉不詳仆潮,而且在實際的開發(fā)中宏蛉,API的使用者需要處理鏈路的問題(如超時等)、種類繁多的HTTP返回碼性置、和實際的返回內(nèi)容拾并,不堪其繁瑣。更嚴(yán)重的是鹏浅,這些返回碼大多最終依賴于服務(wù)端開發(fā)者的具體實現(xiàn)辟灰,而這種看似約定的東西分別在客戶端和服務(wù)端開發(fā)者眼中的含義可能相去甚遠(yuǎn)。

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

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

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

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

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

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

{"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)是建立一個通用的規(guī)范奈辰,所以響應(yīng)格式的設(shè)計還是有些復(fù)雜,我們可以只取其中它對于error對象的設(shè)計乱豆,所有返回的格式必須是這樣的:

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

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

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

  2. Uri格式如文中所述孵奶。

  3. 使用GET POST PUT DELETE四種方法分別代表對資源的“查詢疲酌、添加、更新了袁、刪除”朗恳。

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

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

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

    他們的含義分別代表:

    • code為0代表調(diào)用成功崭庸,其他會自定義的錯誤碼怀浆;
    • message表示在API調(diào)用失敗的情況下詳細(xì)的錯誤信息谊囚,這個信息可以由客戶端直接呈現(xiàn)給用戶,否則為空执赡;
    • data表示服務(wù)端返回的數(shù)據(jù)镰踏,具體格式由服務(wù)端自定義,API調(diào)用錯誤為空
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沙合,一起剝皮案震驚了整個濱河市奠伪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌首懈,老刑警劉巖绊率,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異猜拾,居然都是意外死亡即舌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門挎袜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顽聂,“玉大人,你說我怎么就攤上這事盯仪∥商拢” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵全景,是天一觀的道長耀石。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任伊佃,我火速辦了婚禮况既,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布亩钟。 她就那樣靜靜地躺著,像睡著了一般鳖轰。 火紅的嫁衣襯著肌膚如雪清酥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天蕴侣,我揣著相機與錄音焰轻,去河邊找鬼。 笑死昆雀,一個胖子當(dāng)著我的面吹牛鹦马,可吹牛的內(nèi)容都是我干的胧谈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荸频,長吁一口氣:“原來是場噩夢啊……” “哼菱肖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起旭从,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤稳强,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后和悦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體退疫,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年鸽素,在試婚紗的時候發(fā)現(xiàn)自己被綠了褒繁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡馍忽,死狀恐怖棒坏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遭笋,我是刑警寧澤坝冕,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站瓦呼,受9級特大地震影響喂窟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜央串,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一磨澡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧质和,春花似錦稳摄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尉共。三九已至褒傅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間袄友,已是汗流浹背殿托。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剧蚣,地道東北人支竹。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓旋廷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親礼搁。 傳聞我的和親對象是個殘疾皇子饶碘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

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