監(jiān)理公司管理系統(tǒng) | 工程企業(yè)管理系統(tǒng) | OA系統(tǒng) | ERP系統(tǒng) | 造價咨詢管理系統(tǒng) | 工程設計管理系統(tǒng) | 甲方項目管理系統(tǒng) | 簽約案例 | 客戶案例 | 在線試用
X 關閉

Web服務說明語言 (WSDL) 淺釋

申請免費試用、咨詢電話:400-8352-114

AMTeam.org

Web服務說明語言 (WSDL) 淺釋


Carlos C. Tapang

Infotects

2001年7月

摘要:使用 WSDL,用戶能夠以真正獨立于語言和平臺的方式,自動生成 Web 服務代理。

為什么采用 WSDL?

Internet 協(xié)議這樣的標準,是由某個機構強加給人們的嗎?還是因為人們認識到,遵循這些標準帶來的好處遠遠超過付出的代價而主動接受?有很多標準提出之后最終都沒有獲得成功。有時候,即使通過法律或政府規(guī)定來強制執(zhí)行,這些標準也沒有得到推廣,Ada 編程語言就是一個例子。

我認為,一項標準被廣泛接受一定是因為遵循它能帶來好處。以鐵路服務為例,不同公司修建的鐵軌要能夠相互銜接,或者來自不同公司的產品要能夠配合使用才行得通。有一些廠商已經聯(lián)合起來,要將 SOAP 變成一種標準。Web 服務說明語言 (WSDL) 為 Web 服務提供商和用戶提供了一種方便的協(xié)作方式,使 SOAP 的好處更加明顯。使各個公司修建的鐵軌能夠銜接很簡單:這些公司只需約定兩根鐵軌之間的距離即可。而對于 Web 服務則要復雜得多。首先,我們必須約定用于指定接口的標準格式。

有反對意見認為,SOAP 并不真正需要接口說明語言。如果 SOAP 是僅為傳輸內容而制定的標準,那么它需要一種語言來說明這些內容。SOAP 消息確實攜帶類型信息,所以 SOAP 允許動態(tài)決定類型。但是,除非知道函數(shù)的名稱以及參數(shù)的數(shù)量和類型,否則我們并不能正確地調用函數(shù)。沒有 WSDL,您可以根據(jù)文檔(這必須有才行)或者通過檢查線路消息來判斷調用語法。這兩種方式都涉及人工操作,因此這個過程就有可能出錯。而使用 WSDL,我能夠以真正獨立于語言和平臺的方式,為 Web 服務自動生成代理。就像 IDL 文件對于 COM 和 CORBA 一樣,WSDL 文件是客戶端和服務器之間的合約。

請注意,盡管 WSDL 被設計成可以表達與非 SOAP 協(xié)議的綁定,我們這里主要考慮的是其與 HTTP 上的 SOAP 相關的情況。另外,雖然 SOAP 現(xiàn)在主要用于進行遠程過程或函數(shù)調用,但是 WSDL 還允許以 SOAP 傳輸文檔規(guī)范。WSDL 1.1 已經作為草案提交到 W3C。請參閱 http://www.w3.org/TR/wsdl.html(英文)。

WSDL 文檔結構

使用模塊圖有助于理解 XML 文檔。下圖通過顯示組成 WSDL 文檔(是 XML 文檔)的五個小節(jié)之間的關系,展示了 WSDL 的結構。

WSDL 文檔的節(jié)可分成兩組。上層的組包含抽象定義,而下層的組包含具體說明。抽象各節(jié)以獨立于語言和平臺的方式定義 SOAP 消息,不包含特定計算機或語言專用的元素。這有助于定義一套各種網站都能實現(xiàn)的服務。與具體站點相關的問題(例如序列化)被放到下層各節(jié),這些節(jié)包含具體的說明。

抽象定義

類型

獨立于計算機和語言的類型定義。

消息

包含函數(shù)參數(shù)(輸入與輸出分開)或文檔說明。

端口類型

引用消息節(jié)中的消息定義來說明函數(shù)簽名(操作名稱、輸出參數(shù)和輸出參數(shù)等)。

具體說明

綁定

指定端口類型節(jié)中每個操作的綁定。

服務

指定每個綁定的端口地址。

在下圖中,箭頭連接符表示文檔各節(jié)之間的關系。圓點和箭頭連接符表示“引用”或“使用”關系。雙箭頭連接符表示“修飾”關系。三維箭頭連接符表示“包含”關系??梢钥闯?,消息節(jié)使用類型節(jié)中的定義,端口類型節(jié)使用消息節(jié)中的定義,綁定節(jié)引用端口類型節(jié),而服務節(jié)引用綁定節(jié)。端口類型和綁定節(jié)包含操作元素,而服務節(jié)包含端口元素。端口節(jié)中的操作元素由綁定節(jié)中的操作元素修飾或說明。

在本文中,我將使用標準的 XML 術語來說明 WSDL 文檔。“元素”指一個 XML 元素,而“屬性”指元素的屬性。形式如下:

<元素 屬性="屬性值">內容</元素>

“內容”可以由一個或多個元素以遞歸方式組成。根元素是最上層的元素,文檔中所有其他元素都從屬于它。子元素始終從屬于另一個元素(稱為“父元素”)。

請注意,可以只有一個或根本就沒有類型節(jié)。所有其他各節(jié)可以有零個、一個或多個父元素。例如,消息節(jié)可以有零個或多個 <message> 元素。WSDL 架構要求所有各節(jié)以規(guī)定的順序出現(xiàn):輸出、類型、消息、端口類型、綁定和服務。每個抽象節(jié)都可以放在獨立的文件中,并被導入到主文檔中。

圖 1:抽象定義和具體定義

WSDL 文件示例

