當前位置:工程項目OA系統(tǒng) > 泛普各地 > 上海OA系統(tǒng) > 上海OA信息化
Web服務說明語言 (WSDL) 淺釋
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 定義了兩組內置的數(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)什么。
<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>
<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>
<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>
<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>
<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 元素。下面的表格總結了這些元素以及它們的屬性和內容:
targetNamespace
xmlns(其他名稱空間)
<message>
<portType>
<binding>
<service>
type
type
parameterOrder
<output>
<fault>
message
message
message
binding
資源:
- WSDL
1.1(英文)
- SOAP 1.1(英文)
- XML 架構入門(英文)
- MS SOAP Toolkit 下載網站(英文)
- 用于把 IDL 轉換為 WSDL 的工具(英文)
- 免費的 Web
服務資源,包括 WSDL 到 VB 的代理生成器(英文)
- PocketSOAP:SOAP 相關的組件、工具和源代碼(英文)
- 1有生命力的上海OA信息化
- 2企業(yè)上海OA信息化架構
- 3軟件服務時代的來臨
- 4推行上海OA信息化,從哪幾個方面考慮?
- 5微軟基于Web計算的框架結構分析
- 6上海OA信息化的幾種學科體系
- 7軟件企業(yè),你真正服務好你的客戶了嗎?
- 8上海OA信息化落差于金融業(yè)風險管理專家系統(tǒng)
- 9[原創(chuàng)]企業(yè)如何規(guī)劃和實施上海OA信息化系統(tǒng)
- 10對高校行政管理辦公自動化(OA)問題的幾點思考
- 11實施上海OA信息化的要素分析
- 12上海OA信息化訪問單
- 13隱性知識轉化是知識創(chuàng)新的基礎
- 14導師制與上海OA信息化
- 15使用Microsoft SOAP Toolkit 2.0建立安全Web服務
- 16上海OA信息化原理的評論
- 17上海寶康嬰童用品有限公司OA辦公平臺
- 18上海市兒童醫(yī)院OA辦公自動化系統(tǒng)
- 19OA+ERP”整合優(yōu)化銷售訂單管理
- 20借力上海OA信息化 提升客戶服務
- 21上海保集(集團)有限公司OA辦公軟件系統(tǒng)平臺 V3.50SP1
- 22個人上海OA信息化與組織上海OA信息化
- 23上海OA信息化不神秘
- 24OA辦公軟件的作用如何讓企業(yè)認可呢
- 25不同業(yè)務模式下的上海OA信息化策略
- 26上海吉祥航空股份有限公司辦公自動化OA系統(tǒng)
- 27上海OA信息化:七分組織 兩分流程 一分技術
- 28企業(yè)為何要實施上海OA信息化
- 29建立共享的企業(yè)文化
- 30上海環(huán)境集團辦公OA信息系統(tǒng)
成都公司:成都市成華區(qū)建設南路160號1層9號
重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務大廈18樓