像Internet協(xié)議之類的標(biāo)準(zhǔn)有沒有為權(quán)威所利用,或者人們這樣看待它是因?yàn)轫樦@的好處遠(yuǎn)遠(yuǎn)超出了代價(jià)?曾經(jīng)有許多試圖建立的標(biāo)準(zhǔn)都流產(chǎn)了。有時(shí)候,那些還沒有普遍使用的標(biāo)準(zhǔn)甚至由法令或政府規(guī)定強(qiáng)行推出:Ada語言就是一例。
我相信正是跟隨標(biāo)準(zhǔn)所帶來的好處使它廣泛接受。例如,對(duì)于鐵路服務(wù)來說,真正重要的是,不同公司所鋪設(shè)的鐵路結(jié)合到一起,或者是來自好幾個(gè)公司的產(chǎn)品協(xié)調(diào)的工作在一起。幾家大的企業(yè)合力建立了SOAP標(biāo)準(zhǔn)。Web Service描述語言(WSDL)向這種Web Service的提供商和用戶推出了方便的協(xié)調(diào)工作的方法,使我們能更容易的獲得SOAP的種種好處。幾家公司的鐵道并在一起不算什么難事,他們所需遵循的只是兩軌間的標(biāo)準(zhǔn)距離。對(duì)Web Service來說,這要復(fù)雜得多。我們必須先制定出指定接口的標(biāo)準(zhǔn)格式。
曾經(jīng)有人說SOAP并不真需要什么接口描述語言。如果SOAP是交流純內(nèi)容的標(biāo)準(zhǔn),那就需要一種語言來描述內(nèi)容。SOAP消息確實(shí)帶有某些類型信息,因此SOAP允許動(dòng)態(tài)的決定類型。但不知道一個(gè)函數(shù)的函數(shù)名、參數(shù)的個(gè)數(shù)和各自類型,怎么可能去調(diào)用這個(gè)函數(shù)呢?沒有WSDL,我可以從必備文檔中確定調(diào)用語法,或者檢查消息。隨便何種方法,都必須有人參與,這個(gè)過程可能會(huì)有錯(cuò)。而使用了WSDL,我就可以通過這種跨平臺(tái)和跨語言的方法使Web Service代理的產(chǎn)生自動(dòng)化。就像COM和CORBA的IDL文件,WSDL文件由客戶和服務(wù)器約定。
注意由于WSDL設(shè)計(jì)成可以綁定除SOAP以外的其他協(xié)議,這里我們主要關(guān)注WSDL在HTTP上和SOAP的關(guān)系。同樣,由于SOAP目前主要用來調(diào)用遠(yuǎn)程的過程和函數(shù),WSDL支持SOAP傳輸?shù)奈臋n規(guī)范。WSDL 1.1已經(jīng)作為記錄遞交給W3C(見http://www.w3.org/TR/wsdl.html)
WSDL文檔結(jié)構(gòu)
若要理解XML文檔,將之看作塊狀圖表非常有用。下圖以XML的文檔形式說明了WSDL的結(jié)構(gòu),它揭示了WSDL文檔五個(gè)欄之間的關(guān)系。
WSDL文檔可以分為兩部分。頂部分由抽象定義組成,而底部分則由具體描述組成。抽象部分以獨(dú)立于平臺(tái)和語言的方式定義SOAP消息,它們并不包含任何隨機(jī)器或語言而變的元素。這就定義了一系列服務(wù),截然不同的網(wǎng)站都可以實(shí)現(xiàn)。隨網(wǎng)站而異的東西如序列化便歸入底部分,因?yàn)樗唧w的定義。
l 抽象定義
Types
獨(dú)立與機(jī)器和語言的類型定義
Messages
包括函數(shù)參數(shù)(輸入與輸出分開)或文檔描述
PortTypes
引用消息部分中消息定義來描述函數(shù)簽名(操作名、輸入?yún)?shù)、輸出參數(shù))
2 具體定義
Bindings
PortTypes部分的每一操作在此綁定實(shí)現(xiàn)
Services
確定每一綁定的端口地址
下面的圖中,箭頭連接符代表文檔不同欄之間的關(guān)系。點(diǎn)和箭頭代表了引用或使用關(guān)系。雙箭頭代表"修改"關(guān)系。3-D的箭頭代表了包含關(guān)系。這樣,各Messages欄使用Types欄的定義,PortTypes欄使用Messages欄的定義;Bindings欄引用了PortTypes欄,Services欄引用Bindings欄,PortTypes和Bindings欄包含了operation元素,而Services欄包含了port元素。PortTypes欄里的operation元素由Bindings欄里的operation元素進(jìn)一步修改或描述。
在此背景中,我將使用標(biāo)準(zhǔn)的XML術(shù)語來描述WSDL文檔。Element是指XML的元素,而"attribute"指元素的屬性。于是:
<element attribute="attribute-value">contents</element> |
內(nèi)容也可能由一個(gè)或多個(gè)元素以遞歸的方式組成。根元素是所有元素之中最高級(jí)的元素。子元素總是從屬于另一個(gè)元素,父元素。
注意,文檔之中可能只有一個(gè)Types欄,或根本沒有。所有其他的欄可以只有零元素、單元素或是多元素。WSDL的列表要求所有的欄以固定的順序出現(xiàn):import, types, message, portType, binding, service。所有的抽象可以是單獨(dú)存在于別的文件中,也可以從主文檔中導(dǎo)入。