讓我們來觀察一個 WSDL 文件示例,了解其結構和原理。請注意,這是一個非常簡單的 WSDL 文檔實例。這里我們的目的僅僅是用它來說明最重要的特性。后面各節(jié)將有更詳細的討論。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions name="FooSample"
targetNamespace="
http://tempuri.org/wsdl/"
xmlns:wsdlns="
http://tempuri.org/wsdl/"
xmlns:typens="
http://tempuri.org/xsd"
xmlns:xsd="
http://www.w3.org/2001/XMLSchema"
xmlns:soap="
http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:stk="
http://schemas.microsoft.com/soap-toolkit/wsdl-extension"
xmlns="
http://schemas.xmlsoap.org/wsdl/">

<types>
<schema targetNamespace="
http://tempuri.org/xsd"  
xmlns="
http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="
http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="
http://schemas.xmlsoap.org/wsdl/"
elementFormDefault="qualified" >
</schema>
</types>

<message name="Simple.foo">
<part name="arg" type="xsd:int"/>
</message>

<message name="Simple.fooResponse">
<part name="result" type="xsd:int"/>
</message>

<portType name="SimplePortType">
<operation name="foo" parameterOrder="arg" >
<input message="wsdlns:Simple.foo"/>
<output message="wsdlns:Simple.fooResponse"/>
</operation>
</portType>

<binding name="SimpleBinding" type="wsdlns:SimplePortType">
<stk:binding preferredEncoding="UTF-8" />
<soap:binding style="rpc"
         transport="
http://schemas.xmlsoap.org/soap/http"/>
<operation name="foo">
<soap:operation
soapAction="
http://tempuri.org/action/Simple.foo"/>
<input>
<soap:body use="encoded" namespace="
http://tempuri.org/message/"
encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/" />
</input>
<output>
<soap:body use="encoded" namespace="
http://tempuri.org/message/"
encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/" />
</output>
</operation>
</binding>

<service name="FOOSAMPLEService">
<port name="SimplePort" binding="wsdlns:SimpleBinding">
<soap:address location="
http://carlos:8080/FooSample/FooSample.asp"/>
</port>
</service>
</definitions>

下面是示例文檔的要點。稍后我會詳細討論每一節(jié)。

第一行聲明該文檔是 XML。雖然這不是必需的,但它可以幫助 XML 語法分析程序判斷是要分析整個 WSDL 文件還是產生一個錯誤信號。第二行是 WSDL 文檔的根元素:<definitions>。根元素附加了一些名稱空間屬性(名稱空間聲明),<types> 元素的 <schema> 子元素也附加了一些名稱空間屬性。

<types> 元素包含類型節(jié)。如果不需要聲明數(shù)據(jù)類型,可以省略這一節(jié)。在示例 WSDL 中,沒有聲明應用程序專用的類型,但我還是使用了類型節(jié),這只是為了聲明文檔中使用的架構名稱空間。

<message> 元素包含消息節(jié)。如果我們把操作看作函數(shù),那么 <message> 元素就定義了該函數(shù)的參數(shù)。<message> 元素中的每個 <part> 子元素都對應一個參數(shù)。輸入的參數(shù)在一個單獨的 <message> 元素中定義,與輸出元素分開,輸出元素在自己的 <message> 元素中定義。輸入和輸出參數(shù)在輸入和輸出 <message> 元素中都有各自對應的 <part> 元素。輸出 <message> 元素的名稱按約定以“Response”結尾,例如“fooResponse”。每個 <part> 元素都有 name 和 type 屬性,就象每個函數(shù)參數(shù)都有名稱和類型一樣。

當用于文檔交換時,WSDL 允許使用 <message> 元素來說明要交換的文檔。

<part> 元素的類型可以是 XSD 基本類型、SOAP 定義類型 (soapenc)、WSDL 定義類型 (wsdl) 或類型節(jié)定義類型。

端口類型節(jié)中可以有零個、一個或多個 <portType> 元素。因為抽象的端口類型定義可以放在獨立的文件中,所以 WSDL 文件中可能有零個 <portType> 元素。上例只顯示了一個 <portType> 元素。正如您所看到的,<portType> 元素用 <operation> 子元素來定義一個或多個操作。示例只顯示了一個 <operation> 元素,名為“foo”。這個名稱相當于函數(shù)名。<operation> 元素可以有一個、兩個或三個子元素:<input>、<output> 和 <fault> 元素。每個 <input> 和 <output> 元素中的 message 屬性引用消息節(jié)中相關的 <message> 元素。這樣,示例中的整個 <portType> 元素相當于下面的 C 函數(shù)聲明:

int foo(int arg);

這個示例表明,與 C 相比,XML 是多么冗長。(包括 <message> 元素在內,示例用了 12 行 XML 來表達這個在 C 中僅一行的函數(shù)聲明。)

綁定節(jié)可以有零個、一個或多個 <binding> 元素。其目的是指定如何通過線路發(fā)送每個 <operation> 的調用和響應。服務節(jié)也可以有零個、一個或多個 <service> 元素。它包含 <port> 元素。每個 <port> 元素都引用綁定節(jié)中的一個 <binding> 元素。綁定和服務節(jié)都包含對 WSDL 文檔的具體說明。

名稱空間

在根元素 <definitions> 和子元素 <schema> 中都有名稱空間屬性:

<definitions name="FooSample"
targetNamespace="
http://tempuri.org/wsdl/"
xmlns:wsdlns="
http://tempuri.org/wsdl/"
xmlns:typens="
http://tempuri.org/xsd"
xmlns:xsd="
http://www.w3.org/2001/XMLSchema"
xmlns:soap="
http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:stk="
http://schemas.microsoft.com/soap-toolkit/wsdl-extension"
xmlns="
http://schemas.xmlsoap.org/wsdl/">

<types>
<schema targetNamespace="
http://tempuri.org/xsd"  
xmlns="
http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="
http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="
http://schemas.xmlsoap.org/wsdl/"
elementFormDefault="qualified" >
</schema>
</types>

