閱讀635 返回首頁    go 阿裏雲 go 技術社區[雲棲]


如何創建RESTFul Web服務

想寫這篇文章很久了,這是個大話題,不是一時半會就能說清楚的。 所以準備花個一星期整理資料,把思路理清楚,然後再在Team裏做個sharing:)

其實RESTFul是架構風格,並不是實現規範,也不一定非要用HTTP,但鑒於HTTP的普世性和 SOA的實現基本都基於HTTP實現。 這句話隻對了前一半, 實際上REST和HTTP是息息相關的,是一種Web架構,WWW是世界最大型的分布式應用,而其實現就是基於REST的web架構的設計標準,REST架構的提出者(Roy T. Fielding 2000年在他的博士論文中提出REST的架構風格),Roy 本身也是web標準(URI, HTTP, HTML) 的製定者,以及Web Server (Apache)的實現者,並且一直用REST的思想指導著標準製定,可以說HTTP就是該架構風格的一個典型應用,所以提到REST,就默認是用HTTP作為通信協議了。


一、HTTP 協議

1. Request/Response Header

2. Status Code

3. HTTP 1.1 Standards

4. 中文掃盲


二、什麼是REST

REST (REpresentational State Transfer,表述性狀態轉移) 。REST 指的是一組架構約束條件和原則。滿足這些約束條件和原則的應用程序或設計就是RESTful
REST 定義了一組體係架構原則,您可以根據這些原則設計以係統資源為中心的 Web 服務,包括使用不同語言編寫的客戶端如何通過 HTTP 處理和傳輸資源狀態。 如果考慮使用它的 Web 服務的數量,REST 近年來已經成為最主要的 Web 服務設計模式。 事實上,REST 對 Web 的影響非常大,由於其使用相當方便,已經普遍地取代了基於 SOAP 和 WSDL 的接口設計。
REST常常被視為是對傳統的RPC(CORBA)和Web Services(SOAP,WSDL)的輕量級架構實現的替代,雖然相比較而言,REST要簡單的多,但同時其功能又是足夠強大和完備的,基本上沒有什麼應用WS可以做,而RESTFul架構做不了的。其實WWW(萬維網)本身就是基於HTTP的,可以看成是RESTful的一個成功應用的典範。Web作為世界上最大,最成功的分布式係統,其背後的原理正是REST。

REST關鍵原則

  • 為所有“事物”定義ID
  • 將所有事物鏈接在一起
  • 使用標準方法
  • 資源多重表述
  • 無狀態通信
https://www.infoq.com/cn/articles/rest-introduction


三、REST開發指導

0. 為每個可被標識的資源都分配一個明顯的全局唯一的ID,使其在Web範圍內是可尋址的(Addressable)。

好處:
一、可尋址使你的服務的各個重要方麵都可以被外界訪問。 URI可以作為每個重要方麵的一個唯一標示符。而URI是可以被搜藏,可在應用中傳遞,可指代實際資源。
試想下,我在淘寶上看到一件漂亮的衣服,想分享給我的好友,如果你當前頁麵的URL不能被拷貝,粘貼,重新打開,這將是多麼糟糕的設計。

二、可尋址令別人可以使用你的服務構建mashups(混搭)應用,將其用於你意想不到的場合。例如你可以使用搜索附近犯罪率的API,和google map API, 產生一個犯罪地圖的mashup,這是很酷的一件事情。

1. 不要使用資源的物理URL,如"https://www.acme.com/inventory/product003.xml",而是使用邏輯URL如"https://www.acme.com/inventory/product/003",其具體的表述形式(Representation)可以通過HTTP的內容協商指定。

2. 使用通用的數據格式,雖然REST並沒有限定通信具體的格式,可以是普通文本,也可以是圖片、視頻等多媒體格式,但是通常的Web都是麵向人類的信息係統,其內容是人可讀的。如果我們開發的服務是給機器調用的,最好還是選用通用格式如XML、JSON,因為常見的格式處理起來非常容易,有大量的框架和庫提供支持。

