為了賬號安全,請及時綁定郵箱和手機立即綁定

RESTFUL Api 學習小記

2017.04.18 20:49 5612瀏覽
何為RESTFUL

REST是Representational State Transfer的縮寫,翻譯為中文就是:表現層狀態轉化。
REST這個詞是由Roy Fielding在2000年的博士論文中首次提出的。
Fielding將他對互聯網軟件的架構原則定義為:REST,如果一個架構符合REST原則,就稱為RESTFUL架構。

如何理解REST

要理解REST也就是Representational State Transfer,首先要理解一個概念:Resources,也就是資源,
在REST這個短語中,其實省略了主語:Resources,表現層其實指的是資源的表現層。
所謂的資源,就是網絡上的一個實體,它可以是一段文本,一首歌曲,一張圖片,一個服務,總之是一種真實的存在,可以用URL指向它,每種資源都有自己獨一無二的URL,要獲取這個資源,訪問它的URL即可。
接下來說說Representational,因為上面說了,資源是一種實體,它可以有多種外在表現形式,我們把資源表現出來的形式,稱之為“表現層“。
比如一段文本,可以以txt格式顯示,也可以以html或者xml,json等多種格式顯示。
它具體的表現形式,應該在報文頭中用AcceptContent-Type指定,這兩個字段是對“表現層“的描述。eg:

accept: application/json
accept-encoding: gzip, deflate
accept-language: en-US,en;q=0.8
content-type: application/json
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

最后就是狀態轉化了,訪問一個網站,就代表了客戶端和服務器的一個互動過程。在這個過程中,勢必涉及到數據和狀態的變化。
互聯網通信協議HTTP協議,是一個無狀態協議。這意味著,所有的狀態都保存在服務器端。因此,如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生"狀態轉化"(State Transfer)。而這種轉化是建立在表現層之上的,所以就是"表現層狀態轉化"。
客戶端用到的手段,只能是HTTP協議。
綜上所述:所謂的RESTful架構需要滿足以下三點:

  • 每個url代表一種資源
  • 客戶端和服務器之間傳遞這種資源的某種表現形式
  • 客戶端通過http動詞,對服務器上的某種資源進行操作,以實現“表現層狀態轉化”
如何設計RESTful API

參考文章:

一、協議

API與用戶的通信協議,總是使用HTTPs協議。

二、域名

應該盡量將api部署在專門的域名下:

https://api.example.com

如果api很簡單,不需要考慮其擴展性,也可以放在主域名下:

https://example.com/api/
三、版本

處理版本號的方式有兩種:

  • 將版本號放在放入url:
    https://api.example/com/v1/
  • 將版本號放在HTTP報文頭中,但是不如放在url中只管和方便。Github是采用這種方式的。
    如下圖所示:
    圖片描述
    使用curl https://api.github.com/users/whatever -i命令查看某一個github提供的api,可以從圖上看到有這樣一行:X-GitHub-Media-Type: github.v3; format=json,(以X-開頭的都是自定義頭,并不包含于HTTP標準),用以描述當前api的版本信息。
四、路徑

路徑又稱為終點(endpoint),(配置過webservice和wcf表示這個詞好熟悉-。-),表示API的具體網址。
在RESTful架構中,每個網址代表一種資源(resource),所以網址中不能有動詞,只能有名詞,而且所用的名詞往往與數據庫的表格名對應。一般來說,數據庫中的表都是同種記錄的"集合"(collection),所以API中的名詞也應該使用復數。
舉例來說,有一個API提供動物園(zoo)的信息,還包括各種動物和雇員的信息,則它的路徑應該設計成下面這樣。

https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees
五、HTTP動詞

