RESTful API設(shè)計(jì)最佳實(shí)踐(轉(zhuǎn)譯)

發(fā)布資源的URL改如何設(shè)計(jì)阻塑?名詞使用復(fù)數(shù)或者單數(shù)脐湾?對(duì)同一個(gè)資源犁珠,改用多少URL來(lái)描述發(fā)布袱蜡?對(duì)創(chuàng)建一個(gè)資源應(yīng)該用怎樣的HTTP方法(POST,PUT,GET,DELETE)描述丝蹭?該如何處置可選參數(shù)?最好的描述分頁(yè)和版本的方法又是什么坪蚁?在設(shè)計(jì)RESTful API的時(shí)候奔穿,因?yàn)榇嬖诤芏喾N可能性,故在設(shè)計(jì)的時(shí)候需要十分的謹(jǐn)慎小心敏晤。在這篇文章中贱田,一起來(lái)看看設(shè)計(jì)RESTful API的最佳實(shí)踐。

每個(gè)資源嘴脾,使用兩個(gè)URL

一個(gè)用來(lái)表示集合男摧,另一個(gè)用來(lái)表示特定的某個(gè)元素。

/employees #資源集合
/employees/56 #資源特定元素

用名詞替代動(dòng)詞表示URL

以下這些寫法,只會(huì)讓URL看起來(lái)完全沒(méi)有經(jīng)過(guò)精心設(shè)計(jì):

/getAllEmployees
/getAllExternalEmployees
/createEmployee
/updateEmployee

作為替代方案彩倚,參考以下設(shè)計(jì)筹我。

使用HTTP方法來(lái)操作URL

GET /employees
GET /employees?state=external
POST /employees
PUT /employees/56

URL用來(lái)指定需要操作的資源,HTTP方法用來(lái)指定對(duì)資源進(jìn)行何種操作帆离。通過(guò)使用HTTP的方法蔬蕊,GET,POST哥谷,PUT岸夯,DELETE,可以用來(lái)表示CRUD函數(shù)功能(Create们妥,Read猜扮,Update,Delete)监婶。

  • Read:使用GET方法讀取資源旅赢。GET請(qǐng)求不會(huì)改變資源狀態(tài),無(wú)任何副作用惑惶,并且是冪等的煮盼。GET方法是包含只讀語(yǔ)義。因此带污,可以緩存響應(yīng)結(jié)果僵控。
  • Create:使用POST方法創(chuàng)建新的資源。
  • Update:使用PUT方法更新已存在的資源鱼冀。
  • Delete:使用DELETE方法刪除已存在的資源报破。

通過(guò)2個(gè)URL和4個(gè)HTTP方法,可以等價(jià)于一系列方法操作集合千绪,如下所示:

/ POST(Create) GET(Read) PUT(Update) DELETE(Delete)
/employees 創(chuàng)建新雇員 獲取所有雇員 批量更新雇員 刪除所有雇員
/employees/56 ... 獲取編號(hào)56雇員詳情 更新編號(hào)56雇員詳情 刪除編號(hào)56雇員

在表示集合URL上充易,使用POST來(lái)創(chuàng)建新的資源

對(duì)于一個(gè)主從式交互過(guò)程,創(chuàng)建一個(gè)資源該如何展示翘紊?


在collection-URL上蔽氨,使用POST方法來(lái)創(chuàng)建資源
  1. 客戶端通過(guò)collection-URL /employees發(fā)送POST方法請(qǐng)求,請(qǐng)求消息體包含新資源“Albert Stark”的相關(guān)屬性帆疟。
  2. 服務(wù)端為新的雇員創(chuàng)建ID鹉究,創(chuàng)建雇員相關(guān)信息并向客戶端發(fā)送回響應(yīng)。響應(yīng)體包含訪問(wèn)新創(chuàng)建資源對(duì)應(yīng)的URL路徑踪宠,/employees/21自赔。

在表示特定袁術(shù)URL上,使用PUT來(lái)表示更新資源