每個名稱空間屬性為文檔中使用的每個名稱空間都聲明了一個簡寫。在實例“xmlns:xsd”中,為名稱空間 http://www.w3.org/2001/XMLSchema(英文)定義了一個簡寫 (xsd)。這樣,在文檔的后面部分只需要給名稱加上前綴(或“限定”)“xsd:”,就可以引用這個名稱空間,例如限定的類型名稱“xsd:int”。標準的作用域規(guī)則適用于這個簡寫前綴。即,在某個元素中定義的前綴只在該元素中有效。

名稱空間的作用是什么?名稱空間是為避免命名沖突而設立的。假設我建立了一個 Web 服務,其 WSDL 文件包含一個名為“foo”的元素,而您要將我的 Web 服務與另一個補充的服務結合使用。如果沒有名稱空間,那么其他 Web 服務就不能在它們的 WSDL 文件中使用名稱“foo”。僅當在兩個實例中它們都是指同一個對象,兩個服務才能使用相同的名稱。而在兩個不同的名稱空間中,我的 Web 服務“foo”與其他 Web 服務“foo”指的是不同的對象。在您的客戶端中,可以通過限定來引用我的“foo”。例如,如果我將 carlos 聲明為 http://www.infotects.com/fooService 的簡寫,則完全限定名 http://www.infotects.com/fooService#foo 等價于“carlos:foo”。請注意,名稱空間中使用 URI 來保證其唯一性并允許在文檔中使用定位符。由 URI 指向的位置不一定對應真實的 Web 位置。GUID 也可以用來代替或補充 URI。例如,GUID“335DB901-D44A-11D4-A96E-0080AD76435D”是一個有效的名稱空間指示符。

targetNamespace 屬性聲明了一個名稱空間,在該元素中聲明的所有名稱都從屬于該名稱空間。在 WSDL 文件示例中,<definitions> 的 targetNamespace 是 http://tempuri.org/wsdl。這意味著在此 WSDL 文檔中聲明的所有名稱都從屬于此名稱空間。<schema> 有自己的 targetNamespace 屬性,其值為 http://tempuri.org/xsd。因此,在此 <schema> 元素中定義的所有名稱都從屬于此名稱空間,而不是主目標名稱空間。

<schema> 元素中的下一行聲明了一個默認的名稱空間。架構中所有未限定的名稱都屬于此名稱空間。

xmlns="http://www.w3.org/2001/XMLSchema"

SOAP 消息

看待 WSDL 文件的方式之一是:對于使用該文件的客戶端和服務器而言,它決定在線路上傳輸什么內容。雖然 SOAP 使用了底層協(xié)議(例如 IP 和 HTTP),但是應用程序決定了它在特定的客戶端和服務器之間使用的高層協(xié)議。換句話說,給定一個操作(例如僅簡單地讀取并返回一個輸入的整數(shù)的“echoInt”),那么參數(shù)的數(shù)量、每個參數(shù)的類型以及這些參數(shù)通過線路(序列化)傳輸?shù)姆绞骄蜆嫵闪藨贸绦驅S玫膮f(xié)議。這種協(xié)議可以按各種方式來指定,我相信最好的方式就是使用 WSDL。這樣看來,WSDL 不僅是一個“接口合約”,而且還是一個協(xié)議規(guī)范語言。如果我們需要從 IP 和 HTTP 等“固定”的協(xié)議發(fā)展到應用程序專用的協(xié)議,這正是我們所需要的。

WSDL 可以指定 SOAP 消息是遵循 rpc 還是遵循文檔樣式。rpc 樣式的消息(如示例中所用的)類似于有零個或更多參數(shù)的函數(shù)調用。文檔樣式的消息較平整,并且它需要的嵌套層次更少。下面的 XML 消息作為使用 MS SOAP Toolkit 2.0 (MSTK2) 中的 SoapClient 對象分析 WSDL 文件示例的結果被發(fā)送和接收。

從客戶端發(fā)送時使用函數(shù)調用“foo(5131953)”:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="
http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<m:foo xmlns:m="
http://tempuri.org/message/">
<arg>5131953</arg>
</m:foo>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

從服務器接收(響應):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="
http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAPSDK1:fooResponse xmlns:SOAPSDK1="
http://tempuri.org/message/">
<result>5131953</result>
</SOAPSDK1:fooResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

函數(shù)調用消息及其響應都是有效的 XML。SOAP 消息包含 <Envelope> 元素,而 <Envelope> 元素包含可選的 <Header> 元素和至少一個 <Body> 元素。發(fā)送和接收的消息在主 <Envelope> 元素中都有一個 <Body> 元素。rpc 函數(shù)調用消息正文有一個以操作名稱“foo”命名的元素,而響應正文有一個“fooResponse”元素。foo 元素有一個 <arg> 部分。<arg> 是一個參數(shù),如示例 WSDL 所說明。fooResponse 同樣有一個 <result> 部分。請注意 encodingStyle、envelope 和 message 名稱空間在 WSDL 的綁定節(jié)中的規(guī)定方式。如下所示。

<binding name="SimpleBinding" type="wsdlns:SimplePortType">
     <stk:binding preferredEncoding="UTF-8" />
        <soap:binding style="rpc"
                  transport="
http://schemas.xmlsoap.org/soap/http"/>
        <operation name="foo">
           <soap:operation
              soapAction="
http://tempuri.org/action/Simple.foo"/>
              <input>
                   <soap:body use="encoded"
                        namespace="
http://tempuri.org/message/"
                        encodingStyle=
                              "
http://schemas.xmlsoap.org/soap/encoding/" />
              </input>
              <output>
                   <soap:body use="encoded"
                        namespace="
http://tempuri.org/message/"
                        encodingStyle=
                              "
http://schemas.xmlsoap.org/soap/encoding/" />
              </output>
        </operation>
      </binding>

WSDL 類型和消息節(jié)中的 XML 架構

WSDL 數(shù)據(jù)類型基于 W3C 推薦的“XML 架構:數(shù)據(jù)類型”(XSD)。此文檔有三個不同的版本(1999、2000/10 和 2001),應該在 <definitions> 元素中的名稱空間之一中聲明,指定在 WSDL 文件中使用哪個版本:

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