對于資源的具體操作類型,由HTTP動詞表示。
常見的命令如下:((括號里是對應的SQL命令)

GET(SELECT):從服務器取出資源(一項或多項)
POST(CREATE):在服務器新建一個資源。
PUT(UPDATE):在服務器更新資源(客戶端提供改變后的完整資源)
PATCH(UPDATE):在服務器更新資源(客戶端提供改變的屬性)
DELETE(DELETE):從服務器刪除資源

還有兩個不常用的HTTP動詞:

HEAD:獲取資源的元數據
OPTIONS:獲取信息,關于資源的哪些屬性是客戶端可以改變的

以下是一些示例:

GET /zoos:列出所有動物園
POST /zoos:新建一個動物園
GET /zoos/ID:獲取某個指定動物園的信息
PUT /zoos/ID:更新某個指定動物園的信息(提供該動物園的全部信息)
PATCH /zoos/ID:更新某個指定動物園的信息(提供該動物園的部分信息)
DELETE /zoos/ID:刪除某個動物園
GET /zoos/ID/animals:列出某個指定動物園的所有動物
DELETE /zoos/ID/animals/ID:刪除某個指定動物園的指定動物
六、過濾信息

如果記錄數量很多,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。
以下是一些常見的過濾參數示例:

?limit=10:指定返回記錄的數量
?offset=10:指定返回記錄的開始位置。
?page=2&per_page=100:指定第幾頁,以及每頁的記錄數。
?sortby=name&order=asc:指定返回結果按照哪個屬性排序,以及排序順序。
?animal_type_id=1:指定篩選條件

參數的設計允許存在冗余,即允許API路徑和URL參數偶爾有重復。比如,GET /zoo/ID/animals 與 GET /animals?zoo_id=ID 的含義是相同的。

七、狀態碼

服務器向用戶返回的狀態碼和提示信息,常見的有以下一些(方括號中是該狀態碼對應的HTTP動詞)。

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]:用戶請求的資源被永久刪除,且不會再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。
500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。

完整的狀態碼戳這里
中文的見這里:(摘自網絡,僅供參考)

Informational 1xx
表示臨時響應并需要請求者繼續執行操作的狀態代碼。
100 Continue
(繼續)說明部分請求已成功收到,其余的現在應該發出。
101 Switching Protocols
(切換協議) 請求者已要求服務器切換協議,服務器已確認并準備切換。

Successful 2xx
表示成功處理了請求的狀態代碼
200 OK
表示成功處理了請求的狀態代碼。
201 Created
(已創建) 請求成功并且服務器創建了新的資源。
202 Accepted
(已接受) 服務器已接受請求,但尚未處理。
203 Non-Authoritative Information
(非授權信息) 服務器已成功處理了請求,但返回的信息可能來自另一來源。
204 No Content
(無內容) 服務器成功處理了請求,但沒有返回任何內容。
205 Reset Content
(重置內容) 服務器成功處理了請求,但沒有返回任何內容。 與 204 響應不同,此響應要求請求者重置文檔視圖(例如,清除表單內容以輸入新內容)。
206 Partial Content
服務器成功處理了部分 GET 請求。

Redirection 3xx
要完成請求,需要進一步操作。 通常,這些狀態代碼用來重定向。 Google 建議您在每次請求中使用重定向不要超過 5 次。 您可以使用網站管理員工 具查看一下 Googlebot 在抓取重定向網頁時是否遇到問題。 診斷下的網絡抓取頁中列出了由于重定向錯誤而導致 Googlebot 無法抓取的 網址。
300 Multiple Choices
(多種選擇) 針對請求,服務器可執行多種操作。 服務器可根據請求者 (user agent) 選擇一項操作,或提供操作列表供請求者選擇。
301 Moved Permanently
(永久移動) 請求的網頁已永久移動到新位置。 服務器返回此響應(對 GET 或 HEAD 請求的響應)時,會自動將請求者轉到新位置。
302 Found
(臨時移動) 服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以后的請求。 此代碼與響應 GET 或 HEAD 請求的 301 代碼類似,會自動將請求者轉到不同的位置。
303 See Other
(查看其他位置) 請求者應當對不同的位置使用單獨的 GET 請求來檢索響應時,服務器返回此代碼。 對于除 HEAD 之外的所有請求,服務器會自動轉到其他位置。
304 Not Modified
(未修改) 自從上次請求后,請求的網頁未修改過。 服務器返回此響應時,不會返回網頁內容。 如果網頁自請求者上次請求后再也沒有更改過,您應當將服務器配置為返回此響應(稱為 If-Modified-Since HTTP 標頭)。
305 Use Proxy
(使用代理) 請求者只能使用代理訪問請求的網頁。 如果服務器返回此響應,還表示請求者應使用代理。
306 (Unused)
307 Temporary Redirect
(臨時重定向) 服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以后的請求。 此代碼與響應 GET 和 HEAD 請求的 301 代碼類似,會自動將請求者轉到不同的位置。