使用PUT方法柳琢,更新已存在的資源

客戶端使用PUT方法绍妨,通過(guò)URL路徑 /employees/21 發(fā)送請(qǐng)求润脸。HTTP消息體包含更新的屬性信息(ID編號(hào)21雇員的新名字)。
服務(wù)端更新 ID編號(hào)21雇員的新名字他去,并通過(guò)返回HTTP狀態(tài)碼200毙驯,確認(rèn)此次更新成功。

堅(jiān)持使用復(fù)數(shù)名詞

相比于如下RESTful URL:

/employee
/employee/21

如下RESTful URL更為合適:

/employees
/employees/21

事實(shí)上灾测,這只能算一種嘗試爆价。但復(fù)數(shù)形式更加的普遍。此外媳搪,這更加直觀铭段。尤其是在使用collection-URL,用來(lái)表示GET /employees?state=external POST /employees PUT /employees/56秦爆,但最重要的是序愚,避免混合使用復(fù)數(shù)和單數(shù)形式,容易讓人困惑并且易于出錯(cuò)等限。

使用“爸吮?”來(lái)拼接可選或者復(fù)雜的參數(shù)

不要使用如下URL表示方法:

GET /employees
GET /externalEmployees
GET /internalEmployees
GET /internalAndSeniorEmployees

保持URL簡(jiǎn)單和URL集合最小化。使用一個(gè)基礎(chǔ)的URL代表一個(gè)資源望门,將復(fù)雜的拗胜、可選的參數(shù)遷移到URL后作為字符串參數(shù)。如下:

GET /employees?state=internal&maturity=senior

使用HTTP狀態(tài)碼

RESTful Web服務(wù)應(yīng)該通過(guò)適當(dāng)?shù)臓顟B(tài)碼返回給客戶端的請(qǐng)求怒允。

  • 2xx – 成功 – 操作成功
  • 4xx – 客戶端錯(cuò)誤 – 客戶端操作錯(cuò)誤 (比如客戶端發(fā)送非法請(qǐng)求或者本身身份并未認(rèn)證)
  • 5xx – 服務(wù)端錯(cuò)誤 – 后臺(tái)出現(xiàn)錯(cuò)誤 (比如后臺(tái)無(wú)法處理請(qǐng)求)

參考維基上HTTP狀態(tài)碼 。不論如何锈遥,如果利用所有的狀態(tài)碼纫事,勢(shì)必會(huì)讓API的調(diào)用者感到困惑。所有將狀態(tài)碼的使用保持在一個(gè)較小的集合內(nèi)所灸,通常如下所示:

2XX:Success 3XX:Redirect 4XX:Client Error 5XX:Server Error
200 OK 301 Moved Permanently 400 Bad Request 500 Internal Server Error
201 Created 304 Not Modified 401 Unauthorized .
. . 403 Forbidden .
. . 404 Not Found .

提供有效的錯(cuò)誤碼或錯(cuò)誤提示

除了要有適當(dāng)?shù)臓顟B(tài)碼丽惶,還需要在HTTP響應(yīng)消息體中提供有用并稍顯啰嗦的關(guān)于錯(cuò)誤信息的描述。
請(qǐng)求:

GET /employees?state=super
響應(yīng):
// 400 Bad Request
{
"message": "You submitted an invalid state. Valid state values are 'internal' or 'external'",
"errorCode": 352,
"additionalInformation" : "http://www.domain.com/rest/errorcode/352"
}

使用駝峰規(guī)則表示屬性名稱

對(duì)屬性定義使用駝峰規(guī)則爬立。

{ "yearOfBirth": 1982 }

不用使用下劃線(year_of_birth) 或者全大寫字母開(kāi)頭钾唬。通常來(lái)說(shuō),客戶端通常使用JavaScript來(lái)解析RESTful web服務(wù)返回的響應(yīng)信息侠驯。常見(jiàn)于抡秆,客戶端通常將JSON轉(zhuǎn)換成JavaScript對(duì)象,比如:

