HTTP-based RESTful API
1. REST介紹
Some Concepts
在理解 REST
或者 RESTful
架構之前,可以先了解一些概念:
-
Resources
: 可以認為是網路上的任何可識別的事物,比如HTML,影象,視訊,歌曲,服務等等。我們通常意義上的“上網”其實就是通過URI
去訪問、操作Resource
。The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. “today’s weather in Los Angeles”), a collection of other resources, a non-virtual object (e.g. a person), and so on. In other words, any concept that might be the target of an author’s hypertext reference must fit within the definition of a resource. A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.
— Roy Fielding
-
URI
(Uniform Resource Identifier): 統一資源識別符號, 是明確標識特定Resource
的字串 。也就是說我們需要通過URI
才能和特定的Resource
互動。URL
是URI
的一種。 -
Resource Representation
: 一項資源在任意特定時間戳上的狀態(state
)。Representation
通常由資料,描述資料的元資料和能夠幫助客戶端過渡到下一個期望狀態的超媒體連結組成。 這裡的資料並不一定就是資源本身,可能是資源的一種表現形式,具體是哪種形式可以通過元資料獲知。 -
Status Transfer
Resource
互動的過程中,勢必會牽扯到資料狀態的變化,簡單點來說就是資料的增刪改查。
What's REST
REST 是 Representational State Transfer 的首字母縮寫,由 Roy Thomas Fielding 在他2000年的博士論文中提出。
所以簡單從字面上理解 Representational State Transfer 的話,就是“與 Representation
有關或以 Representation
為特徵的狀態轉化”,或者說“基於 Representation
的 Resource
狀態轉化”
REST 實際上 Fielding 定義的是一種架構風格,這種風格體現在六種
-
Client-Server architecture
Client-Server 約束背後的原理是 Separation of Concerns。Client 和 Server 之前不應該有任何依賴,Client只需要知道所要互動的 Resource 的 URI。
-
Statelessness
Client 的每次 Request 對 Server 來說都是“全新”的,Server 不會儲存任何 Client context。
-
Cacheability
Response 必須隱式或顯式地宣告自己是否可快取。
-
Layered system
REST允許使用分層的系統架構,例如,可以在伺服器A上部署API,在伺服器B上儲存資料,在伺服器C中進行身份驗證,而這些對 Client 是“隱形”的。
-
Code on demand (optional)
Server 可以通過傳輸可執行程式碼來臨時擴充套件或定製Client的功能。例如JavaScript之類的客戶端指令碼。
-
Uniform interface
Uniform interface 是設計 RESTful 架構的基礎,包含以下四個原則,
-
Resource identification in requests
在請求中標識每個資源,比如使用URI。 並且 Resource 本身與返回給客戶端的 Representation 是分開的。 例如,Server 可以從 Database 中將資料以HTML,XML或JSON的格式傳送,但這些格式都不是Resource 在伺服器的內部表示形式。
-
Resource manipulation through representations
當 Client 持有 Resource 的 Representation(包括附加的任何元資料)時,它具有足夠的資訊來進行”State Transfer“。
-
Self-descriptive messages
每個訊息都包含足夠的資訊來描述如何處理該訊息。例如,可以通過
media type
指定要如何解析訊息。 -
Hypermedia as the engine of application state (HATEOAS)
當訪問了一個REST API的初始URI之後, Client 應該能夠動態使用 Server 提供的連結來發現其所需的所有可用 Resource。 隨著訪問的進行,Server 將以文字作為響應,該文字包括指向當前可用其他 Resource 的超連結。
-
如果一個API框架滿足以上六種約束,那麼我們就說它是RESTful的API,或者說是RESTful架構的API。
簡單的說,在REST架構風格中,資料和功能被視為 Resource
,並且可以使用 URI
進行訪問。 通過使用一組簡單的,定義明確的操作對 Resource
進行操作。 Client 和 Server 通過使用標準化的介面和協議來交換 Resource Representation
。
2. HTTP-based RESTful API
REST vs HTTP
沒有可比性,REST是一種架構風格,而HTTP是一種通訊協議。
Fielding 並未在他的論文中指示應該如何具體實現 REST,但目前通常都是基於 HTTP 協議。
如果說我們實現了一個 RESTful 風格的 API,這個 API 是基於 HTTP 協議的,我們可以說它是 HTTP-based RESTful API 。
Some Conventions
假設我們有個 API 的專用域名
https://api.example.com
REST Resource Naming Guide
儘可能的使用一致的資源命名約定和URI格式,以最小化和最大可讀性和可維護性。
假設在我們的業務邏輯中存在以下實體:
- Employee
- Task
- Report
URI中應該儘量使用能夠代表 Resource 的名詞,而不是使用動詞。
URI中應該首選小寫字母
使用名詞複數表示集合,比如:
GET /employees
使用集合配合 identifier 獲取單個例項
GET /tasks/{taskId}
使用 /
表示層級關係,並且不要在URI尾部使用 /
GET /employees/{employeeId}/tasks
GET /employees/{employeeId}/tasks/{taskId}
使用連字元 -
分割單詞可以使URI提高可讀性,也更利於SEO,但不要使用下劃線 _
GET /employees/{employeeId}/reports/current-month-report
不要帶有副檔名, 依賴於通過 Content-Type
傳達的媒體型別
GET /employees/{employeeId}/reports/current-month-report HTTP/1.1
content-type: application/pdf;
使用查詢引數過濾集合
GET /employees/{employeeId}/tasks?top=10&skip=20
GET /employees/{employeeId}/tasks?status=completed
Http Method
四種常用的 Http Method
Http Method | CRUD | |
---|---|---|
GET | Read | 返回查詢的資源 |
POST | Create | 返回新建的資源 |
PUT | Update/Replace | 返回更新的資源 |
DELETE | DELETE | 返回空 |
比如:
GET /employees/{emplpyeeId}
POST /tasks HTTP/1.1
{
"Name":"New Task"
}
Status Code
常用的 Status Code:
Status Code | Description | |
---|---|---|
200 OK | [GET] | 伺服器成功返回使用者請求的資料 |
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] | 使用者請求的格式不可得 |
410 Gone | [GET] | 使用者請求的資源被永久刪除 |
422 Unprocesable entity | [POST/PUT/PATCH] | 當建立一個物件時,發生一個驗證錯誤 |
500 INTERNAL SERVER ERROR | [*] | 伺服器發生錯誤 |
Version Control
-
URI 方式
GET {version}/employees
-
自定義 Header 方式
GET /employees HTTP/1.1 accept-version: v1
Error Handle
HTTP/1.1 404 NOT FOUND
{
"error": "Can't not found."
}
HATEOAS
從 API 的入口可以獲取針對所有資源的操作,列如
GET https://api.example.com
HTTP/1.1 200 OK
{
"links":[{
"rel":"collection",
"href":"/emplpyees",
"title":"list of employee",
"type":"application/json"
}]
}
rel
: 當前上下文和目標是如何關聯的
href
: 目標資源的URI
title
type
: 一些屬性
參考 RFC5988
以上都是“約定”而不是“守則”,RESTful API 的具體實現應該是要基於專案的實際情況,沒有必要學究式的生搬硬套。
Richardson Maturity Model
-
Level 0
使用一種通訊協議,通常是 Http, 向一個特定的 Endpoint 傳送一個請求,該請求中包含了所有的細節。並且Request 和 Response 的格式可以是任何格式。比如:
檢視一個 Employee 正在做的 Task 列表
POST /GetEmplopyeeTasks HTTP/1.1 { "employeeId":1, "status":"processing" }
將會得到的 Response:
HTTP/1.1 200 OK { tasks:[{ "id":1, "status":"processing" },{ "id":2, "status":"processing" }] }
接下來可以根據這些資訊去結束一個 Task:
POST /CompleteTask HTTP/1.1 { "taskId":1 }
如果一切正常,可能會得到一個 Response:
HTTP/1.1 200 OK { "status":"success" }
如果這個 Task 早就被完成了,可能會得到下面 Response
HTTP/1.1 200 OK { "status":"error", "reason":"task has been completed" }
到目前為止,這都是非常直觀的基於RPC風格的系統。它是簡單的,因為只有Plain Old XML(POX)在這個過程中被傳輸。如果你使用SOAP或者XML-RPC,原理上也是基本相同的 。
-
Level 1 - Resources
通往 REST 風格的第一步是引入 Resource 的概念,將通過約定的 URI 命名規範來和 Resource 互動。 不是通過傳入引數而呼叫一個函式,而是通過呼叫某個特定物件上的某個服務,同時將其它資訊作為引數傳入到該服務中 。 比如之前的 Request 可以變成:
POST /employee/1/tasks HTTP/1.1 { "status":"processing" }
POST /task/1/completion HTTP/1.1
-
Level 2 - HTTP Verb
儘可能根據HTTP協議定義的那樣來合理使用HTTP Verb 。
GET /employee/1/tasks?status=processing HTTP/1.1
POST /task/1/completion HTTP/1.1
DELETE /task/1 HTTP/1.1
並且在 Response 中使用正確的 StatusCode, 比如
HTTP/1.1 409 Conflict { "error":"task has been completed" }
-
Level 3 - Hypermedia Controls
使用 HATEOAS
RMM的用處在於它提供了一個層層遞進的思考RESTful背後本質思想的方法。正因為如此,應該將它視為一個工具來幫助我們學習概念,而不是作為某種評估機制。這一切的理論都只是為我們設計 API 框架時提供一些指導意見和思考方向,並不是必須遵守的”規定“。