3. 使用超媒體,也就是"將超媒體作為應用狀態的引擎"HATEOAS ( Hypermedia as the engine of application state) ,這個架構約束所講到的事情,在人類使用的Web上,是非常熟悉的觀念,在一個典型的電子商務解決方案中,服務器生成帶有鏈接的Web頁麵,引導用戶進入選擇商品、購買和安排發貨的這個流程。
這就是超媒體的實際引用場景,但是它並沒有局限於人類,計算機同樣能夠跟蹤由狀態機定義的協議。
例如,用GET獲取"Production List", 我們可以在Response中隻返回ProductID,讓客戶端自己組裝URL "https://www.acme.com/product/PRODUCT_ID " 去獲得額外信息,我們也可以在每一個Product Item上附上獲取詳細的URL,這樣做的好處是獲得詳細信息的動作對客戶端是透明的,即使其URL有什麼改動,客戶端代碼也可以不用修改,其次,服務器端可以根據業務擴展,向客戶端推廣新的URL,其壞處是,客戶端為了理解URL的語義,需要增加額外的代碼進行處理,從而複雜度也有所增加。
其實在Richardson的REST成熟度模型中,運用超媒體,被視為是成熟度模型的Level 3,也就是最高等級。

4. 統一接口。使用標準方法,HTTP Methods 和 標準的返回狀態碼
好處:
一、統一接口施加的約束(GET和HEAD是安全的,PUT和DELETE是冪等的)增加了HTTP的可靠性。
二、具有和現有Web通用的標準,增加了於Web的兼容性,擴大了應用範圍。

5.對瀏覽器友好,可以用瀏覽器+HTML完成所有測試,對緩存友好,利用HTTP緩存機製,提高性能和可伸縮性。例如對於GET操作,服務器可以在response的HTTP 頭部中標明可緩存內容和緩存有效時間,這樣客戶端在對同樣資源的再次請求時,就可以利用本地緩存,而不用去服務器端取數據,在提高了性能同時,也節省了網絡帶寬和服務器的負載,當然其前提條件是我們設計的REST API要符合HTTP語義,即GET方法不能改變資源的狀態,且必須是冪等的。如果在你的REST服務中,GET請求時能改變資源狀態的,使用客戶端緩存很可能就會導致數據不一致的情況,也就不符合緩存使用對於語義透明性的要求。除了客戶端緩存,從HTTP代理到服務器之間,很多地方都可以利用緩存,在服務器端也可以利用緩存,能否合理的使用緩存在某種意思上決定了我們能否提供高質量、高性能和高伸縮性分布式應用。

6.利用URL中的V1,V2做版本控製,當然也可以自定義的HTTP頭部信息標識版本,但不夠瀏覽器友好。盡量是我們的API是冪等的,可以減輕客戶端錯誤處理機製,因為是冪等的,所以客戶端用同樣的請求多次調用服務,對係統造成的影響是一致的,這一點在出錯重試時,特別有用。

7. 無狀態通信
每個HTTP請求都是自包含(self-contained)和獨立的,請求包含服務器實現該請求的全部信息,不依賴於之前的請求。
這樣做的好處是:

一、提升服務器的可伸縮性和可用性。如果一台服務器無法處理所有請求,可以進行負載均衡,加入更多的服務器,因為請求是無狀態、自包含的,所以連續兩次請求不依賴於同一台服務器,那麼加減服務器的數量對客戶端是透明的,即使集群中的一台或幾台服務器宕機了,也不會影響服務的可用性。

二、具有更高的可靠性。 若客戶端請求超時,則客戶端隻要把請求重新發一遍即可。當然這同時也需要服務具有冪等性。

參考:

A very nice article of REST:

https://rest.elkstein.org/2008/02/what-is-rest.html

How to impl REST:

https://www.infoq.com/cn/articles/RESTSOAFuture



什麼是好的RESTful API?

1. 這個API應該是對瀏覽器友好的,能夠很好地融入web,而不是與web格格不入,好的RESTful API應該能夠使用瀏覽器+HTML完成所有的測試。