本文中我們只考慮 2001 版。WSDL 標準的支持者強烈建議使用 2001 版。

在本節(jié)和后面各節(jié)中,使用了以下前綴或名稱空間簡寫:

前綴 等價的名稱空間 說明
soapenc
http://schemas.xmlsoap.org/soap/encoding SOAP 1.1 編碼
wsdl
http://schemas.xmlsoap.org/wsdl/soap WSDL 1.1
xsd
http://www.w3.org/2001/XMLSchema XML 架構

XSD 基本類型

下表摘自 MSTK2 文檔,列舉了 MSTK2 支持的全部 XSD 基本類型。它說明了客戶端和服務器端的 WSDL 閱讀器如何將 XSD 類型映射到 variant 類型以及 VB、C++ 和 IDL 中對應的類型。

XSD (Soap) 類型 variant 類型 VB C++ IDL 注釋 anyURI VT_BSTR String BSTR BSTR   base64Binary VT_ARRAY | VT_UI1 Byte() SAFEARRAY SAFEARRAY(unsigned char)   boolean VT_BOOL Boolean VARIANT_BOOL VARIANT_BOOL   byte VT_I2 Integer short short 轉換時驗證范圍 date VT_DATE Date DATE DATE 時間設置為 oo:oo:oo dateTime VT_DATE Date DATE DATE   double VT_R8 Double double double   duration VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 ENTITIES VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 ENTITY VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 float VT_R4 Single float float   gDay VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 gMonth VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 gMonthDay VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 gYear VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 gYearMonth VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 ID VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 IDREF VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 IDREFS VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 int VT_I4 Long long long   integer VT_DECIMAL Variant DECIMAL DECIMAL 轉換時驗證范圍 language VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 long VT_DECIMAL Variant DECIMAL DECIMAL 轉換時驗證范圍 Name VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 NCName VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 negativeInteger VT_DECIMAL Variant DECIMAL DECIMAL 轉換時驗證范圍 NMTOKEN VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 NMTOKENS VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 nonNegativeInteger VT_DECIMAL Variant DECIMAL DECIMAL 轉換時驗證范圍 nonPositiveInteger VT_DECIMAL Variant DECIMAL DECIMAL 轉換時驗證范圍 normalizedString VT_BSTR String BSTR BSTR   NOTATION VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 number VT_DECIMAL Variant DECIMAL DECIMAL   positiveInteger VT_DECIMAL Variant DECIMAL DECIMAL 轉換時驗證范圍 QName VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 short VT_I2 Integer short short   string VT_BSTR String BSTR BSTR   time VT_DATE Date DATE DATE 日期設置為 1899 年 12 月 30 日 token VT_BSTR String BSTR BSTR 不執(zhí)行轉換或驗證 unsignedByte VT_UI1 Byte unsigned char unsigned char   unsignedInt VT_DECIMAL Variant DECIMAL DECIMAL 轉換時驗證范圍 unsignedLong VT_DECIMAL Variant DECIMAL DECIMAL 轉換時驗證范圍 unsignedShort VT_UI4 Long Long Long 轉換時驗證范圍

XSD 定義了兩組內置的數(shù)據(jù)類型:原始型和導出型。在 http://www.w3.org/TR/2001/PR-xmlschema-2-20010330(英文)中查看內置數(shù)據(jù)類型的層次結構會有所幫助。

復雜類型

XML 架構允許定義復雜類型。在 C 中,復雜類型為結構。例如,要定義與以下 C 結構等價的數(shù)據(jù)類型:

typedef  struct {
   string  firstName;
   string  lastName;
   long    ageInYears;
   float    weightInLbs;
   float    heightInInches;
} PERSON;

我們可以在 XML 架構中寫入:

<xsd:complexType name="PERSON">
   <xsd:sequence>
      <xsd:element name="firstName" type="xsd:string"/>
      <xsd:element name="lastName" type="xsd:string"/>
      <xsd:element name="ageInYears" type="xsd:int"/>
      <xsd:element name="weightInLbs" type="xsd:float"/>
      <xsd:element name="heightInInches" type="xsd:float"/>
   </xsd:sequence>
</xsd:complexType>

但是,<complexType> 能表達的不僅是結構的等價類型。它可以有除 <sequence> 以外的其他子元素。我可以使用 <all> 而不是 <sequence>:

<xsd:complexType name="PERSON">
   <xsd:all>
      <xsd:element name="firstName" type="xsd:string"/>
      <xsd:element name="lastName" type="xsd:string"/>
      <xsd:element name="ageInYears" type="xsd:int"/>
      <xsd:element name="weightInLbs" type="xsd:float"/>
      <xsd:element name="heightInInches" type="xsd:float"/>
   </xsd:all>
</xsd:complexType>

這意味著成員變量 <element> 可以以任何順序出現(xiàn),并且每一個都是可選的。這與 C 結構不完全一樣。

請注意,示例中使用了內置數(shù)據(jù)類型 string、int、float。C 中的 string 與 XML 中的 string 相同,float 也相同。但是 C 中的 long 是 XML 中的 int(如上表所示)。

在 WSDL 文件中,復雜類型(如上面所示的類型)在類型節(jié)中聲明。例如,我可以按以下方式聲明 PERSON 類型,并在消息節(jié)中使用:

<?xml version="1.0" encoding="UTF-8" ?>
<definitions ?>
<types>
     <schema targetNamespace="someNamespace"
            xmlns:typens="someNamespace" >
     <xsd:complexType name="PERSON">
        <xsd:sequence>
           <xsd:element name="firstName" type="xsd:string"/>
           <xsd:element name="lastName" type="xsd:string"/>
           <xsd:element name="ageInYears" type="xsd:int"/>
           <xsd:element name="weightInLbs" type="xsd:float"/>
           <xsd:element name="heightInInches" type="xsd:float"/>
        </xsd:sequence>
     </xsd:complexType>
     </schema>
</types>