Client Error 4xx
這些狀態代碼表示請求可能出錯,妨礙了服務器的處理。
400 Bad Request
(錯誤請求) 服務器不理解請求的語法。
401 Unauthorized
(未授權) 請求要求身份驗證。 對于需要登錄的網頁,服務器可能返回此響應。
402 Payment Required
未被使用的代碼,被標識為“保留,以備經來使用”
403 Forbidden
(禁止) 服務器拒絕請求。
404 Not Found
(未找到) 服務器找不到請求的網頁。
405 Method Not Allowed
(方法禁用) 禁用請求中指定的方法。
406 Not Acceptable
(不接受) 無法使用請求的內容特性響應請求的網頁
407 Proxy Authentication Required
(需要代理授權) 此狀態代碼與 401(未授權)類似,但指定請求者應當授權使用代理。 如果服務器返回此響應,還會指明請求者應當使用的代理。
408 Request Timeout
(請求超時) 服務器等候請求時發生超時。
409 Conflict
(沖突) 服務器在完成請求時發生沖突。 服務器必須在響應中包含有關沖突的信息。 服務器在響應與前一個請求相沖突的 PUT 請求時可能會返回此代碼,同時會附上兩個請求的差異列表。
410 Gone
(已刪除) 如果請求的資源已永久刪除,服務器就會返回此響應。 該代碼與 404(未找到)代碼相似,但在資源以前存在而現在不存在的情況下,有時會用來替代 404 代碼。 如果資源已永久刪除,您應當使用 301 指定資源的新位置。
411 Length Required
(需要有效長度) 服務器不接受不含有效內容長度標頭字段的請求。
412 Precondition Failed
(未滿足前提條件) 服務器未滿足請求者在請求中設置的其中一個前提條件。
413 Request Entity Too Large
(請求實體過大) 服務器無法處理請求,因為請求實體過大,超出服務器的處理能力。
414 Request-URI Too Long
(不支持的媒體類型) 請求的格式不受請求頁面的支持。
415 Unsupported Media Type
(不支持的媒體類型) 請求的格式不受請求頁面的支持。
416 Requested Range Not Satisfiable
(請求范圍不符合要求) 如果頁面無法提供請求的范圍,則服務器會返回此狀態代碼。
417 Expectation Failed
(未滿足期望值) 服務器未滿足"期望"請求標頭字段的要求。

Server Error 5xx
這些狀態代碼表示服務器在嘗試處理請求時發生內部錯誤。 這些錯誤可能是服務器本身的錯誤,而不是請求出錯
500 Internal Server Error
(服務器內部錯誤) 服務器遇到錯誤,無法完成請求。
501 Not Implemented
(尚未實施) 服務器不具備完成請求的功能。 例如,服務器無法識別請求方法時可能會返回此代碼。
502 Bad Gateway
(錯誤網關) 服務器作為網關或代理,從上游服務器收到無效響應。
503 Service Unavailable
(服務不可用) 服務器目前無法使用(由于超載或停機維護)。 通常,這只是暫時狀態。
504 Gateway Timeout
(網關超時) 服務器作為網關或代理,但是沒有及時從上游服務器收到請求。
505 HTTP Version Not Supported
(HTTP 版本不受支持) 服務器不支持請求中所用的 HTTP 協議版本。
八、錯誤處理

