原文位址:http://www.ibm.com/developerworks/cn/webservices/ws-whichwsdl/#listing9
use屬性可以為literal,encoded;style可以為rpc,document,我們來對五種類型的style/use決定的binding作一比較,分别是rpc/literal, document/literal none-wrapper, document/literal with wrapper, rpc/encoded, 以及document/encoded。
從生成soap消息的角度看
rpc和document的差别在于方法的操作名是否出現在生成的Soap消息中。
literal和encoded編碼方式的差别在于參數類型是否出現在生成的Soap消息中。
rpc/encoded能夠完整地表示一個方法調用,但性能很差,而且不能校驗Soap消息的有效性。
rpc/literal不對參數類型進行編碼,但仍然無法對Soap消息進行校驗。
document/encoded沒有意義,因為沒有方法名,對參數類型編碼沒有什麼意義。
document/literal none-wrapper無法生成操作名,适用于完整的文檔作為參數傳遞給方法的場景。
document/literal with wrapper應該是目前使用最多的方式。
WSDL 綁定樣式可以是 RPC 樣式或文檔樣式。用法可以是編碼的,也可以是文字的。您如何決定使用哪一種樣式/用法的組合呢?本文将幫助您解決這個問題。
Web 服務是通過 WSDL 文檔來描述的。WSDL 綁定描述了如何把服務綁定到消息傳遞協定(特别是 SOAP 消息傳遞協定)。WSDL SOAP 綁定可以是 RPC 樣式的綁定,也可以是文檔樣式的綁定。同樣,SOAP 綁定可以有編碼的用法,也可以有文字的用法。這給我們提供了四種樣式/用法模型:RPC/編碼、RPC/文字、文檔/編碼、文檔/文字
除了這些樣式之外,還有一種樣式也很常見,它稱為文檔/文字包裝的樣式,算上這一種,在建立 WSDL 檔案時您就有了五種綁定樣式可以從中選擇。您應該選擇哪一種呢?
對于本文的讨論,讓我們從 清單1中的 Java 方法開始,并且對其應用 JAX-RPC Java-to-WSDL 規則。清單 1. Java 方法
public void myMethod(int x); |
RPC/編碼
采用清單1中的方法并且使用您喜歡的 Java-to-WSDL 工具來運作它,指定您想讓它生成 RPC/編碼的 WSDL。您最後應該得到如 清單2所示的 WSDL 片斷。
清單 2. 用于 myMethod 的 RPC/編碼的 WSDL
<message name="myMethodRequest"> <part name="x" type="xsd:int"/> </message> <message name="empty"/> <portType name="PT"> <operation name="myMethod"> <input message="myMethodRequest"/> <output message="empty"/> </operation> </portType> <binding .../> <!-- I won't bother with the details, just assume it's RPC/encoded. --> |
現在用“5”作為參數 x 的值來調用此方法。我們将發送一個與 清單3類似的 SOAP 消息。
清單 3. 用于 myMethod 的 RPC/編碼的 SOAP 消息
<soap:envelope> <soap:body> <myMethod> <x xsi:type="xsd:int">5</x> </myMethod> </soap:body> </soap:envelope> |
對于這個 RPC/編碼的示例中的 WSDL 和 SOAP 消息,有許多需要注意的事項:
為了簡單起見,在本文的大部分 XML 示例中,我省略了名稱空間和字首。不過,我還是使用了少數字首,您可以假定它們是用下列名稱空間進行定義的。
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
優點
WSDL 基本達到了盡可能地簡單易懂的要求。
操作名出現在消息中,這樣接收者就可以很輕松地把消息發送到方法的實作。
缺點
類型編碼資訊(比如 xsi:type="xsd:int" )通常就是降低吞吐量性能的開銷。
您不能簡單地檢驗此消息的有效性,因為隻有 <x xsi:type="xsd:int">5</x> 行包含在 Schema 中定義的内容;其餘的 soap:body 内容都來自 WSDL 定義。
有沒有一種方法能夠保留這些優點而消除其中的缺點呢?或許有。讓我們來看一看 RPC/文字的樣式。
RPC/文字
用于我們的方法的 RPC/文字的 WSDL 看起來與 RPC/編碼的 WSDL 幾乎一樣(請參見清單4)。隻是綁定的用法由編碼改為文字。僅此而已。
清單 4. 用于 myMethod 的 RPC/文字的 WSDL
<message name="myMethodRequest"> <part name="x" type="xsd:int"/> </message> <message name="empty"/> <portType name="PT"> <operation name="myMethod"> <input message="myMethodRequest"/> <output message="empty"/> </operation> </portType> <binding .../> <!-- I won't bother with the details, just assume it's RPC/ literal. --> |
RPC/文字的 SOAP 消息又是怎樣的呢(請參見 清單 5)?這裡的更改要多一點。去掉了類型編碼。
清單 5. 用于 myMethod 的RPC/文字的 SOAP 消息
<soap:envelope> <soap:body> <myMethod> <x>5</x> </myMethod> </soap:body> </soap:envelope> |
下面是這種方法的優點和缺點:
優點
WSDL 還是基本達到了盡可能地簡單易懂的要求。
操作名仍然出現在消息中。
去掉了類型編碼。
缺點
您仍然不能簡單地檢驗此消息的有效性,因為隻有 <x xsi:type="xsd:int">5</x> 行包含在 Schema 中定義的内容;其餘的 soap:body 内容都來自 WSDL 定義。
文檔樣式如何呢?它們能夠幫助克服這些困難嗎?
我不知道有誰懂得這種方法的真正含義。我也不知道這種方法的任何實作。它将可能從 WSDL 的後續版本中消失。是以我們還是讨論别的吧。
文檔/文字的 WSDL
文檔/文字的 WSDL對 RPC/文字的 WSDL 作了一些更改。它們之間的不同之處顯示在 清單6中。
清單6. 用于 myMethod 的文檔/文字的 WSDL
<types> <schema> <element name="xElement" type="xsd:int"/> </schema> </types> <message name="myMethodRequest"> <part name="x" element="xElement"/> </message> <message name="empty"/> <portType name="PT"> <operation name="myMethod"> <input message="myMethodRequest"/> <output message="empty"/> </operation> </portType> <binding .../> <!-- I won't bother with the details, just assume it's document/literal. --> |
而現在的 SOAP 應該如 清單 7所示:
清單7. 用于 myMethod 的文檔/文字的 SOAP 消息
<soap:envelope> <soap:body> <xElement>5</xElement> </soap:body> </soap:envelope> |
關于消息組成部分的注意事項
我本來可以隻更改綁定,就像我從 RPC/編碼轉到 RPC/所做的那樣。它将是合法的 WSDL。然而,WS-I 基本概要(WS-I Basic Profile)規定文檔/文字的消息的組成部分引用元素而不是類型,是以我遵循了 WS-I(并且此處使用元素部分可以很好地把我們帶到關于文檔/文字包裝的樣式的讨論)。
下面是這種方法的優點和缺點:
優點
沒有編碼資訊
您可以在最後用任何 XML 檢驗器檢驗此消息的有效性。 soap:body ( <xElement>5</xElement> )中每項内容都定義在 Schema 中。
缺點
WSDL 變得有些複雜。不過,這是一個非常小的缺點,因為 WSDL 并沒有打算由人來讀取。
SOAP 消息中缺少操作名。而如果沒有操作名,發送就可能比較困難,并且有時變得不可能。
文檔/文字的樣式看起來似乎隻是重新安排了 RPC/文字的模型的優點和缺點。您可以檢驗消息的有效性,但是您失去了操作名。有沒有一種方法可以改進這一點呢?有的。它就是
文檔/文字包裝的樣式
在我說明文檔/文字包裝的樣式的含義之前,讓我給您展示 清單 8和 清單9中的 WSDL 和 SOAP 消息。
清單8. 用于 myMethod 的文檔/文字包裝的 WSDL
<types> <schema> <element name="myMethod"/> <complexType> <sequence> <element name="x" type="xsd:int"/> </sequence> </complexType> </element> </schema> </types> <message name="myMethodRequest"> <part name="parameters" element="myMethod"/> </message> <message name="empty"/> <portType name="PT"> <operation name="myMethod"> <input message="myMethodRequest"/> <output message="empty"/> </operation> </portType> <binding .../> <!-- I won't bother with the details, just assume it's document/literal. --> |
WSDL Schema 現在把參數放在包裝中(請參見 清單9)。
清單:9. 用于 myMethod 的文檔/文字包裝的 SOAP 消息
<soap:envelope> <soap:body> <myMethod> <x>5</x> </myMethod> </soap:body> </soap:envelope> |
注意到此 SOAP 消息看起來非常類似于 RPC/文字的 SOAP 消息。您可能會說,它看起來與 RPC/文字的 SOAP 消息是完全一樣的,不過,這兩種消息之間存在着微妙的差別。在 RPC/文字的 SOAP 消息中, <soap:body> 的 <myMethod> 子句是操作的名稱。在文檔/文字包裝的 SOAP 消息中, <myMethod> 子句是單個輸入消息的組成部分引用的元素的名稱。是以,包裝的樣式具有這樣的一個特征,輸入元素的名稱與操作的名稱是相同的。此樣式是把操作名放入 SOAP 消息的一種巧妙方式。
文檔/文字包裝的樣式的特征有:
輸入消息隻有一個組成部分;該部分就是一個元素;該元素有與操作相同的名稱;該元素的複雜類型沒有屬性;
下面是這種方法的優點和缺點:
優點
沒有編碼資訊。
出現在 soap:body 中的每項内容都是由 Schema 定義的,是以您現在可以很容易地檢驗此消息的有效性。
方法名又出現在 SOAP 消息中。
缺點
WSDL 甚至更複雜,但是這仍然是一個非常小的缺點。
如您所見,文檔/文字包裝的樣式還是有一些缺點,不過與優點比起來,它們都顯得無足輕重。
RPC/文字包裝的樣式?
從 WSDL 的角度來看,沒有理由隻是把把包裝的樣式和文檔/文字綁定聯系在一起。它可以很容易地應用于 RPC/文字綁定。但是這樣做是相當不明智的。SOAP 将包含操作的一個 myMethod 元素和元素名稱的子 myMethod 元素。另外,即使它是一個合法的 WSDL,RPC/文字元素部分也不遵循 WS-I。
文檔/文字的樣式在哪裡定義?
這種包裝的樣式來源于 Microsoft。沒有定義這種樣式的規範;是以雖然這種樣式是一個好的東西,但不幸的是,為了與 Microsoft 和其他公司的實作進行互操作,現在惟一的選擇就是根據 Microsoft WSDL 的輸出來猜測它是如何工作的。文檔/文字包裝的樣式也實作在 IBM WebSphere SDK for Web Services 中。在這個示例中,樣式是相當明顯的;但是也存在個别情況,在這些情況中,由于缺少定義而導緻需要操作的适當事項不夠特别清晰。我們希望看到的最理想的情況就是将來能有像 Web 服務互操作組織(Web Services Interoperability Organization)這樣的獨立團體來幫助對此進行穩定化和标準化。
為什麼不始終采用文檔/文字包裝的樣式
至此,本文已經給了您這樣的一個印象,文檔/文字包裝的樣式是最好的方法。而實際的情況往往确實如此。不過,仍然存在着一些情況,在這些情況下,您最好是換一種别的樣式。
采用文檔/文字非包裝的樣式的理由
如果您已經重載了操作,就不能采用文檔/文字包裝的樣式。
想象一下,除了我們一直在使用的方法之外,還有另一種方法,請參見 清單10。
清單10. 用于文檔/文字包裝的問題方法
public void myMethod(int x); public void myMethod(int x, String y); |
WSDL 允許重載的操作。但是當您添加包裝的樣式到WSDL 時,需要元素有與操作相同的名稱,并且在 XML 中不能有兩個名稱相同的元素。是以您必須采用文檔/文字非包裝的樣式或某種 RPC 樣式。
采用 RPC/文字的樣式的理由
由于文檔/文字非包裝的樣式沒有提供操作名,是以在有些情況下,您将需要采用某種 RPC 樣式。比如說 清單11中的一組方法。
清單11. 用于文檔/文字非包裝的樣式的問題方法
public void myMethod(int x); public void myMethod(int x, String y); public void someOtherMethod(int x); |
現在假定您的伺服器接收到文檔/文字的 SOAP 消息(您可以回過頭在 清單 7中看一看它)。伺服器應該發送哪一種方法呢?所有您能确切知道的就是,它一定不是 myMethod(int x, String x) ,因為消息隻有一個參數,而這種方法需要兩個參數。它可能是其他兩種方法中的一種。采用文檔/文字的樣式,您沒有辦法知道是哪一種方法。
假定伺服器接收到一個 RPC/文字的消息(比如 清單5中的),而不是文檔/文字的消息。對于這種消息,伺服器很容易決定把它發送到哪一種方法。您知道操作名是 myMethod,并且也知道隻有一個參數,是以它必定是 myMethod(int x) 。
采用 RPC/編碼的理由有很多。
其中兩個主要的原因是:
資料圖形
多态性
資料圖形
設想您有一個二進制樹,其中的節點定義在 清單12中。
清單12. 二進制樹節點 Schema
<complexType name="Node"> <sequence> <element name="name" type="xsd:string"/> <element name="left" type="Node" xsd:nillable="true"/> <element name="right" type="Node" xsd:nillable="true"/> </sequence> </complexType> |
根據這種節點定義,我們可以構造一個樹形結構,它的根節點 A 通過它左邊和右邊的的連結可以指向節點 B(請參見 圖1)。
發送資料圖形的标準方式是使用 href 标記,它是 RPC/編碼的樣式( 清單13)的一部分。
清單:13. RPC/編碼的二進制樹
<A> <name>A</name> <left href="12345" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" /> <right href="12345" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" /> </A> <B id="12345"> <name>B</name> <left xsi:nil="true"/> <right xsi:nil="true"/> </B> |
在任何文字的樣式中,href 屬性都是不可用的,這樣圖形連結就不再起作用了( 清單14和 圖2)。您仍然有一個根節點 A,它從左邊指向一個節點 B,從右邊指向另一個節點 B。這兩個 B 節點是等同的,但它們不是相同的節點。是複制了資料而不是引用了兩次資料。
14. 文字二進制樹
<A> <name>A</name> <left> <name>B</name> <left xsi:nil="true"/> <right xsi:nil="true"/> </left> <right> <name>B</name> <left xsi:nil="true"/> <right xsi:nil="true"/> </right> </A> |
在文字樣式中,您可以通過各種方法構造圖形,但是卻沒有标準的方法;是以您做的任何事情很可能不能與網絡中其他端點上的服務進行互操作。
多态性
看一看 清單15中使用多态性 Schema 的 WSDL。
清單15. 一個多态性 WSDL 的示例
<types> <schema> <complexType name="animal"> <sequence> <element name="name" type="xsd:string"/> </sequence> </complexType> <complexType name="dog"> <complexContent mixed="false"> <extension base="animal"> <sequence> <element name="breed" type="xsd:string"/> </sequence> </extension> </complexContent> </complexType> </schema> </types> <message name="in"> <part name="trainee" type="animal"/> </message> <message name="empty"/> <portType name="AnimalTrainer"> <operation name="train"> <input message="in"/> <output message="empty"/> </operation> </portType> |
當您把一個 dog 的執行個體傳送給 train 操作時,所生成的 SOAP 消息必須包含類型編碼資訊,這樣接收終端才能知道它所接收的是 animal 的哪一個擴充(請參見 清單16)。這種類型編碼資訊可用在 RPC/編碼的樣式中。
清單16. 一個多态性 SOAP 消息
<soap:envelope> <soap:body> <train> <trainee xsi:type="Dog"> <name>Bob</name> <breed>Bloodhound</breed> </trainee> </train> </soap:body> </soap:envelope> |
總結
有四種綁定樣式(其實真正有五種,不過文檔/編碼的樣式沒有什麼意義)。雖然每種樣式都有自己的用武之地,但是在大多數情況下,最好的樣式是文檔/文字包裝的樣式。