Restful API 设计规范实战
Restful API 設(shè)計(jì)規(guī)范
使用的名詞而不是動(dòng)詞
不應(yīng)該使用動(dòng)詞:
/getAllResources
/createNewResources
/deleteAllResources
GET方法和查詢(xún)參數(shù)不能改變資源狀態(tài):
如果要改變資源的狀態(tài),使用PUT、POST、DELETE。下面是錯(cuò)誤的用GET方法來(lái)修改user的狀態(tài):
GET /users/711?activate GET /users/711/activateRest的核心原則是將你的API拆分為邏輯上的資源。這些資源通過(guò)HTTP被操作(GET,POST,PUT,DELETE)
我們定義資源ticket、user、group:
GET /tickets # 獲取ticket列表
GET /tickets/12 # 查看某個(gè)具體的ticket
POST /tickets # 新建一個(gè)ticket
PUT /tickets/12 #新建ticket 12
DELETE /tickets/12 # 刪除ticket 12
只需要一個(gè)endpoint:/tickets,再也沒(méi)有其他什么命名規(guī)則和url規(guī)則了。
一個(gè)可以遵循的規(guī)則是:雖然看起來(lái)使用復(fù)數(shù)來(lái)描述某一個(gè)資源看起來(lái)特別扭,但是統(tǒng)一所有的endpoint,使用復(fù)數(shù)使得你的URL更加規(guī)整。這讓API使用者更加容易理解,對(duì)開(kāi)發(fā)者來(lái)說(shuō)也更容易實(shí)現(xiàn)。
處理關(guān)聯(lián):
GET /tickets/12/messages # 獲取ticket 12的message列表
GET /tickets/12/messages/5 #獲取ticket 12的message 5
POST /tickets/12/messages 創(chuàng)建ticket 12的一個(gè)message
PUT /tickets/12/messages/5 更新ticket 12的message 5
DELETE /tickets/12/messages/5 刪除ticket 12的message 5
避免層級(jí)過(guò)深的URI
/ 在url中表達(dá)層級(jí),用于按實(shí)體關(guān)聯(lián)關(guān)系進(jìn)行對(duì)象導(dǎo)航,一般根據(jù)id導(dǎo)航。
過(guò)深的導(dǎo)航容易導(dǎo)致url膨脹,不易維護(hù),如 GET /zoos/1/areas/3/animals/4,盡量使用查詢(xún)參數(shù)代替路勁中的實(shí)體導(dǎo)航,如GET /animals?zoo=1&area=3。
結(jié)果過(guò)濾,排序,搜索
url最好越簡(jiǎn)短越好,對(duì)結(jié)果過(guò)濾、排序、搜索相關(guān)的功能都應(yīng)該通過(guò)參數(shù)實(shí)現(xiàn)。
過(guò)濾:例如你想限制GET /tickets 的返回結(jié)果:只返回那些open狀態(tài)的ticket, GET /tickets?state=open 這里的state就是過(guò)濾參數(shù)。
排序:和過(guò)濾一樣,一個(gè)好的排序參數(shù)應(yīng)該能夠描述排序規(guī)則,而不和業(yè)務(wù)相關(guān)。復(fù)雜的排序規(guī)則應(yīng)該通過(guò)組合實(shí)現(xiàn)。排序參數(shù)通過(guò) , 分隔,排序參數(shù)前加 - 表示降序排列。
GET /tickets?sort=-priority #獲取按優(yōu)先級(jí)降序排列的ticket列表
GET /tickets?sort=-priority,created_at #獲取按優(yōu)先級(jí)降序排列的ticket列表,在同一個(gè)優(yōu)先級(jí)內(nèi),先創(chuàng)建的ticket排列在前面。
搜索:有些時(shí)候簡(jiǎn)單的排序是不夠的。我們可以使用搜索技術(shù)來(lái)實(shí)現(xiàn)
GET /tickets?q=return&state=open&sort=-priority,create_at # 獲取優(yōu)先級(jí)最高且打開(kāi)狀態(tài)的ticket,而且包含單詞return的ticket列表。
限制API返回值的域
有時(shí)候API使用者不需要所有的結(jié)果,在進(jìn)行橫向限制的同時(shí)(例如值返回API結(jié)果的前十個(gè)),還應(yīng)該可以進(jìn)行縱向限制,并且這個(gè)功能能有效的提高網(wǎng)絡(luò)帶寬使用率和速度。可以使用fields查詢(xún)參數(shù)來(lái)限制返回的域例如:
GET /tickets?fields=id,subject,customer_name,updated_at&state=open&sort=-updated_at
Response不要包裝
response 的 body直接就是數(shù)據(jù),不要做多余的包裝。錯(cuò)誤實(shí)例:
{"success":true,"data":{"id":1, "name":"xiaotuan"} }更新和創(chuàng)建操作應(yīng)該返回資源
在POST操作以后,返回201created 狀態(tài)碼,并且包含一個(gè)指向新資源的url作為返回頭。
命名方式
是蛇形命名還是駝峰命名?如果使用json那么最好的應(yīng)該是遵守JavaScript的命名方法-駝峰命名法。Java、C# 使用駝峰,python、ruby使用蛇形。
默認(rèn)使用pretty print格式,開(kāi)啟gzip
開(kāi)啟pretty print返回結(jié)果會(huì)更加友好易讀,而且額外的傳輸也可以忽略不計(jì)。如果忘了使用gzip那么傳輸效率將會(huì)大大減少,損失大大增加。
GitHub v3S實(shí)踐經(jīng)驗(yàn)
1.Current Version
通過(guò)Accept字段來(lái)區(qū)分版本號(hào),而不是在url中嵌入版本號(hào):
Accept: application/vnd.github.v3+json
2.Schema
Summary Representation
當(dāng)你請(qǐng)求獲取某一資源的列表時(shí),響應(yīng)僅返回資源的屬性子集。有些屬性對(duì)API來(lái)說(shuō)代價(jià)是非常高的,出于性能的考慮,會(huì)排除這些屬性。要獲取這些屬性,請(qǐng)求"detailed" representation。
Example:當(dāng)你獲取倉(cāng)庫(kù)的列表時(shí),你獲得的是每個(gè)倉(cāng)庫(kù)的summary representation。
GET /orgs/octokit/reposDetailed Representation
當(dāng)你獲取一個(gè)單獨(dú)的資源時(shí),響應(yīng)會(huì)返回這個(gè)資源的所有屬性。
Example:當(dāng)你獲取一個(gè)單獨(dú)的倉(cāng)庫(kù),你會(huì)獲得這個(gè)倉(cāng)庫(kù)的detailed representation。
GET /repos/octokit/octokit.rb3.Parameters
許多API都帶有可選參數(shù)。對(duì)于GET請(qǐng)求,任何不作為路徑構(gòu)成部分的參數(shù)都可以通過(guò)HTTP查詢(xún)參數(shù)傳入。
GET https://api.github.com/repos/vmg/redcarpet/issues?state=closed在這個(gè)例子中,'vmg' 和 'redcarpet' 作為 :owner 和 :repo 的參數(shù),而 :state 作為查詢(xún)參數(shù)。
對(duì)于POST、PATCH、PUT和DELETE的請(qǐng)求,不包含在URL中的參數(shù)需要編碼成JSON傳遞,且 Content-Type為 'application/json'。
Root Endpoint
你可以對(duì)根節(jié)點(diǎn)GET請(qǐng)求,獲取根節(jié)點(diǎn)下的所有API分類(lèi)。
Client Errors
有三種可能的客戶端錯(cuò)誤,在接收到請(qǐng)求體時(shí):
1 發(fā)送非法JSON會(huì)返回 400 Bad Request.
HTTP/1.1 400 Bad Request Content-Length: 35{"message":"Problems parsing JSON"}2 發(fā)送錯(cuò)誤類(lèi)型的JSON值會(huì)返回 400 Bad Request.
HTTP/1.1 400 Bad Request Content-Length: 40{"message":"Body should be a JSON object"}3 發(fā)送無(wú)效的值會(huì)返回 422 Unprocessable Entity.
HTTP/1.1 422 Unprocessable Entity Content-Length: 149{"message": "Validation Failed","errors": [{"resource": "Issue","field": "title","code": "missing_field"}] }我們可以告訴發(fā)生了什么錯(cuò)誤,下面是一些可能的驗(yàn)證錯(cuò)誤碼:
| missing | 資源不存在 |
| missing_field | 資源必需的域沒(méi)有被設(shè)置 |
| invalid | 域的格式非法 |
| already_exists | 另一個(gè)資源的域的值和此處的相同,這會(huì)發(fā)生在資源有唯一的鍵的時(shí)候 |
HTTP Redirects
API v3在合適的地方使用HTTP重定向。客戶端應(yīng)該假設(shè)任何請(qǐng)求都會(huì)導(dǎo)致重定向。重定向在響應(yīng)頭中有一個(gè) Location 的域,此域包含了資源的真實(shí)位置。
HTTP Verbs
API v3力爭(zhēng)使用正確的HTTP動(dòng)詞來(lái)表示每次請(qǐng)求。
| HEAD | 對(duì)任何資源僅請(qǐng)求頭信息 |
| GET | 獲取資源 |
| POST | 創(chuàng)建資源 |
| PATCH | 使用部分的JSON數(shù)據(jù)更新資源 |
| PUT | 取代資源或資源集合 |
| DELETE | 刪除資源 |
Hypermedia
很多資源有一個(gè)或者更多的 *_url 屬性指向其他資源。這意味著服務(wù)端提供明確的URL,這樣客戶端就不必要自己構(gòu)造URL了。
Pagination
請(qǐng)求資源列表時(shí)會(huì)進(jìn)行分頁(yè),默認(rèn)每頁(yè)30個(gè)。當(dāng)你請(qǐng)求后續(xù)頁(yè)的時(shí)候可以使用 ?page 參數(shù)。對(duì)于某些資源,你可以通過(guò)參數(shù) ?per_page自定義每頁(yè)的大小。
curl 'https://api.github.com/user/repos?page=2&per_page=100'需要注意的一點(diǎn)是,頁(yè)碼是從1開(kāi)始的,當(dāng)省略參數(shù) ?page 時(shí),會(huì)返回首頁(yè)。
Basics of Pagination
關(guān)于分頁(yè)的其他相關(guān)信息在響應(yīng)的頭信息的 Link 里提供。比如,去請(qǐng)求一個(gè)搜索的API,查找Mozilla的項(xiàng)目中哪些包含詞匯addClass :
curl -I "https://api.github.com/search/code?q=addClass+user:mozilla"頭信息中Link字段如下:
Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=2>; rel="next", <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel="last"rel="next" 表示下一頁(yè)是 page=2。也就是說(shuō),默認(rèn)情況下所有的分頁(yè)請(qǐng)求都是從首頁(yè)開(kāi)始。rel="last" 提供更多信息,表示最后一頁(yè)是34。即我們還有33頁(yè)的信息包含addClass。
總之,我們應(yīng)該依賴(lài)于Link提供的信息,而不要嘗試自己去猜或者構(gòu)造URL。
Navigating through the pages
既然已經(jīng)知道會(huì)接收多少頁(yè)面,我們可以通過(guò)頁(yè)面導(dǎo)航來(lái)消費(fèi)結(jié)果。我們可以通過(guò)傳遞一個(gè)page參數(shù),例如跳到14頁(yè):
curl -I "https://api.github.com/search/code?q=addClass+user:mozilla&page=14"這是頭信息中Link字段:
Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=15>; rel="next",<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel="last",<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=1>; rel="first",<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=13>; rel="prev"我們會(huì)獲得更多的信息,rel="first"表示首頁(yè),rel="prev"表示前一頁(yè)的頁(yè)碼。通過(guò)這些信息,我們可以構(gòu)造一個(gè)UI界面讓用戶在first、previous、next、last之間進(jìn)行跳轉(zhuǎn)。
Rate Limiting
對(duì)于認(rèn)證的請(qǐng)求,可以每小時(shí)最多請(qǐng)求5000次。對(duì)于沒(méi)有認(rèn)證的請(qǐng)求,限制在每小時(shí)60次請(qǐng)求。
檢查返回的HTTP頭,可以看到當(dāng)前的速率限制:
curl -i https://api.github.com/users/whatever HTTP/1.1 200 OK Server: GitHub.com Date: Thu, 27 Oct 2016 03:05:42 GMT Content-Type: application/json; charset=utf-8 Content-Length: 1219 Status: 200 OK X-RateLimit-Limit: 60 X-RateLimit-Remaining: 48 X-RateLimit-Reset: 1477540017header頭信息告訴你當(dāng)前的速率限制狀態(tài):
| X-RateLimit-Limit | 當(dāng)前用戶被允許的每小時(shí)請(qǐng)求數(shù) |
| X-RateLimit-Remaining | 在當(dāng)前發(fā)送窗口內(nèi)還可以發(fā)送的請(qǐng)求數(shù) |
| X-RateLimit-Reset | 按當(dāng)前速率發(fā)送后,發(fā)送窗口重置的時(shí)間 |
一旦你超過(guò)了發(fā)送速率限制,你會(huì)收到一個(gè)錯(cuò)誤響應(yīng):
HTTP/1.1 403 Forbidden Date: Tue, 20 Aug 2013 14:50:41 GMT Status: 403 Forbidden X-RateLimit-Limit: 60 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1377013266{"message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)","documentation_url": "https://developer.github.com/v3/#rate-limiting" }User Agent Required
所有的API請(qǐng)求必須包含一個(gè)有效的 User-Agent 頭。請(qǐng)求頭不包含User-Agent的請(qǐng)求會(huì)被拒絕。
Conditional requests
大多數(shù)響應(yīng)都會(huì)返回一個(gè) ETag 頭。很多響應(yīng)也會(huì)返回一個(gè) Last-Modified 頭。你可以使用這些頭信息對(duì)這些資源進(jìn)行后續(xù)請(qǐng)求,分別使用 If-None-Match 和 If-Modified-Since頭。如果資源沒(méi)有發(fā)生改變,服務(wù)器端會(huì)返回 304 Not Modified。
Enchant REST API 實(shí)踐經(jīng)驗(yàn)
Requests
Limited HTTP Clients
如果你使用的HTTP客戶端不支持PUT、PATCH、DELETE方法,發(fā)送一個(gè)POST請(qǐng)求,頭信息里包含X-HTTP-Method-Override字段,它的值是實(shí)際需要的動(dòng)詞。
$ curl -u email:password https://site.enchant.com/api/v1/users/543abc \-X POST \-H "X-HTTP-Method-Override: DELETE"Rate Limiting
所有響應(yīng)的頭部包含描述當(dāng)前限流狀態(tài)的字段:
Rate-Limit-Limit: 100 Rate-Limit-Remaining: 99 Rate-Limit-Used: 1 Rate-Limit-Reset: 20Rate-Limit-Limit - 當(dāng)前時(shí)間段內(nèi)允許的總的請(qǐng)求數(shù)
Rate-Limit-Remaining - 當(dāng)前時(shí)間段內(nèi)還剩余的請(qǐng)求數(shù)
Rate-Limit-Used - 本次所使用的請(qǐng)求數(shù)
Rate-Limit-Reset - 重置所需秒數(shù)
如果速率限制被打破,API會(huì)返回 429 Too Many Requests 的狀態(tài)碼。在這種情況下,你的應(yīng)用不應(yīng)該再發(fā)送任何請(qǐng)求直到 Rate-Limit-Reset 所規(guī)定的時(shí)間過(guò)去。
Field Filtering
你可以自己限制響應(yīng)返回的域。只需要你傳遞一個(gè) fields 參數(shù),用逗號(hào)分隔所需要的域,比如:
GET /api/v1/users?fields=id,first_nameCounting
所有返回一個(gè)集合的URL,都會(huì)提供count統(tǒng)計(jì)所有結(jié)果的個(gè)數(shù)。要獲取count值需要加一個(gè) count=true 的參數(shù)。count會(huì)在消息頭中的Total-Count 字段中返回。
GET /api/v1/tickets?count=true
200 OK Total-Count: 135 Rate-Limit-Limit: 100 Rate-Limit-Remaining: 98 Rate-Limit-Used: 2 Rate-Limit-Reset: 20 Content-Type: application/json[... results ... ]
count表示所有現(xiàn)存結(jié)果的數(shù)量,而不是此次響應(yīng)返回的結(jié)果的數(shù)量。
Enveloping
如果你的HTTP客戶端難以讀取狀態(tài)碼和頭信息,我們可以將所有都打包進(jìn)響應(yīng)消息體中。我們只需要傳遞參數(shù) envelope=true,而API會(huì)始終返回200的HTTP狀態(tài)碼。真正的狀態(tài)碼、頭信息和響應(yīng)都在消息體中。
GET /api/v1/users/does-not-exist?envelope=true200 OK
{"status": 404,"headers": {"Rate-Limit-Limit": 100,"Rate-Limit-Remaining": 50,"Rate-Limit-Used": 0,"Rate-Limit-Reset": 25},"response": {"message": "Not Found"} }
其他如 分頁(yè)、排序等,enchant的設(shè)計(jì)規(guī)范和GitHub v3大致相同,不在贅述。
原文鏈接
https://segmentfault.com/a/11...
總結(jié)
以上是生活随笔為你收集整理的Restful API 设计规范实战的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Builder设计模式
- 下一篇: spring 读取配置文件的优先级