var person=JSON.parse(response)

因此吟策,使用JavaScript的約定是的JavaScript更容易閱讀和習(xí)慣性表達(dá)儒士。
比較如下表達(dá)方式:

person.year_of_birth //violates JavaScript convention
person.YearOfBirth //suggests constructor method

person.yearOfBirth //nice!

總是強(qiáng)制在URL中包含版本號(hào)

在發(fā)布RESTful API的時(shí)候,需要帶上版本號(hào)檩坚。強(qiáng)制將版本號(hào)發(fā)布到URL上着撩,這樣API能更加容易發(fā)生演進(jìn)诅福。將版本號(hào)加入API,調(diào)用的客戶端可以根據(jù)他們自身情況有條理的對(duì)現(xiàn)有API遷移和過(guò)度拖叙∶ト螅客戶端不會(huì)因?yàn)橥蝗怀霈F(xiàn)新的行為發(fā)生改變的API有太多的困惑。
使用“v”開(kāi)頭薯鳍,并跟上數(shù)字版本號(hào)作為標(biāo)記咖气。

/v1/employees

不需要使用小版本號(hào)(“v1.2”),因?yàn)锳PI的改變不會(huì)非常頻繁辐啄。相比較于API和文檔語(yǔ)義采章,作為API的實(shí)現(xiàn)可以經(jīng)常性的改進(jìn)。

提供分頁(yè)查詢支持

一次將所有資源從數(shù)據(jù)庫(kù)返回絕對(duì)不是一個(gè)好的注意壶辜。所以悯舟,需要提供分頁(yè)機(jī)制。一般使用參數(shù) offset 和 limit砸民,向數(shù)據(jù)庫(kù)提供過(guò)濾抵怎。

/employees?offset=30&limit=15 #returns the employees 30 to 45

需要對(duì)客戶端忽略的參數(shù)提供默認(rèn)值,防止返回全部的查詢資源結(jié)果岭参。通常如 offset=0 和 limit=10 是比較適當(dāng)?shù)姆刺琛<偃绾笈_(tái)檢索十分昂貴,則需要增加更多的參數(shù)限制演侯。

/employees #returns the employees 0 to 10

此外姿染,在使用分頁(yè)時(shí),需要返回客戶端資源總數(shù)秒际,
請(qǐng)求:

GET /employees

響應(yīng):

{
"offset": 0,
"limit": 10,
"total": 3465,
"employees": [
//...
]
}

對(duì)非資源型響應(yīng)使用動(dòng)詞表達(dá)

有時(shí)候悬赏,一個(gè)API的響應(yīng)并不包括資源,如下:

GET /translate?from=de_DE&to=en_US&text=Hallo
GET /calculate?para2=23&para2=432

這種情況下娄徊,API不返回任何資源闽颇。相反,需要執(zhí)行相關(guān)操作和返回結(jié)果寄锐。因?yàn)楸啵赨RL設(shè)計(jì)上,需要使用動(dòng)詞用來(lái)替代名詞橄仆,用以區(qū)分非資源響應(yīng)和資源相關(guān)的響應(yīng)剩膘。

考慮特定資源和跨域資源查找

提供對(duì)資源指定類型的查找是方便的。只要使用一致的collection-URL沿癞,然后加上查找關(guān)鍵詞即可援雇。

GET /employees?query=Paul
加入需要提供一個(gè)所有資源的查詢,需要一個(gè)稍微不同的方法椎扬。想想上面提到的關(guān)于非資源類URL設(shè)計(jì)惫搏,使用動(dòng)詞替代名稱具温。因此,設(shè)計(jì)的查詢URL可參考如下:
GET /search?query=Paul //returns employees, customers, suppliers etc.

通過(guò)API筐赔,提供其他鏈接