<message name="addPerson">
     <part name="person" type="typens:PERSON"/>
</message>

<message name="addPersonResponse">
     <part name="result" type="xsd:int"/>
</message>

</definitions>

在上例中,第一個消息名為“addPerson”,它有一個類型為“PERSON”的 <part>。PERSON 類型在類型節(jié)中聲明為復雜類型。

如果初始化 MSTK2 SoapClient 時,我們在完整的 WSDL 文件中使用上述片段,將能夠成功分析該文件。但是,不能發(fā)送對 <addPerson> 的函數(shù)調用。因為 SoapClient 本身不知道如何處理復雜類型,所以需要使用自定義類型映射程序來處理復雜類型。MSTK2 文檔有一個示例應用程序,其中包含了自定義類型映射程序。

還有另外一種方式,可以將 <part> 元素與類型聲明相關聯(lián)。這種方式使用 element 屬性,而不是 type 屬性。在下面的另一個示例中,我在類型節(jié)中聲明了兩個元素(“Person”和“Gender”),然后用 element 屬性在“addPerson”<message> 中引用它們。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions ?>
<types>
     <schema targetNamespace="someNamespace"
            xmlns:typens="someNamespace" >
     <element name="Person">
     <xsd:complexType>
        <xsd:sequence>
           <xsd:element name="firstName" type="xsd:string"/>
           <xsd:element name="lastName" type="xsd:string"/>
           <xsd:element name="ageInYears" type="xsd:int"/>
           <xsd:element name="weightInLbs" type="xsd:float"/>
           <xsd:element name="heightInInches" type="xsd:float"/>
        </xsd:sequence>
     </xsd:complexType>
     </element>
     <element name="Gender">
     <xsd:simpleType>
        <xsd:restriction base="xsd:string">
           <xsd:enumeration value="Male" />
           <xsd:enumeration value="Female" />
        </xsd:restriction>
     </xsd:simpleType>
     </element>
     </schema>
</types>

<message name="addPerson">
     <part name="who" element="typens:Person"/>
     <part name="sex" element="typens:Gender"/>
</message>

<message name="addPersonResponse">
     <part name="result" type="xsd:int"/>
</message>
</definitions>

類型節(jié)的 Gender <element> 中嵌入了一個匿名枚舉類型,這種類型可能的取值有“Male”和“Female”。我在“addPerson”<message> 中使用 element 屬性而不是 type 屬性來引用該元素。

在將特定的類型與 <part> 相關聯(lián)時,“element”和“type”之間有什么區(qū)別呢?使用“type”屬性,我們可以說明一個能夠使用多種類型的部分(類似 variant);而在使用“element”屬性時我們不能這樣做。下面的示例說明了這一點。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions ?>
<types>
     <schema targetNamespace="someNamespace"
            xmlns:typens="someNamespace">
     <xsd:complexType name="PERSON">
        <xsd:sequence>
           <xsd:element name="firstName" type="xsd:string"/>
           <xsd:element name="lastName" type="xsd:string"/>
           <xsd:element name="ageInYears" type="xsd:int"/>
           <xsd:element name="weightInLbs" type="xsd:float"/>
           <xsd:element name="heightInInches" type="xsd:float"/>
        </xsd:sequence>
     </xsd:complexType>
     <xsd:complexType name="femalePerson">
        <xsd:complexContent>
           <xsd:extension base="typens:PERSON" >
           <xsd:element name="favoriteLipstick" type="xsd:string" />
           </xsd:extension>
        </xsd:complexContent>
     </xsd:complexType>
     <xsd:complexType name="malePerson">
        <xsd:complexContent>
           <xsd:extension base="typens:PERSON" >
           <xsd:element name="favoriteShavingLotion" type="xsd:string" />
           </xsd:extension>
        </xsd:complexContent>
     </xsd:complexType>
     <xsd:complexType name="maleOrFemalePerson">
        <xsd:choice>
           <xsd:element name="fArg" type="typens:femalePerson" >
           <xsd:element name="mArg" type="typens:malePerson" />
        </xsd:choice>
     </xsd:complexType>
     </schema>
</types>

<message name="addPerson">
     <part name="person" type="typens:maleOrFemalePerson"/>
</message>

<message name="addPersonResponse">
     <part name="result" type="xsd:int"/>
</message>

</definitions>

上例還說明了擴展導出的方式?!癴emalePerson”和“malePerson”都是從“PERSON”導出的。每個都有一個額外的元素:“femalePerson”有“favoriteLipstick”,“malePerson”有“favoriteShavingLotion”。使用 <choice> 構造將兩個導出的類型合并到一個復雜類型“maleOrFemalePerson”中。最后,在“addPerson”<message> 中,由“person”<part> 引用合并的類型。然后,這個 <part> 或參數(shù)就可以是“femalePerson”或“malePerson”。

數(shù)組

XSD 提供了 <list> 構造來聲明一個項目用空格分隔的數(shù)組。但是,SOAP 不使用 XSD 列表來編碼數(shù)組。而是為數(shù)組定義了自己的類型“SOAP-ENC:Array”。下例顯示了如何從這個類型導出,為一維的整數(shù)數(shù)組聲明一個類型:

<xsd:complexType name="ArrayOfInt">
   <xsd:complexContent>
      <xsd:restriction base="soapenc:Array">
         <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:int[]"/>
      </xsd:restriction>
   </xsd:complexContent>
</xsd:complexType>

通過使用限制導出的方式從 soapenc:Array 導出,來聲明一個新的復雜類型。然后,為該復雜類型聲明一個屬性 arrayType。對“soapenc:arrayType”的引用實際上通過以下方式聲明了 arrayType 屬性:

<xsd:attribute name="arrayType" type="xsd:string"/>

然后,wsdl:arrayType 屬性值決定了數(shù)組中每個成員的類型。數(shù)組項目也可以是復雜類型。