圖一:抽象定義和具體定義 |
WSDL文件示例
讓我們來研究一下WSDL文件,看看它的結(jié)構(gòu),以及如何工作。請(qǐng)注意這是一個(gè)非常簡(jiǎn)單的WSDL文檔實(shí)例。我們的意圖只是說明它最顯著的特征。以下的內(nèi)容中包括更加詳細(xì)的討論。
<?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">
?。紁art name="arg" type="xsd:int"/>
</message>
<message name="Simple.fooResponse">
?。紁art name="result" type="xsd:int"/>
</message>
<portType name="SimplePortType">
?。紀(jì)peration name="foo" parameterOrder="arg" >
<input message="wsdlns:Simple.foo"/>
?。紀(jì)utput message="wsdlns:Simple.fooResponse"/>
?。?operation>
</portType>
<binding name="SimpleBinding" type="wsdlns:SimplePortType">
?。約tk:binding preferredEncoding="UTF-8" />
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
?。紀(jì)peration name="foo">
?。約oap:operation soapAction="http://tempuri.org/action/Simple.foo"/>
?。糹nput>
?。約oap:body use="encoded" namespace="http://tempuri.org/message/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
?。?input>
?。紀(jì)utput>
?。約oap:body use="encoded" namespace="http://tempuri.org/message/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
</output>
</operation>
</binding>
<service name="FOOSAMPLEService">
?。紁ort name="SimplePort" binding="wsdlns:SimpleBinding">
?。約oap:address location="http://carlos:8080/FooSample/FooSample.asp"/>
?。?port>
</service>
</definitions> |
以下是該實(shí)例文檔的總述:稍后我將詳細(xì)討論每一部分的細(xì)節(jié)。
第一行申明該文檔是XML。盡管這并不是必需的,但它有助于XML解析器決定是否解析WSDL文件或只是報(bào)錯(cuò)。第二行是WSDL文檔的根元素:<definitions>。一些屬性附屬于根元素,就像<schema>子元素對(duì)于<types>元素。
?。紅ypes>元素包含了Types欄。如果沒有需要聲明的數(shù)據(jù)類型,這欄可以缺省。在WSDL范例中,沒有應(yīng)用程序特定的types聲明,但我仍然使用了Types欄,只是為了聲明schema namespaces。
<message>元素包含了Messages欄。如果我們把操作看作函數(shù),<message>元素定義了那個(gè)函數(shù)的參數(shù)。<message>元素中的每個(gè)<part>子元素都和某個(gè)參數(shù)相符。輸入?yún)?shù)在<message>元素中定義,與輸出參數(shù)相隔離--輸出參數(shù)有自己的<message>元素。兼作輸入、輸出的參數(shù)在輸入輸出的<message>元素中有它們相應(yīng)的<part>元素。輸出<message>元素以"Response"結(jié)尾,就像以前所用的"fooResponse"。每個(gè)<part>元素都有名字和類型屬性,就像函數(shù)的參數(shù)有參數(shù)名和參數(shù)類型。
用于交換文檔時(shí),WSDL允許使用<message>元素來描述交換的文檔。
<part>元素的類型可以是XSD基類型,也可以是SOAP定義類型(soapenc)、WSDL定義類型(wsdl)或是Types欄定義的類型。
一個(gè)PortTypes欄中,可以有零個(gè)、單個(gè)或多個(gè)<portType>元素。由于抽象PortType定義可以放置在分開的文件中,在某個(gè)WSDL文件中沒有<portType>元素是可能的。上面的例子里只是用了一個(gè)<portType>元素。而一個(gè)<portType>元素可在<o(jì)peration>元素中定義一個(gè)或是多個(gè)操作。示例僅使用了一個(gè)名為"foo"的<o(jì)peration>元素。這和某個(gè)函數(shù)名相同。<o(jì)peration>元素可以有一個(gè)、兩個(gè)、三個(gè)子元素:<input>, <o(jì)utput> 和<fault>元素。每個(gè)<input>和<o(jì)utput>元素中的消息都引用Message欄中的相關(guān)的<message>元素。這樣,示例中的整個(gè)<portType>元素就和以下的C函數(shù)等效:
這個(gè)例子足見XML和C相比要冗長(zhǎng)的多。(包括<message>元素,XML在示例中共使用了12行代碼來表達(dá)相同的單行函數(shù)聲明。)
Bindings欄可以有零個(gè)、一個(gè)或者多個(gè)<binding>元素。它的意圖是制定每個(gè)<o(jì)peration>通過網(wǎng)絡(luò)調(diào)用和回應(yīng)。Services欄同樣可以有零個(gè)、一個(gè)、多個(gè)<service>元素。它還包含了<port>元素,每個(gè)<port>元素引用一個(gè)Bindings欄里的<binding>元素。Bindings和Services欄都包含WSDL文檔。
Namespace
?。糳efinitions>和子節(jié)點(diǎn)<schema>都是namespace屬性:
<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>
?。約chema 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> |
每個(gè)namespace屬性都聲明了一個(gè)縮略語,用在文檔中。例如"xmlns:xsd"就為 http://www.w3.org/2001/XMLSchema定義了一個(gè)縮略語(xsd)。這就允許對(duì)該namespace的引用只需簡(jiǎn)單的在名字前加上前綴就可以了,如:"xsd:int"中的"xsd"就是合法的類型名。普通范圍規(guī)則可運(yùn)用于縮略前綴。也就是說,前綴所定義的元素只在元素中有效。
Namespace派什么用?namespace的作用是要避免命名沖突。如果我建立一項(xiàng)Web Service,其中的WSDL文件包含一個(gè)名為"foo"的元素,而你想要使用我的服務(wù)與另一項(xiàng)服務(wù)連接作為補(bǔ)充,這樣的話另一項(xiàng)服務(wù)的WSDL文件就不能包含名為"foo"的元素。兩個(gè)服務(wù)器程序只有在它們?cè)趦蓚€(gè)事例中表示完全相同的東西時(shí),才可以取相同的名字。如果有了表示區(qū)別的namespace,我的網(wǎng)絡(luò)服務(wù)里的"foo"就可以表示完全不同于另一個(gè)網(wǎng)絡(luò)服務(wù)里"foo"的含義。在你的客戶端里,你只要加以限制就可以引用我的"foo"。
見下例:http://www.infotects.com/fooService#foo 就是完全限制的名字,相當(dāng)于"carlos:foo",如果我聲明了carlos作為http://www.infotects.com/fooService的快捷方式。請(qǐng)注意namespace中的URL是用來確定它們的唯一性的,同時(shí)也便于定位。URL所指向的地方不必是實(shí)際存在的網(wǎng)絡(luò)地址,也可以使用GUID來代替或補(bǔ)充URL。例如,GUID"335DB901-D44A-11D4-A96E-0080AD76435D"就是一個(gè)合法的namespace指派。
targetNamespace屬性聲明了一個(gè)namespace,元素中所有的聲明的名字都列于其內(nèi)。在WSDL示例中,<definitions>的targetNamespace 是http://tempuri.org/wsdl。這意味著所有在WSDL文檔中聲明的名字都屬于這個(gè)namespace。<schema>元素有自己的targetNamespace屬性,其值為 http://tempuri.org/xsd ,在<schma>元素中定義的所有名字都屬于這個(gè)namespace而不是main的target namespace。
?。約chema>元素的以下這行聲明了默認(rèn)的namespace。Schema中所有有效的名字都屬于這個(gè)namespace。
xmlns="http://www.w3.org/2001/XMLSchema" |