SOAP簡單對象通路協定(Simple Object Access Protocol,SOAP)是一種基于 XML 的協定,可以和現存的許多網際網路協定和格式結合使用,包括超文本傳輸協定(HTTP),簡單郵件傳輸協定(SMTP),多用途網際郵件擴充協定(MIME),基于“通用”傳輸協定是 SOAP的一個優點。它還支援從消息系統到遠端過程調用(Remote Procedure Call,RPC)等大量的應用程式。SOAP提供了一系列的标準,如WSRM(WS-Reliable Messaging)形式化契約確定可靠性與安全性,確定異步處理與調用;WS-Security、WS-Transactions和WS-Coordination等标準提供了上下文資訊與對話狀态管理。
相對而言,SOAP協定屬于複雜的、重量級的協定,目前随着Web2.0的興起,表述性狀态轉移(Representational State Transfer,REST)逐漸成為一個流行的架構風格。REST是一種輕量級的Web Service架構風格,其實作和操作比SOAP和XML-RPC更為簡潔,可以完全通過HTTP協定實作,還可以利用緩存Cache來提高響應速度,性能、效率和易用性上都優于SOAP協定。REST架構對資源的操作包括擷取、建立、修改和删除資源的操作正好對應HTTP協定提供的GET、POST、PUT和DELETE方法,這種針對網絡應用的設計和開發方式,可以降低開發的複雜性,提高系統的可伸縮性。REST架構尤其适用于完全無狀态的CRUD(Create、Read、Update、Delete,建立、讀取、更新、删除)操作。
基于REST的軟體體系結構風格(Software Architecture Style)稱之為面向資源體系架構(Resource-oriented Architecture,ROA)。按照REST原則設計的軟體、體系結構,通常被稱為“REST式的”(RESTful),在本文中以下稱之為RESTful Web服務,以便于和基于SOAP的Web服務差別。
伺服器端采用J2EE,用戶端采用JSP、Flex、JavaFX、AIR等可以直接調用Servlet,其他的實作技術基本上不能直接調用,但是無論是那種用戶端,對于基于SOAP的Web服務或者基于RESTful Web服務務都是支援的,如AJAX的 XMLHttpRequest、Flex的HTTPService等。如下圖所示:
用戶端和伺服器端的通訊方式
HTTP 的 GET、HEAD 請求本質上應該是安全的調用,即:GET、HEAD 調用不會有任何的副作用,不會造成伺服器端狀态的改變。對于伺服器來說,用戶端對某一 URI 做 n 次的 GET、HAED 調用,其狀态與沒有做調用是一樣的,不會發生任何的改變。
HTTP 的 PUT、DELTE 調用,具有幂指相等特性 , 即:用戶端對某一 URI 做 n 次的 PUT、DELTE 調用,其效果與做一次的調用是一樣的。HTTP 的 GET、HEAD 方法也具有幂指相等特性。
HTTP 這些标準方法在原則上保證你的分布式系統具有這些特性,以幫助建構更加健壯的分布式系統。
安全控制
為了說明問題,基于上面的線上使用者管理系統,我們給定以下場景:
參考一開始我們給出的用例圖,對于用戶端 Client2,我們隻希望它能以隻讀的方式通路 User 和 User List 資源,而 Client1 具有通路所有資源的所有權限。
如何做這樣的安全控制?
通行的做法是:所有從用戶端 Client2 發出的 HTTP 請求都經過代理伺服器 (Proxy Server)。代理伺服器制定安全政策:所有經過該代理的通路 User 和 User List 資源的請求隻具有讀取權限,即:允許 GET/HEAD 操作,而像具有寫權限的 PUT/DELTE 是不被允許的。
如果對于 REST,我們看看這樣的安全政策是如何部署的。如下圖所示:
圖 4. REST 與代理伺服器 (Proxy Servers)
一般代理伺服器的實作根據 (URI, HTTP Method) 兩元組來決定 HTTP 請求的安全合法性。
當發現類似于(http://localhost:8182/v1/users/{username},DELETE)這樣的請求時,予以拒絕。
對于 SOAP,如果我們想借助于既有的代理伺服器進行安全控制,會比較尴尬,如下圖:
圖 5. SOAP 與代理伺服器 (Proxy Servers)
所有的 SOAP 消息經過代理伺服器,隻能看到(
http://localhost:8182/v1/soap/servlet/messagerouter
, HTTP POST)這樣的資訊,如果代理伺服器想知道目前的 HTTP 請求具體做的是什麼,必須對 SOAP 的消息體解碼,這樣的話,意味着要求第三方的代理伺服器需要了解目前的 SOAP 消息語義,而這種 SOAP 應用與代理伺服器之間的緊耦合關系是不合理的。
衆所周知,對于基于網絡的分布式應用,網絡傳輸是一個影響應用性能的重要因素。如何使用緩存來節省網絡傳輸帶來的開銷,這是每一個建構分布式網絡應用的開發人員必須考慮的問題。
HTTP 協定帶條件的 HTTP GET 請求 (Conditional GET) 被設計用來節省用戶端與伺服器之間網絡傳輸帶來的開銷,這也給用戶端實作 Cache 機制 ( 包括在用戶端與伺服器之間的任何代理 ) 提供了可能。HTTP 協定通過 HTTP HEADER 域:If-Modified-Since/Last- Modified,If-None-Match/ETag 實作帶條件的 GET 請求。
REST 的應用可以充分地挖掘 HTTP 協定對緩存支援的能力。當用戶端第一次發送 HTTP GET 請求給伺服器獲得内容後,該内容可能被緩存伺服器 (Cache Server) 緩存。當下一次用戶端請求同樣的資源時,緩存可以直接給出響應,而不需要請求遠端的伺服器獲得。而這一切對用戶端來說都是透明的。
圖 6. REST 與緩存伺服器 (Cache Server)
而對于 SOAP,情況又是怎樣的呢?
使用 HTTP 協定的 SOAP,由于其設計原則上并不像 REST 那樣強調與 Web 的工作方式相一緻,是以,基于 SOAP 應用很難充分發揮 HTTP 本身的緩存能力,圖 7. SOAP 與緩存伺服器 (Cache Server)
兩個因素決定了基于 SOAP 應用的緩存機制要遠比 REST 複雜:
其一、所有經過緩存伺服器的 SOAP 消息總是 HTTP POST,緩存伺服器如果不解碼 SOAP 消息體,沒法知道該 HTTP 請求是否是想從伺服器獲得資料。
其二、SOAP 消息所使用的 URI 總是指向 SOAP 的伺服器,如本文例子中的
,這并沒有表達真實的資源 URI,其結果是緩存伺服器根本不知道那個資源正在被請求,更不用談進行緩存處理。
關于連接配接性
在一個純的 SOAP 應用中,URI 本質上除了用來訓示 SOAP 伺服器外,本身沒有任何意義。與 REST 的不同的是,無法通過 URI 驅動 SOAP 方法調用。例如在我們的例子中,當我們通過
getUserList SOAP 消息獲得所有的使用者清單後,仍然無法通過既有的資訊得到某個具體的使用者資訊。唯一的方法隻有通過 WSDL 的訓示,通過調用 getUserByName 獲得,getUserList 與 getUserByName 是彼此孤立的。
而對于 REST,情況是完全不同的:通過
http://localhost:8182/v1/users
URI 獲得使用者清單,然後再通過使用者清單中所提供的 LINK 屬性,例如
<link>http://localhost:8182/v1/users/tester</link>
獲得 tester 使用者的使用者資訊。這樣的工作方式,非常類似于你在浏覽器的某個頁面上點選某個 hyperlink, 浏覽器幫你自動定向到你想通路的頁面,并不依賴任何第三方的資訊
REST 建構的系統其系統的擴充能力要強于 SOAP,這可以展現在它的統一接口抽象、代理伺服器支援、緩存伺服器支援等諸多方面, 而SOAP的成熟性可以給需要提供給多開發語言的,多傳輸方式的,對于安全性要求較高的接口設計帶來便利。