理想上來(lái)說(shuō)铣猩,可以不需要客戶端動(dòng)態(tài)構(gòu)造URL,來(lái)訪問(wèn)REST API茴丰,可以考慮下面一個(gè)例子达皿。
一個(gè)客戶端想訪問(wèn)一個(gè)雇員的工資清單。此外贿肩,他還知道通過(guò)增加關(guān)鍵字“salaryStatements”到雇員URL(如:/employees/21/salaryStatements)訪問(wèn)工資清單峦椰。這種字符串串聯(lián)非常容易出錯(cuò)躬存,并非很難維護(hù)度硝。但假如改變工資清單的訪問(wèn)方式(如使用新的“salary-statements”或者“paySlips”)踏幻,則所有客戶端都無(wú)法訪問(wèn)察署。
最好的方式是在響應(yīng)中提供相關(guān)鏈接,比如:
請(qǐng)求:

GET /employees/

響應(yīng):

//...
{
"id":1,
"name":"Paul",
"links": [
{
"rel": "salary",
"href": "/employees/1/salaryStatements"
}
]
},
//...

這樣掸屡,假如客戶端依賴返回的鏈接獲取工資清單圈澈,即使改變API痘昌,也不會(huì)出現(xiàn)無(wú)法訪問(wèn)的問(wèn)題茂嗓,因?yàn)榭蛻舳丝偸强梢垣@取一個(gè)合法的URL(即使一直在改變URL)餐茵。另外一個(gè)好處是,服務(wù)端的API變的更加具有自我描述性述吸,需要更少的文檔忿族。
當(dāng)在分頁(yè)使用中,可以提供下一頁(yè)或者上一頁(yè)的鏈接蝌矛。僅僅提供鏈接和相關(guān)的 offset 和 limit 參數(shù)肠阱。

GET /employees?offset=20&limit=10

{
"offset": 20,
"limit": 10,
"total": 3465,
"employees": [
//...
],
"links": [
{
"rel": "nextPage",
"href": "/employees?offset=30&limit=10"
},
{
"rel": "previousPage",
"href": "/employees?offset=10&limit=10"
}
]
}

以上文章的 原文地址 ,可以看原文如何寫朴读。

PS:第一次翻譯,翻譯的很局促走趋,大家見(jiàn)諒衅金。有表達(dá)不合適的,大家提簿煌,我修改氮唯。

這是我博客新地址,歡迎大家一起交流 https://lxzchen.github.io

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末姨伟,一起剝皮案震驚了整個(gè)濱河市惩琉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夺荒,老刑警劉巖瞒渠,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件良蒸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡伍玖,警方通過(guò)查閱死者的電腦和手機(jī)嫩痰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)窍箍,“玉大人串纺,你說(shuō)我怎么就攤上這事∫” “怎么了纺棺?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)邪狞。 經(jīng)常有香客問(wèn)我祷蝌,道長(zhǎng),這世上最難降的妖魔是什么外恕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任杆逗,我火速辦了婚禮,結(jié)果婚禮上鳞疲,老公的妹妹穿的比我還像新娘罪郊。我一直安慰自己,他們只是感情好尚洽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布悔橄。 她就那樣靜靜地躺著,像睡著了一般腺毫。 火紅的嫁衣襯著肌膚如雪癣疟。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天潮酒,我揣著相機(jī)與錄音睛挚,去河邊找鬼。 笑死急黎,一個(gè)胖子當(dāng)著我的面吹牛扎狱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勃教,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼淤击,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了故源?” 一聲冷哼從身側(cè)響起污抬,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绳军,沒(méi)想到半個(gè)月后印机,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體矢腻,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年耳贬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了踏堡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咒劲,死狀恐怖顷蟆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腐魂,我是刑警寧澤帐偎,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站蛔屹,受9級(jí)特大地震影響削樊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜兔毒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一漫贞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧育叁,春花似錦迅脐、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至龟梦,卻和暖如春隐锭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背计贰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工钦睡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人躁倒。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓赎婚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親樱溉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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