發(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è)資源該如何展示翘紊?
- 客戶端通過(guò)collection-URL /employees發(fā)送POST方法請(qǐng)求,請(qǐng)求消息體包含新資源“Albert Stark”的相關(guān)屬性帆疟。
- 服務(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方法绍妨,通過(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¶2=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