<xsd:complexType name="ArrayOfPERSON">
   <xsd:complexContent>
      <xsd:restriction base="soapenc:Array">
         <attribute ref="soapenc:arrayType"
               wsdl:arrayType="typens:PERSON[]"/>
      </xsd:restriction>
   </xsd:complexContent>
</xsd:complexType>

WSDL 要求數(shù)組的類型名稱必須是“ArrayOf”加上數(shù)組中項目的類型。這樣,僅從名稱就可以知道“ArrayOfPERSON”是 PERSON 結構的數(shù)組。下面,我使用 ArrayOfPERSON 來聲明一個 <message>,以添加多個 PERSON 而不僅僅是一個 PERSON。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions ?>
<types>
     <schema targetNamespace="someNamespace"
            xmlns:typens="someNamespace" >
     <xsd:complexType name="PERSON">
        <xsd:sequence>
           <xsd:element name="firstName" type="xsd:string"/>
           <xsd:element name="lastName" type="xsd:string"/>
           <xsd:element name="ageInYears" type="xsd:int"/>
           <xsd:element name="weightInLbs" type="xsd:float"/>
           <xsd:element name="heightInInches" type="xsd:float"/>
        </xsd:sequence>
     </xsd:complexType>
     <xsd:complexType name="ArrayOfPERSON">
        <xsd:complexContent>
           <xsd:restriction base="soapenc:Array">
              <attribute ref="soapenc:arrayType"
           wsdl:arrayType="typens:PERSON[]"/>
           </xsd:restriction>
        </xsd:complexContent>
     </xsd:complexType>
     </schema>
</types>

<message name="addPersons">
     <part name="person" type="typens:ArrayOfPERSON"/>
</message>

<message name="addPersonResponse">
     <part name="result" type="xsd:int"/>
</message>

</definitions>

<portType> 和 <operation> 元素

portType 抽象定義了一系列操作。portType 中的 operation 元素定義了調用 portType 中所有方法的語法。每個 operation 元素都聲明了方法的名稱、參數(shù)(使用 <message> 元素)以及每個參數(shù)的類型(每個 <message> 中聲明的 <part> 元素)。

一個 WSDL 文檔中可以有多個 <portType> 元素。每個 <portType> 元素組合了一系列相關的操作,這與 COM 接口組合方法類似。

在一個 <operation> 元素中,最多可以有一個 <output> 元素、一個 <output> 元素和一個 <fault> 元素。這三個元素中的每一個都有 name 和 message 屬性。

<output>、<output> 和 <fault> 元素中的 name 屬性的作用是什么呢?它可以用來區(qū)分兩個名稱相同的操作(重載)。例如,考慮以下兩個名稱相同而參數(shù)不同的 C 函數(shù):

void foo(int arg);
void foo(string arg);

這種重載可以在 WSDL 中表示如下:

<?xml version="1.0" encoding="UTF-8" ?>
<definitions name="fooDescription"
     targetNamespace="
http://tempuri.org/wsdl/"
     xmlns:wsdlns="
http://tempuri.org/wsdl/"
     xmlns:typens="
http://tempuri.org/xsd"
     xmlns:xsd="
http://www.w3.org/2001/XMLSchema"
     xmlns:soap="
http://schemas.xmlsoap.org/wsdl/soap/"
     xmlns:stk="
http://schemas.microsoft.com/soap-toolkit/wsdl-
            extension"
     xmlns="
http://schemas.xmlsoap.org/wsdl/">
<types>
     <schema targetNamespace="
http://tempuri.org/xsd"
              xmlns="
http://www.w3.org/2001/XMLSchema"
              xmlns:SOAP-ENC="
http://schemas.xmlsoap.org/soap/encoding/"
                    xmlns:wsdl="
http://schemas.xmlsoap.org/wsdl/"
           elementFormDefault="qualified" >
</schema>
</types>

<message name="foo1">
     <part name="arg" type="xsd:int"/>
</message>

<message name="foo2">
     <part name="arg" type="xsd:string"/>
</message>

<portType name="fooSamplePortType">
     <operation name="foo" parameterOrder="arg " >
          <input name="foo1" message="wsdlns:foo1"/>
     </operation>
     <operation name="foo" parameterOrder="arg " >
          <input name="foo2" message="wsdlns:foo2"/>
     </operation>
</portType>

<binding name="fooSampleBinding" type="wsdlns:fooSamplePortType">
<stk:binding preferredEncoding="UTF-8" />
     <soap:binding style="rpc"
            transport="
http://schemas.xmlsoap.org/soap/http"/>
     <operation name="foo">
     <soap:operation soapAction="
http://tempuri.org/action/foo1"/>
          <input name="foo1">
     <soap:body use="encoded" namespace="
http://tempuri.org/message/
      encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/" />
     </input>
</operation>
<operation name="foo">
     <soap:operation soapAction="
http://tempuri.org/action/foo2"/>
          <input name="foo2">
               <soap:body use="encoded"
               namespace="
http://tempuri.org/message/
               encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/"
               />
          </input>
</operation>
</binding>

     <service name="FOOService">
          <port name="fooSamplePort" binding="fooSampleBinding">
               <soap:address
                  location="
http://carlos:8080/fooService/foo.asp"/>
          </port>
     </service>
</definitions>

直到我撰寫這篇文章時,還沒有哪種 SOAP 實現(xiàn)能夠重載操作名稱。對基于 Java 的客戶端,這很重要。因為基于 Java 的服務器使用的接口利用了 Java 的重載特性。而對基于 COM 的客戶端,這并不重要,因為 COM 不支持重載。

<binding> 和 <operation> 元素
在綁定節(jié)中完全指定協(xié)議、序列化和編碼。類型、消息和端口類型節(jié)處理抽象的數(shù)據(jù)內容,而綁定節(jié)處理數(shù)據(jù)傳輸?shù)奈锢砑毠?jié)。綁定節(jié)將前三節(jié)中的抽象內容具體化。

