Restful API 近年來應用越來越廣泛,各大網際網路公司紛紛推出了自己的 Restful API 服務。
本文將從實際應用出發,從 REST 到 Restful 再到 Restful API ,逐一進行介紹和分析。
REST 風格
REST 風格最早由 Roy Thomas Fielding 博士提出, REST 是一種系統架構設計風格,主要面向基於網路的軟體架構設計。這一架構風格,包含了以下一些基本要求:
客戶-伺服器
在 REST 風格中,最基本的要求就是對於一個程式來說,應當分離使用者介面和資料儲存,改善使用者介面跨平臺遷移的可移植性,同時簡化伺服器元件,改善系統的可伸縮性。
對於這一點,目前我們所接觸到的大多數應用都已經使用了這一模式,無論是瀏覽器,還是自主開發的客戶端程式,其根本的實現方式都是採用了 客戶-伺服器 模式。客戶端負責與使用者之間的互動處理,而伺服器端則實現資料儲存以及相關的業務邏輯。
同時,對於伺服器端,完整的系統大部分情況下都會包含多個不同的模組,這些模組之間的呼叫也應當遵循 客戶-伺服器 的模式,模組之間通過介面進行互相訪問。
無狀態
服務端在設計介面時,應當設計為無狀態介面。也就是說,伺服器端不儲存任何與客戶端相關的狀態上下文資訊,客戶端在每次呼叫服務端介面時,需要提供足夠的資訊,以供服務端完成操作。
在無狀態的設計中,服務端減少了儲存客戶端相關上下文資料,因此,一方面服務端能夠更加容易的實現動態擴充套件,而不至於影響客戶端使用;另一方面則減少了服務端從故障中恢復的任務量。
但無狀態也會帶來額外的問題。客戶端將需要儲存完整的使用者狀態資訊,在每次與服務端互動時可能需要增加與使用者狀態相關的上下文資訊,這樣將導致請求資料的重複和增大。
快取
根據介面的實際情況,應當在介面設計中增加快取策略,服務端可以決定是否可以快取當前返回的資料。通過此種方式,可以在一定程度上減少實際到達服務端請求,從而提高網路訪問效能。
但快取需要謹慎使用,快取哪些資料,快取過期時間都是需要根據實際情況進行設計。適當的快取可以有效的提高系統效率,但是如果設計不當,將有可能導致大量的過期資料,進而影響系統執行。
一般而言,資料字典類資料、修改頻率非常低的資料、實時性要求很低的資料等,這些資料可以設計一定的快取策略,以提高系統執行效率。
系統分層
在設計系統,尤其是大型系統,通常需要將系統按照不同的功能進行橫向和縱向的分層。例如橫向分層一般可分為互動層、服務層、資料層等,而縱向分層則通常會按照不同的業務功能對系統進行切分。經過分層後,系統將劃分為不同的模組進行獨立開發部署執行。系統分層後,不同的模組可以獨立進化,實現功能解耦,提高整個系統的可擴充套件性。
統一介面
統一介面,即是不同系統模組之間的呼叫介面統一規範,使用統一的呼叫協議,統一的資料格式等。統一介面帶來的是系統互動的規範化,介面呼叫與業務解耦,各模組獨立進化。
以上即是對於 REST 風格的解釋說明。REST 本身是一種系統架構設計風格,因此其更加偏向理論性。以下的內容將專注於實際場景中運用 REST 風格。
Restful 實現
REST 風格是面向網路的軟體架構設計風格,其針對的是基於網路的軟體架構。當軟體架構設計符合 REST 風格時,則可以描述為該設計是 Restful 的。而在實際場景中,REST 論文中描述了應用 REST 風格基本方式,即 REST (表述性狀態轉移)。
面向資源
網路上的一個實體,一個具體資訊,都可以描述為一個 資源 ,資源可以是文字、圖片、音訊、服務等具體存在。在網路中,每種資源都對應與一個 URI (統一資源識別符號)地址,通過 URI 就可以訪問到該資源。而我們通常的上網,即是對資源的各種操作。
在 Restful 架構中,所有的介面應當採用面向資源的介面設計,即對於介面的訪問地址指向其 URI 地址。
表述性
資源在網路上呈現出來的可能是多種形式,例如 HTML 、 XML 、 JSON 、圖片等等。而客戶端與伺服器之間則傳輸的是資源的這種具體表現形式。客戶端與服務端的互動,本質上就是通過這些表現形式,實現對資源的操作。
按照面向資源介面設計的要求,通常所見到的 URI 地址中,*.html / *.xml / *.json 等副檔名,其實都指向了當前資源的具體表現形式,而 URI 嚴格意義上僅指向了資源實體,並不包含具體表現形式。
狀態轉移
為了使操作資源,也即使資源發生狀態轉移,按照 REST 的要求,客戶端若想要操作服務端資源,需要通過 HTTP 協議進行操作。而在 HTTP 協議中,規定了若干用於具體操作的動詞,指向了不同的操作型別。
一般而言,對於資源的操作可以表示 CRUD 四類最基本的操作,即 增刪改查 。而 HTTP 協議中的通常用以下動詞表示這四類具體的操作:
-
GET :查詢資源操作。
-
POST :新建資源操作,也可以用於更新資源。
-
PUT :更新資源操作。
-
DELETE :刪除資源操作。
在實際應用中,客戶端與服務端之間的互動,即是建立在 HTTP 協議之上,通過面向資源的介面地址,使用 HTTP 協議動詞作為操作描述,進而實現客戶端與服務端的互動過程。
Restful API 設計
在 REST 提出多年來,當前對於 REST 風格的應用最多的即是 Restful API 。
Restful API 一般分為對外和對內。對外的 Restful API 為面向公網的公共服務介面,此類介面一般可以通過公網直接訪問,或是經過一定的安全認證後通過公網訪問。而對內的 Restful API 則主要是一整套系統內部各個子系統或模組之間互動的標準介面,相對於對外的 Restful API 介面,內部 API 介面更加標準化。
按照 REST 的要求,Restful API 的設計可以總結出以下的一些具體要求。
HTTPS + 域名
Restful API 的無狀態性,要求客戶端需要在呼叫介面時傳入足夠的資訊以供服務端用於操作指定的資源,這就有可能使得介面資料在網路傳輸過程中遭到攔截導致更多的資料洩漏。因此在提供 Restful API ,特別是對外的 API 時,應當儘可能的使用 HTTPS 協議,以確保傳輸過程的安全。
另一方面,在 API 地址中使用域名,可以進一步解耦服務端與客戶端,服務端可以更加容易的遷移和擴充套件,而不會影響服務端的使用。
例如:
https://open.domain.com/複製程式碼
實際應用過程中,使用 HTTPS 協議,更多應用與對外的 Restful API 介面,而對內網的 Restful API 來說,可以在信任內網安全的前提下,使用 HTTP 協議,以降低複雜度,提高效率。
URL 指向資源,HTTP 動詞指向操作
按照 REST 的要求,Restful API 的 URL 地址應指向具體的一個資源,例如使用者 user 。URL 中應當只包含資源名詞,不應該包含指向操作的動詞,例如新建、查詢、修改、刪除等。具體操作通過 HTTP 動詞( GET / POST / PUT / DELETE )指定。
例如,傳統的訪問地址,獲取使用者資訊:
https://open.domain.com/app/getUser複製程式碼
此時的 URL 地址指向了 獲取使用者 這一具體的操作過程,這也就是傳統的 RPC 形式。而按照 Restful API 的設計,該例項將設計為如下形式:
https://open.domain.com/app/user
// HTTP GET 請求複製程式碼
此時的 URL 地址指向了 user 這一資源實體,而通過 HTTP 協議中的 GET 動詞指定了該請求為 獲取使用者 請求。
指定 API 版本號
在設計 Restful API 時,特別是對外的 API ,通常需要考慮 API 多版本的問題,因為 API 會進行升級,而客戶端則處於不可控狀態,可能無法及時對 API 呼叫過程進行配合升級。因此,服務端需要提供對不同版本 API 的支援,同時,客戶端在呼叫 API 時也需要指定特定的版本號,以確保呼叫過程正常進行。
版本號的指定,可以在 URL 中,也可以在 HTTP 頭資訊中。
例如,在 URL 中指定版本號:
https://open.domain.com/v1/app/user複製程式碼
這種指定版本號的方式相對簡單直觀,但將導致指向統一資源的 URL 產生多個,增加了管理 URL 的成本和複雜度。
例如,在 HTTP 頭資訊中增加版本號:
https://open.domain.com/app/user
HTTP 頭:
API-Vsersion: 1
複製程式碼
以上例項中,在 HTTP 請求的頭資訊中增加了自定義欄位,用於表示 Restful API 版本。這種方式不會產生多個 URL ,但其問題是不夠直觀和簡潔,客戶端和服務端增加了區分版本號的難度,在在一些特定情況下,例如瀏覽器端的 HTTP 請求,難以對 HTTP 請求的頭資訊進行更改。
指定引數
在 Restful API 請求中,可能需要根據不同的情況進行過濾,需要增加操作引數。一般來說,針對 GET 和 DELTE 請求需要增加操作引數的情況較多,而 POST 和 PUT 更多的是通過 HTTP 報文體提供運算元據資訊。
指定引數可以通過兩種方式:URL 地址引數,? 引數。
URL 地址引數:即在 URL 之後,繼續按照 URL 格式拼接引數,服務端接到請求後,通過識別 URL 中字串的位置,獲取不同的引數。
例如:
https://open.domain.com/app/user/123456/Admin複製程式碼
複製程式碼
在以上例項中, 123456 代表使用者 ID 引數值, Admin 則代表使用者型別引數值,後臺解析該 URL 後即可根據字串位置獲取到特定的引數。
? 引數:即通過 QueryString 查詢字串的形式,拼接到 URL 之後,查詢字串的格式如下:
?key1=value1&key2=value2&...複製程式碼
例如:
https://open.domain.com/app/user?id=123456&type=Admin複製程式碼
以上例項中,服務端通過解析 ? 之後的字串獲取特定的引數。
使用 JSON 作為返回資料格式
相對於 XML 格式來說,JSON 格式更加簡潔易用,佔用資料量更小,在以網路為基礎,使用 HTTP 協議的 Restful API 中,使用 JSON 作為返回資料格式更加合理。
例如:
// JSON 格式
{"name":"user","type":"Admin"}
複製程式碼
// XML 格式
<user><name>user</name><type>Admin</type></user>複製程式碼
使用安全認證機制
在使用 Restful API ,特別是對公網開放的 Restful API,通常需要通過一定的安全認證機制來進行實現訪問控制。目前主流的方案是通過 OAuth2.0 實現安全認證。
Restful API 分析
Restful API 的興起,證明了其具備相比於傳統 RPC 介面的優勢。當同樣的,Restful API 在實際應用過程中,也存在這自己的劣勢和問題。
Restful API 優勢
Restful API 充分利用了 HTTP 協議的設計,使用面向資源的介面設計,相對於傳統 RPC 降低了介面設計的複雜度。
例如,使用傳統 RPC 形式設計針對使用者物件的 CRUD 操作:
// 新建使用者
https://open.domain.com/app/addUser
// 查詢使用者
https://open.domain.com/app/retrieveUser
// 更新使用者
https://open.domain.com/app/updateUser
// 刪除使用者
https://open.domain.com/app/deleteUser複製程式碼
在以上例項中,需要通過四個 URL 來實現 CRUD 操作。而通過 Restful API 設計,可為如下例項:
// GET: 查詢使用者;POST: 新建使用者;PUT: 更新使用者;DELETE: 刪除使用者
https://open.domain.com/app/user複製程式碼
複製程式碼
在以上例項中,通過 HTTP 動詞指定了不同的 CRUD 操作,將介面 URL 簡化為了同一個地址,僅需要改變 HTTP 動詞即可實現不同的操作。
另一方面,相對於 SOAP/XML 形式的 RPC 服務,Restful API 採用 HTTP/JSON 的形式傳遞資料,降低了傳輸資料量,同時提高了資料解析的效率,單位時間內的負載能力會高於 SOAP WebService 服務。
例如,對於 SOAP WebService 來說,基本的請求響應格式如下:
<soapenv:Envelopexmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
</soapenv:Header>
<soapenv:Body>
</soapenv:Body>
</soapenv:Envelope>複製程式碼
而對於 Restful API 來說,其請求格式符合 HTTP 協議的要求,返回格式則符合標準的 JSON 格式即可。
Restful API 劣勢
Restful API 面向資源設計介面,而對於一些複雜操作來說,介面設計難度將遠大於 RPC 形式。
例如,使用者登入驗證,使用 RPC 形式設計介面如下:
https://open.domain.com/app/checkUser複製程式碼
在以上例項中,對於 Restful API 而言,很難將該業務歸類為 HTTP 動詞中的某一種。又比如“下單”這一功能,它涉及到了訂單、使用者、支付賬戶等多個資源實體的多種操作,因此同樣也難以設計為 Restful API 。
Restful API 具備高效簡潔的特點,但這也因此造成 Restful API 沒有類似於 SOAP 協議的規範性協議,Restful API 中的資料格式、標準、安全性等都需要由開發者決定,這也就造成了無法建立統一的 Restful API 標準,作為客戶端可能需要適配多種格式的 Restful API 。
Restful API 可能遇到的問題
當 Restful API 存在多版本時,服務端需要維護多版本的介面,這會導致更多的維護成本,而且隨著 API 的不斷升級,多版本的成本將會越來越高,如何通知客戶端進行介面版本升級同樣也是可能遇到的問題。
當使用 URL 地址引數形式時,對於可選引數,服務端難以實現正確的讀取。在 API 存在可選引數的情況下,URL 地址中的引數位置是不固定的,因此服務端很難判斷引數所處的正確位置。
針對特定的業務功能,難以完全按照 Restful 要求進行設計。例如使用者驗證、訂單提交等涉及到多個資源實體和多種操作的業務流程,其一個介面中需要完成多項相關聯的操作,若使用 RPC 形式設計介面,則設計為一個統一的介面即可,而使用 Restful API 則可能出現多個介面且介面間存在呼叫依賴,這將會增加客戶端和服務端的處理難度。
Restful 本身並沒有安全性方面的標準,需要根據不同的使用場景設計 API 的安全控制方案。目前常用的安全方案即是通過 OAuth2.0 進行安全認證,但不同的 API 在安全認證機制方面也會存在一定的差別,如果設計統一完善的安全機制,也是需要考慮的問題。
Restful API 使用場景
根據 Restful API 的特定,其應用場景可以參考以下的場景:
-
資源集中型服務,例如針對使用者的資訊查詢,針對訂單的資訊查詢的等,這型別服務以資源實體為中心,操作大多為簡單的 CRUD 操作,業務邏輯簡單。
-
訪問量大,且對訪問時效要求比較高的服務。Restful API 相對於 SOAP WebService 來說,資料量更小,解析更快,在網路環境下能夠提高訪問的速度和承載能力。
-
面向公網的,且安全性要求較低的開放型 API 服務。這類服務通常由開發者向公網的所有使用者開放,Restful API 的形式能夠簡化服務呼叫過程,提高訪問效率。
對於複雜業務操作,例如保全申請提交,理賠申請提交等,使用 Restful API 形式難以進行設計,因此此類的業務可以使用傳統 RPC 的介面形式進行設計,SOAP WebService 或者 HTTP/JSON 形式的 RPC 都是可行的選擇,使用 RPC 形式反而會更加簡單。
選擇哪種方式的介面設計,需要根據實際的應用情況進行調整,沒有最好的,只有最合適的。
傳統 RPC 服務改造
在已經成型的系統當中,多數呼叫採用了 RPC 形式進行設計,並使用 SOAP WebService 作為具體的實現方式,而且開發者也已經習慣了使用了 RPC 的形式設計服務介面。在這種情況下,若想要向 Restful API 形式改造,將付出大量的改造成本。已有系統中存在錯綜複雜的呼叫邏輯,而介面也多數都是程式導向的,這種情況下改造介面,需要在介面設計上進行大幅改動才能夠實現 Restful API。同時,開發者也需要在設計開發介面時轉變思想,以面向資源的方式進行介面設計,這也是需要面臨的問題。
而對於新建系統來說,不存在歷史介面,則可以從頭按照 REST 風格進行設計,介面可設計為 Restful API,其成本要小於對已有系統的改造。
參考文章:
-
《架構風格與基於網路的軟體架構設計》 Roy Thomas Fielding 博士論文
-
《理解RESTful架構》 www.ruanyifeng.com/blog/2011/0…
-
《RESTful API 設計指南》 www.ruanyifeng.com/blog/2014/0…
-
《SOAP Webservice和RESTful Webservice》 blog.sina.com.cn/s/blog_493a…