參考1, 參考2 RESTful架構,一種互聯網軟體架構。它結構清晰、符合標準、易於理解、擴展方便,所以正得到越來越多網站的採用 起源 REST這個詞,是Roy Thomas Fielding在他2000年的博士論文中提出的 Fielding將他對互聯網軟體的架構原則,定名為REST,即Repres ...
RESTful架構,一種互聯網軟體架構。它結構清晰、符合標準、易於理解、擴展方便,所以正得到越來越多網站的採用
起源 REST這個詞,是Roy Thomas Fielding在他2000年的博士論文中提出的
Fielding將他對互聯網軟體的架構原則,定名為REST,即Representational State Transfer的縮寫。這個片語的翻譯是"表現層狀態轉化",如果一個架構符合rest原則,我們就稱它為restful架構
資源(resources)
rest翻譯為表現層狀態轉化,它沒有指名是誰的表現層,缺乏主語,在這裡,我們用資源當做主語,即為資源的表現層狀態轉化。
所謂資源,就是網路上的一個實體,它可以是一張圖片,一段文本。。。我們可以用一個url指向它,用戶通過url就能訪問到這個資源。
普及兩個概念 uri與url,uri(統一資源標識符 Uniform Resource Identifier),url(統一資源定位符 Uniform Resource Locator),其中url是uri的子集
表現層(representation)
資源是一種信息實體,它可以有多種外在表現,比如文本可以以txt格式表現,也可以用html,json,xml格式表現,我們把資源具體呈現出的形式叫做它的表現層。
uri指向資源,但不代表資源的形式,http頭部中的accept和content-type等表達了資源的形式
狀態轉化(state transfer)
訪問一個uri,就代表客戶和伺服器發生了一個交互,這個過程會涉及到數據和狀態的變化。
互聯網通信協議http協議,是無狀態的,不能記錄資源的各種狀態,資源的狀態保存在伺服器端,如果要操作伺服器上的資源,就要通過一些手段,讓伺服器知道,從而讓伺服器的資源發生狀態轉化,這個變化是建立在表現層之上的,就是表現層狀態轉化。
客戶端使用的手段,只能是http協議,http協議里有很多操作方式的動詞比如get,post,put,delete,它們對應了四種基本操作,get用來獲取資源(查),post用來新建資源(增)或者修改資源(改),put用來更新資源(改),delete用來刪除資源(刪)。
綜上
restfull架構即為
每個url指向一種資源,客戶端和伺服器之間傳遞資源的某種表現層,客戶通過4個http動詞,對伺服器資源進行操作,實現伺服器資源表現層狀態轉化
常見誤區
url中含有動詞,比如/users/update/1,其中update是動詞(手段),這個url就不滿足restful架構,正確的寫法是/user/1,然後用put方式或post方式
url中帶有版本號,不同版本,可以理解為同一資源的不同表現形式,可以把版本號放到http頭信息中,從url剔除
restful api設計
一、協議
api與用戶的通信協議採用http協議
二、功能變數名稱
儘量將api部署到專用功能變數名稱下,比如 http://api.test.com。如果api不多且比較簡單,不太會進一步擴展,也可以放到主功能變數名稱下,比如http://test.com/api/...
三、版本(version)
將版本號放到http頭信息中,也有說法說版本號放到url中更方便直接,github的是放在http頭信息里的
四、路徑(endpoint)
路徑又稱“終點”(endpoint),表示api具體網址,在restful架構中,每個網址代表一種資源(resource),所以網址中不能有動詞,比如說一個api提供動物園zoo信息,其中包括各種動物以及雇員信息,符合restful架構的url可如下設計
- http://api.test.com/zoos
- http://api.test.com/animals
- http://api.test.com/employees
- http://api.test.com/zoo/1
五、http動詞
對於資源的具體操作類型,由http動詞表示,常用的http動詞如下
- get(select),從伺服器獲取資源(一項或多項)
- post(create),在伺服器新建一個資源
- put(update),在伺服器更新資源(客戶端提供改變後的完整資源)
- patch(update),在伺服器更新資源(客戶端提供改變的屬性)比如svn中針對修改打patch操作
- delete(delete),從伺服器刪除資源
兩個不常用http動詞
- head,獲取資源元數據
- options,獲取信息,關於資源的哪些屬性是客戶端可以修改的
例子如下
- get /zoos,獲取所有動物園
- post /zoos,新建一個動物園
- get /zoo/id,獲取某個指定動物園的信息
- put /zoo/id,更新某個指定動物園信息(提供該動物園修改後的完整信息)
- patch /zoo/id,更新某個指定動物園信息(提供該動物園部分信息)
- delete /zoo/id, 刪除某個動物園
- get /zoos/id/animals,獲取指定動物園的所有動物
- delete /zoos/id/anamal/id,刪除指定動物園的指定動物
六、信息過濾(filter)
如果資源數量過多,伺服器不好一次性全部返回給用戶,api應該提供參數,過濾返回結果,比如指定篩選條件,排序,分頁,限制返回數量等
- ?animalType=1,指定篩選條件
- ?sort=age&order=desc,排序
- ?page=2&perPage=10,分頁
- ?limit=10,限制返回數量
參數設計允許冗餘,即允許api路徑與url參數偶爾重覆,比如 get /zoos/id/animals與 get /animals?zooId=id含義相同
七、狀態碼(status code)
伺服器向用戶返回的狀態碼和提示信息,常見的列表以下
- 200 OK - [get],伺服器成功返回用戶請求的數據,該操作是冪等的(idempotent)
- 201 CREATED - [post/put/patch],用戶新建或修改數據成功
- 202 ACCEPTED - [*],表示請求已進入後臺排隊(非同步任務)
- 204 NO CONTENT - [delete],用戶數據刪除成功
- 400 INVALID REQUEST - [post/put/patch],用戶發出的請求錯誤,伺服器沒有進行新建或修改數據的操作,該操作冪等
- 401 Unauthorized - [*],用戶沒有許可權(令牌,用戶,密碼錯誤)
- 403 Forbidden - [*],表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的
- 404 NOT FOUND - [*],用戶發出的請求針對的是不存在的記錄,伺服器沒有進行操作,該操作冪等
- 406 Not Acceptable - [get],用戶請求的格式不可得(比如用戶請求json格式,但是只有xml格式)
- 410 Gone - [get],用戶請求的資源被永久刪除,且不會再得到
- 500 INTERNAL SERVER ERROR - [*],伺服器發生錯誤,用戶將無法判斷發出的請求是否成功
八、錯誤處理(error handling)
如果狀態碼是4xx,應該向用戶返回錯誤信息,一般來說,返回的信息中將error作為鍵名,出錯信息作為鍵值即可
九、返回結果
針對不同操作,伺服器向用戶返回的結果應該符合以下規範
- get /collection 返回資源對象的列表(數組)
- get /collection/resource 返回單個資源對象
- post /collection 返回新生成的資源對象
- put /collection/resource 返回完整的資源對象
- patch /collection/resource 返回完整的資源對象
- delete /collection/resource 返回一個空文檔
十、hypermedia api
restful api最好做到hypermedia,即返回結果中信息豐富一點,提供鏈接,連向其他api方法,使得用戶不用去查詢文檔,也知道下一步應該做什麼。比如當用戶向api.test.com的根目錄發出請求時,得到的文檔包含以下結果
{
"link":{ "rel": "collection http://api.test.com/zoos",
"href": "http://api/test.com",
"title": "list of zoos",
"type": "application/vnd.yourformat+json"
} }
上面代碼表示,文檔中有一個link屬性,用戶讀取這個屬性就知道下一步該調用什麼api,rel代表這個api與當前網址的關係(collection關係,並給出該collection網址),href表示api路徑,title表示api的標題,type表示返回類型。
hypermedia api的設計也被稱為HATEOAS,github的api就是這種設計,請求github的api會得到一個所有可用api網址列表
{ "current_user_url": "https://api.github.com/user", "current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}", "authorizations_url": "https://api.github.com/authorizations", "code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}", "commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}", "emails_url": "https://api.github.com/user/emails", "emojis_url": "https://api.github.com/emojis", "events_url": "https://api.github.com/events", "feeds_url": "https://api.github.com/feeds", "followers_url": "https://api.github.com/user/followers", "following_url": "https://api.github.com/user/following{/target}", "gists_url": "https://api.github.com/gists{/gist_id}", "hub_url": "https://api.github.com/hub", "issue_search_url": "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}", "issues_url": "https://api.github.com/issues", "keys_url": "https://api.github.com/user/keys", "notifications_url": "https://api.github.com/notifications", "organization_repositories_url": "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}", "organization_url": "https://api.github.com/orgs/{org}", "public_gists_url": "https://api.github.com/gists/public", "rate_limit_url": "https://api.github.com/rate_limit", "repository_url": "https://api.github.com/repos/{owner}/{repo}", "repository_search_url": "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}", "current_user_repositories_url": "https://api.github.com/user/repos{?type,page,per_page,sort}", "starred_url": "https://api.github.com/user/starred{/owner}{/repo}", "starred_gists_url": "https://api.github.com/gists/starred", "team_url": "https://api.github.com/teams", "user_url": "https://api.github.com/users/{user}", "user_organizations_url": "https://api.github.com/user/orgs", "user_repositories_url": "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}", "user_search_url": "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}" }
從上面可以看到,如果想獲取當前用戶的信息,應該去訪問api.github.com/user,會得到如下返回結果
{ "message": "Requires authentication", "documentation_url": "https://developer.github.com/v3/users/#get-the-authenticated-user" }
返回結果給出了伺服器的提示信息,以及相應文檔地址
十一、其他
api的身份認證可以使用Oauth框架
伺服器返回的數據格式,儘量使用json,xml