綁定規(guī)范與數(shù)據(jù)和消息聲明的分離意味著參與同種業(yè)務的服務提供商可以將一組操作標準化 (portType)。然后,每個提供商可以通過提供不同的自定義綁定來彼此區(qū)分。WSDL 同樣有一個導入構造,這樣抽象定義就能夠放到自己的文件中,而與綁定和服務節(jié)分開。這些文件可以在服務提供商之間分發(fā),并把抽象定義用作標準。例如,多家銀行可以將一組銀行業(yè)務操作標準化,在抽象 WSDL 文檔中進行準確說明。然后,每個銀行就可以自由地“自定義”底層協(xié)議、序列化優(yōu)化和編碼。

下面是重載示例 WSDL 的綁定節(jié),在此重復是為了進行更詳細的討論:

<binding name="fooSampleBinding" type="wsdlns:fooSamplePortType">
     <stk:binding preferredEncoding="UTF-8" />
     <soap:binding style="rpc"
            transport="
http://schemas.xmlsoap.org/soap/http"/>
     <operation name="foo">
          <soap:operation soapAction="
http://tempuri.org/action/foo1"/>
               <input name="foo1">
                    <soap:body use="encoded"
                         namespace="
http://tempuri.org/message/
                         encodingStyle=
                        "
http://schemas.xmlsoap.org/soap/encoding/" />
               </input>
     </operation>
     <operation name="foo">
          <soap:operation soapAction="
http://tempuri.org/action/foo2"/>
               <input name="foo2">
                    <soap:body use="encoded"
                         namespace="
http://tempuri.org/message/
                         encodingStyle=
                        "
http://schemas.xmlsoap.org/soap/encoding/" />
               </input>
     </operation>
</binding>

<binding> 元素被給定一個名稱(在本例中是“fooSampleBinding”),使服務節(jié)中的 <port> 元素能夠引用它。它有一個“type”屬性引用 <portType>,在本例中是“wsdlns:fooSamplePortType”。第二行是 MSTK2 擴展元素 <stk:binding>,它指定了 preferredEncoding“UTF-8”。

<soap:binding> 元素指定了所使用的樣式 (rpc) 和傳送方式。attribute 屬性引用了指定使用 HTTP SOAP 協(xié)議的名稱空間。

有兩個 <operation> 元素具有相同的名稱“foo”。區(qū)分這兩個操作的是兩個不同的 <input> 名稱:“foo1”和“foo2”。兩個 <operation> 元素中的 <soap:operation> 元素有相同的“soapAction”屬性(是 URI)。soapAction 屬性是在 SOAP 消息中逐字使用的 SOAP 專用 URI。產生的 SOAP 消息具有 SOAPAction 標題,<soap:operation> 元素中的 URI 成為其值。soapAction 屬性是 HTTP 綁定所必需的,但不應該出現(xiàn)在非 HTTP 綁定中。其用法在本文中不作詳細說明。在本例中,它可以用來幫助區(qū)分兩個“foo”操作。SOAP 1.1 說明 soapAction 用于確定消息的“意圖”。它建議服務器使用此屬性來路由消息,而不必解析整個消息。在實際應用中,其用法很多。<soap:operation> 元素還可以包含另外一個屬性“style”。如果對于某個操作,有必要修改在 <soap:binding> 中指定的格式,就可以使用“style”屬性。

<operation> 元素可以包含 <input>、<output> 和 <fault> 元素,它們與端口類型節(jié)中相同的元素對應。上例中僅使用了 <input> 元素。這三個元素都有可選的“name”屬性,用于區(qū)分同名的操作(如本例所示)。在示例的 <input> 元素中包含了 <soap:body> 元素,它指定產生的 SOAP 消息的 <body> 中包括的內容。此元素有以下屬性:

Use

用于指定數(shù)據(jù)是“encoded”還是“l(fā)iteral”。“l(fā)iteral”指產生的 SOAP 消息包含的數(shù)據(jù)按照抽象定義(類型、消息和端口類型節(jié))所指定的方式進行格式化。“encoded”指由“encodingStyle”屬性(請參閱下面的說明)決定編碼。

Namespace

每個 SOAP 消息正文都可以有自己的名稱空間,以防止名稱沖突。在此屬性中指定的 URI 在產生的 SOAP 消息中被逐字使用。

EncodingStyle

對于 SOAP 編碼,這應該具有 URI 值“http://schemas.xmlsoap.org/soap/encoding”。

文檔樣式綁定

在上一節(jié)中,<soap:binding> 元素有一個值為“rpc”的 type 屬性。此屬性設置為“document”時,會改變線路上消息的序列化?,F(xiàn)在消息是文檔傳輸,而不是函數(shù)簽名。在這種綁定中,<message> 元素定義了文檔格式,而不是函數(shù)簽名。例如,考慮以下 WSDL 片段:

<definitions
  xmlns:stns="(SchemaTNS)"
  xmlns:wtns="(WsdlTNS)"
  targetNamespace="(WsdlTNS)">

<schema targetNamespace="(SchemaTNS)"
    elementFormDefault="qualified">
  <element name="SimpleElement" type="xsd:int"/>
  <element name="CompositElement" type="stns:CompositeType"/>
  <complexType name="CompositeType">
    <all>
      <element name='a' type="xsd:int"/>
      <element name='b' type="xsd:string"/>
    </all>
  </complexType>
</schema>

<message...>
  <part name='p1' type="stns:CompositeType"/>
  <part name='p2' type="xsd:int"/>
  <part name='p3' element="stns:SimpleElement"/>
  <part name='p4' element="stns:CompositeElement"/>
</message>
?</definitions>

該架構有兩個元素(SimpleElement 和 CompositeElement)和一個聲明的類型 (CompositeType)。唯一聲明的 <message> 元素有四個部分:p1,是 CompositeType 類型;p2,是 int 類型;p3,是 SimpleElement;p4,是 CompositeElement。下表比較了由以下使用/類型決定的四種綁定:rpc/literal、document/literal、rpc/encoded 和 document/encoded。表格顯示了對于每一種綁定會在線路上出現(xiàn)什么。