如果狀態碼是4xx或者5xx,就應該向用戶返回出錯信息。一般來說,返回的信息中將error作為鍵名,出錯信息作為鍵值即可。例如:

{
    error: "Invalid API key"
}
九、返回結果

針對不同操作,服務器向用戶返回的結果應該符合以下規范。

GET /collection:返回資源對象的列表(數組)
GET /collection/resource:返回單個資源對象
POST /collection:返回新生成的資源對象
PUT /collection/resource:返回完整的資源對象
PATCH /collection/resource:返回完整的資源對象
DELETE /collection/resource:返回一個空文檔
十、Hypermedia API(超媒體API)

RESTful API最好做到Hypermedia,即返回結果中提供鏈接,連向其他API方法,使得用戶不查文檔,也知道下一步應該做什么。
Hypermedia API的設計被稱為HATEOAS
仍然以github為例,訪問https://api.github.com,得到的內容如下:

{
  "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": "Not Found",
  "documentation_url": "https://developer.github.com/v3"
}

從上面的結果可以看出文檔的地址。接下來根據文檔的描述,可以利用這個api找到我的信息(羞~(@^_^@)~),訪問api.github.com/user/nicky-lau,得到以下信息:

{
  "login": "nicky-lau",
  "id": 12084271,
  "avatar_url": "https://avatars2.githubusercontent.com/u/12084271?v=3",
  "gravatar_id": "",
  "url": "https://api.github.com/users/nicky-lau",
  "html_url": "https://github.com/nicky-lau",
  "followers_url": "https://api.github.com/users/nicky-lau/followers",
  "following_url": "https://api.github.com/users/nicky-lau/following{/other_user}",
  "gists_url": "https://api.github.com/users/nicky-lau/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/nicky-lau/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/nicky-lau/subscriptions",
  "organizations_url": "https://api.github.com/users/nicky-lau/orgs",
  "repos_url": "https://api.github.com/users/nicky-lau/repos",
  "events_url": "https://api.github.com/users/nicky-lau/events{/privacy}",
  "received_events_url": "https://api.github.com/users/nicky-lau/received_events",
  "type": "User",
  "site_admin": false,
  "name": "liu zhuang",
  "company": null,
  "blog": "http://liu-zhuang.github.io",
  "location": "Shang Hai,China",
  "email": "70458055@qq.com",
  "hireable": null,
  "bio": null,
  "public_repos": 13,
  "public_gists": 0,
  "followers": 4,
  "following": 9,
  "created_at": "2015-04-23T12:36:38Z",
  "updated_at": "2016-07-21T22:56:52Z"
}
十一、其他
  • API的身份認證應該使用OAuth 2.0框架。
  • 服務器返回的數據格式,應該盡量使用JSON,避免使用XML。
實際應用

參考某MS大叔的博客,地址戳這里
demo戳這里

不過今天看到在.net core中新建webapi controller的時候,會默認生成"GET POST PUT DELETE"方法,自己要做的事情就是實現就可以了。
(家里的臺式機壞了,在macbook上裝windows系統或者虛擬機實在不能忍,等過段時間新買臺pc再把實踐代碼放出來吧)

相關
  • 調試工具:ARC
    chrome下的一款插件
    圖片描述
    又忍不住炫耀下藍燈俠(lantern)真的好用,現在媽媽再也不用擔心我不能直接在chrome應用商店下載插件了-。-
  • 相關學習內容:理解OAuth 2.0
點擊查看更多內容
15人點贊

若覺得本文不錯,就分享一下吧!

評論

相關文章推薦

正在加載中
意見反饋 幫助中心 APP下載
官方微信

舉報

0/150
提交
取消
lpl竞猜