2. API中所包含的資源和對資源的操作,應該是直觀和容易理解的,並且符合HTTP協議的要求。

3.API應該是鬆耦合的。(有待進一步理解

    REST的耦合度比SOAP更低,因為SOAP實現仍是基於RPC調用方式,RPC方式是緊密耦合的表現;而緊密耦合的係統是無法適應Web級的規模可伸縮性的。REST Web服務則繼承了Web鬆散耦合的特點,客戶應用通過邏輯URL訪問服務,服務的實現對客戶來說是完全透明的,客戶程序可以對服務的實現技術、方法毫無所知。

4.API中所有的格式應該是常見的通用格式,常見的有HTML、XML、JSON,常見的格式處理起來非常容易,有大量的框架和庫提供支持。

5. 使用HTTP狀態碼來表達各種出錯情況。如果一個所謂的”RESTful API“對任何請求都返回200 OK的響應,在響應的消息體中返回出錯情況信息,這種做法顯然不符合”確保操作語義的可見性“這個REST架構風格的基本要求。

6. 這個API應該對HTTP緩存是友好的

HTTP協議是個分層的架構,從兩端的User agent(瀏覽器)到Origin server(Apache 服務器)之間,可以插入很多中間組件。在整個HTTP通信鏈條的很多位置,都可以設置緩存 。最常見的是瀏覽器緩存和服務器緩存:

瀏覽器端,下載一個類似HTTP watch的瀏覽器插件,就能很容易觀察到對一個網站的進行第二次訪問時,很多信息如圖片,JS,JSS都是從瀏覽器緩存中取得,而不是去服務器取,節省了很多網絡開銷,也減輕了服務器壓力。

在服務器端,也有很多緩存技術,可以參見這篇文章Caching your REST API來了解一種典型的服務器緩存技術,大概意思就是在對資源執行安全的操作如GET時,將response放入緩存,當對資源進行不安全操作如PUT/DELETE,將該response從緩存中移除(PURGE)出去,這樣保證客戶端不會得到過期的response。

在使用緩存時,不管是在客戶端還是服務器端,一個非常重要的原則就是就語義透明性(Semantic Transparency)。

什麼是緩存的語義透明性?就是不管在客戶端還是服務器端使用緩存,對一個請求來說,其使用緩存得到的響應(Response)和直接訪問服務器(Origin Server)所得到響應結果,應該是一模一樣的,沒有任何差別,緩存對服務的訪問者來說應該是透明的。 

完全達到語義透明性隻是一個理想狀態,Web是鬆耦合的,所以弱一致性(Weak consistency) 是基於Web的分布式應用的特點,但是我們可以通過一些手段來增強一致性,盡量達到語義透明性。常用的方式有失效、驗證、過期。


如何對RESTful API進行版本控製?

一個比較簡單實用的做法是直接在URI中插入版本號,這樣做允許多個版本的API並行運行。另一個做法是在HTTP請求中加入自定義頭信息,標明使用的版本號。不過這個做法其實對瀏覽器不夠友好,簡單地使用瀏覽器+HTML無法測試。

還有對於微小改動,最好可以做到向後兼容而不改變版本號,最關鍵的就是在擴展時不能破壞現有的客戶端,例如,變更一個參數,可以選擇同時兼容新舊兩種輸入,或者保持老參數不動,提供一個新參數,在文檔中必須做出說明,不推薦新用戶繼續使用之前的參數。


HTTP1.1規範中給出的動詞對於設計RESTful API 夠用嗎?

如果資源抽象做的很好,對於某個資源的任何操作,通常都能夠映射到CRUD四個類別中。CRUD四個類別對於操作資源來說,絕大多數情況下是完備的。HTTP的四個方法對CRUD的映射關係是Create-POST, Retrieve-GET, Update-PUT, Delete-DELETE。如果在資源上定義的操作過多,往往這個資源可以拆分出更多的資源。


關於API的冪等性

冪等性是係統接口對外部調用的承諾,承諾隻要調用接口成功,外部多次調用對係統影響是一致的。冪等性的好處是,外部在調用係統失敗後,在進行重試時,無需更改請求內容。

在大規模分布式係統中,完成一個功能,需要很多服務或者組件的協作,通常我們會用一個uniqueRequestId or dedupeId 來標識一次請求的唯一性,而在調用服務出錯時,如網絡超時,係統內部錯誤,一般都要進行重試,冪等的接口允許重試時,使用相同的dedupeId。而非冪等的接口可能需要更改dedupeId來和上一次請求做區分,增加了客戶端使用服務的負擔。

所以在我看來,不僅GET/DELETE需要時冪等的,所有的REST API都應該盡量做到冪等,包括POST/PUT。比如在eLedger係統裏,創建journal entry(流水賬)是最常用的API,如果客戶POST兩次一模一樣的請求(最主要是dedupeId一樣),eLedger每次都會返回成功的結果,隻不過在第二個POST到來時,係統檢測到流水已經被創建,並不會創建新的流水,隻是將已創建的結果返回給客戶端,從某種意義上,減輕了客戶使用eLedger服務的難度,因為其不用擔心因為重複POST所造成的數據不一致。


關於REST的安全性

REST對安全性並沒有像基於WSDL,SOAP的Web Service的WS-Security的安全規範,主要取決於REST service自己的實現,因為REST是架設在HTTP之上的,所以在底層通信(TCP)使用SSL是自然而然的,SSL基本可以解決中間人(man in middle) 和 重放攻擊(replay attack)等安全問題,那麼對於應用層,所要解決的就是認證(authentication),授權(authorization),訪問(access)的問題了,鑒於可擴展性和開放性的考慮,使用OAuth是一個比較好的選擇,首先OAuth是W3C的標準規範,提供了統一的標準和規範,降低了客戶端的學習成本,其次是它把真個驗證流程劃分為認證、授權、訪問三個步驟,不僅能滿足傳統的Client-Server驗證,還具備非常良好的可擴展性,使得地方


如何定義Resource

REST開發又被稱作“麵向資源的開發”,這說明對於資源的抽象,是設計RESTful API的核心內容。RESTful API建模的過程與麵向對象建模類似,是以名詞為核心的。這些名詞就是資源,任何可命名的抽象概念都可以定義為一個資源。而HTTP協議並不是一種傳輸協議,它實際提供了一個操作資源的統一接口。利用有限的幾個方法(GET/POST/PUT/DELETE)來達到資源在不同表述狀態(REpresentationalState)之間的轉移(Transfer),這也是REST名稱的由來。 所以RESTful API建模的過程,可以看作是具有統一接口約束的麵向對象建模過程。

如果發現資源上的操作過多,以至於HTTP的方法不夠用,應該考慮設計出更多的資源。設計出更多資源(以及相應的URI)對於RESTful API來說並沒有什麼害處。

五、REST 的成熟度模型(Maturity Model)

Level 0 零級服務

單個的URI,單個的HTTP方法(通常是POST)。例如大多數基於Web Service的服務使用單個URI來標識端點(endpoint),別且使用HTTP POST來轉移基於SOAP的載荷,完全忽略了HTTP動詞的其餘部分。

Level 1 一級服務

使用了多個URI,但是隻使用了單個HTTP動詞。這種基本的服務和零級之間的關鍵區別在於,這種級別的服務暴露出了很多邏輯上的資源

Level 2 二級服務

使用了大量的可通過URI尋址的資源。這樣的服務支持多個HTTP動詞來暴露資源。包含在這個級別的是CRUD(Create Read Update Delete)服務.

Level 3 三級服務

最Web感知(Web-aware)的服務級別,支持超媒體作為應用狀態的引擎的觀念。即,表述包含了消費者可能感興趣的到其他資源的URI連接,這種服務通過追蹤資源來引導消費者,結果是引起應用狀態的遷移。


https://martinfowler.com/articles/richardsonMaturityModel.html


最後更新:2017-04-03 16:49:04

  上一篇:go 數組的冒泡排序及拷貝
  下一篇:go 企業級API設計