rpc / literal
<operation name="method1" style="rpc" ...>
<input>
<soap:body parts="p1 p2 p3 p4"

use="literal"
namespace="(MessageNS)"/>
</input>
</operation>

在線路上:
<soapenv:body... xmlns:mns="(MessageNS)"
xmlns:stns="(SchemaTNS)">
<mns:method1>
<mns:p1>
<stns:a>123</stns:a>
<stns:b>hello</stns:b>
</mns:p1>
<mns:p2>123</mns:p2>
<mns:p3>

<stns:SimpleElement>

123

</stns:SimpleElement>
</mns:p3>
<mns:p4>
<stns:CompositeElement>
<stns:a>123</stns:a>
<stns:b>hello</stns:b>
</stns:CompositeElement>
</mns:p4>
</mns:method1>
</soapenv:body>

document / literal / type=
<operation name="method1"

style="document" ...>
<input>
<soap:body parts="p1" use="literal">
</input>
</operation>

在線路上:

<soapenv:body... xmlns:stns="(SchemaTNS)">
<stns:a>123</stns:a>
<stns:b>hello</stns:b>
</soapenv:body>

rpc / encoded
<operation name="method1" style="rpc" ...>
<input>
<soap:body parts="p1 p2" use="encoded"
encoding=

"http://schemas.xmlsoap.org/soap/encoding/"
namespace="(MessageNS)"/>
</input>
</operation>

在線路上:
<soapenv:body... xmlns:mns="(MessageNS)">
<mns:method1>
<p1 TARGET="_self" HREF="#1"/>
<p2>123</p2>
</mns:method1>
<mns:CompositeType id="#1">
<a>123</a>
<b>hello</b>
</mns:CompositeType>
</soapenv:body>

document / literal / element=
<operation name="method1"

style="document" ...>
<input>
<soap:body parts="p3 p4"

use="literal">
</input>
</operation>

在線路上:

<soapenv:body... xmlns:stns="(SchemaTNS)">
<stns:SimpleElement>

123

</stns:SimpleElement>
<stns:CompositeElement>
<stns:a>123</stns:a>
<stns:b>hello</stns:b>
</stns:CompositeElement>
</soapenv:body>

  document / encoded
<operation name="method1"

style="document" ...>
<input>
<soap:body parts="p1 p2" use="encoded"
encoding=

"http://schemas.xmlsoap.org/soap/encoding/"
namespace="(MessageNS)"/>
</input>
</operation>

在線路上:
<soapenv:body... xmlns:mns="(MessageNS)">
<mns:CompositeType>
<a>123</a>
<b>hello</b>
</mns:CompositeType>
<soapenc:int>123</soapenc:int>
</soapenv:body>

 

<service> 和 <port> 元素

服務是一組 <port> 元素。每個 <port> 元素以一對一的方式將位置與 <binding> 相關聯(lián)。如果有多個 <port> 元素與同一個 <binding> 相關聯(lián),那么其他 URL 位置將用作備用項。

一個 WSDL 文檔中可以有多個 <service> 元素。允許多重 <service> 元素有很多用途。其中之一就是按照 URL 目標將端口組合在一起。這樣,我就可以用另一個 <service> 重定向我的所有股票報價請求,而同時客戶端程序還能繼續(xù)工作。因為在這種服務組合中,與不同服務關聯(lián)的協(xié)議都是相同的。多重 <service> 元素的另一種用途是根據(jù)底層協(xié)議對端口進行分類。例如,我可以將所有的 HTTP 端口放在一個 <service> 中,將所有 SMTP 端口放在另一個 <service> 中。然后,我的客戶端就可以搜索與它能處理的協(xié)議匹配的 <service>。

<service name="FOOService">
     <port name="fooSamplePort" binding="fooSampleBinding">
          <soap:address
            location="
http://carlos:8080/fooService/foo.asp"/>
     </port>
</service>

在同一個 WSDL 文檔中,<service> 的“name”屬性區(qū)分了各個服務。因為一個服務中可能有多個端口,端口也可以有一個“name”屬性。

總結

在本文中,我介紹了 WSDL 文檔中與 SOAP 相關的最重要的特性。需要指出的是:WSDL 不僅僅能說明 HTTP 上的 SOAP。WSDL 完全能夠說明使用 HTTP-POST、HTTP-GET、SMTP 等協(xié)議的 SOAP。有了 WSDL,開發(fā)者和用戶都更容易使用 SOAP。我相信 WSDL 和 SOAP 結合將引入一種通過網絡利用 Web 服務的新型應用。

WSDL 在其名稱空間中涉及很多 XML 元素。下面的表格總結了這些元素以及它們的屬性和內容:

屬性 內容(子) <definitions> name
targetNamespace
xmlns(其他名稱空間)
<types>
<message>
<portType>
<binding>
<service>
<types> (無) <xsd:schema> <message> name <part> <portType> name <operation> <binding> name
type
<operation> <service> name <port> <part> name
type
(空) <operation> name
parameterOrder
<input>
<output>
<fault>
<input> name
message
(空) <output> name
message
(空) <fault> name

message
(空) <port> name
binding
<soap:address>

資源:

  1. WSDL 1.1(英文)
  2. SOAP 1.1(英文)
  3. XML 架構入門(英文)
  4. MS SOAP Toolkit 下載網站(英文)
  5. 用于把 IDL 轉換為 WSDL 的工具(英文)
  6. 免費的 Web 服務資源,包括 WSDL 到 VB 的代理生成器(英文)
  7. PocketSOAP:SOAP 相關的組件、工具和源代碼(英文)

 

發(fā)布:2007-03-24 18:00    編輯:泛普軟件 · xiaona    [打印此頁]    [關閉]
相關文章:
上海OA系統(tǒng)
聯(lián)系方式

成都公司:成都市成華區(qū)建設南路160號1層9號

重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務大廈18樓

咨詢:400-8352-114

加微信,免費獲取試用系統(tǒng)

QQ在線咨詢