<a href="http://www.blogjava.net/libin2722/articles/159469.html">http://www.blogjava.net/libin2722/articles/159469.html</a>
為什麼使用WSDL?
像Internet協定之類的标準有沒有為權威所利用,或者人們這樣看待它是因為順之所獲的好處遠遠超出了代價?曾經有許多試圖建立的标準都流産了。有時候,那些還沒有普遍使用的标準甚至由法令或政府規定強行推出:Ada語言就是一例。
我相信正是跟随标準所帶來的好處使它廣泛接受。例如,對于鐵路服務來說,真正重要的是,不同公司所鋪設的鐵路結合到一起,或者是來自好幾個公司的産品協調的工作在一起。幾家大的企業合力建立了SOAP标準。Web Service描述語言(WSDL)向這種Web Service的提供商和使用者推出了友善的協調工作的方法,使我們能更容易的獲得SOAP的種種好處。幾家公司的鐵道并在一起不算什麼難事,他們所需遵循的隻是兩軌間的标準距離。對Web Service來說,這要複雜得多。我們必須先制定出指定接口的标準格式。
曾經有人說SOAP并不真需要什麼接口描述語言。如果SOAP是交流純内容的标準,那就需要一種語言來描述内容。SOAP消息确實帶有某些類型資訊,是以SOAP允許動态的決定類型。但不知道一個函數的函數名、參數的個數和各自類型,怎麼可能去調用這個函數呢?沒有WSDL,我可以從必備文檔中确定調用文法,或者檢查消息。随便何種方法,都必須有人參與,這個過程可能會有錯。而使用了WSDL,我就可以通過這種跨平台和跨語言的方法使Web Service代理的産生自動化。就像COM和CORBA的IDL檔案,WSDL檔案由客戶和伺服器約定。
注意由于WSDL設計成可以綁定除SOAP以外的其他協定,這裡我們主要關注WSDL在HTTP上和SOAP的關系。同樣,由于SOAP目前主要用來調用遠端的過程和函數,WSDL支援SOAP傳輸的文檔規範。WSDL 1.1已經作為記錄遞交給W3C(見http://www.w3.org/TR/wsdl.html)
WSDL文檔結構
若要了解XML文檔,将之看作塊狀圖表非常有用。下圖以XML的文檔形式說明了WSDL的結構,它揭示了WSDL文檔五個欄之間的關系。
WSDL文檔可以分為兩部分。頂部分由抽象定義組成,而底部分則由具體描述組成。抽象部分以獨立于平台和語言的方式定義SOAP消息,它們并不包含任何随機器或語言而變的元素。這就定義了一系列服務,截然不同的網站都可以實作。随網站而異的東西如序列化便歸入底部分,因為它包含具體的定義。
l 抽象定義
Types
獨立與機器和語言的類型定義
Messages
包括函數參數(輸入與輸出分開)或文檔描述
PortTypes
引用消息部分中消息定義來描述函數簽名(操作名、輸入參數、輸出參數)
2 具體定義
Bindings
PortTypes部分的每一操作在此綁定實作
Services
确定每一綁定的端口位址
下面的圖中,箭頭連接配接符代表文檔不同欄之間的關系。點和箭頭代表了引用或使用關系。雙箭頭代表"修改"關系。3-D的箭頭代表了包含關系。這樣,各Messages欄使用Types欄的定義,PortTypes欄使用Messages欄的定義;Bindings欄引用了PortTypes欄,Services欄引用Bindings欄,PortTypes和Bindings欄包含了operation元素,而Services欄包含了port元素。PortTypes欄裡的operation元素由Bindings欄裡的operation元素進一步修改或描述。
在此背景中,我将使用标準的XML術語來描述WSDL文檔。Element是指XML的元素,而"attribute"指元素的屬性。于是:
<element attribute="attribute-value">contents</element>
内容也可能由一個或多個元素以遞歸的方式組成。根元素是所有元素之中最進階的元素。子元素總是從屬于另一個元素,父元素。
注意,文檔之中可能隻有一個Types欄,或根本沒有。所有其他的欄可以隻有零元素、單元素或是多元素。WSDL的清單要求所有的欄以固定的順序出現:import, types, message, portType, binding, service。所有的抽象可以是單獨存在于别的檔案中,也可以從主文檔中導入。
圖一:抽象定義和具體定義
WSDL檔案示例
讓我們來研究一下WSDL檔案,看看它的結構,以及如何工作。請注意這是一個非常簡單的WSDL文檔執行個體。我們的意圖隻是說明它最顯著的特征。以下的内容中包括更加詳細的讨論。
<?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"/>
<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>
</output>
</binding>
<service name="FOOSAMPLEService">
<port name="SimplePort" binding="wsdlns:SimpleBinding">
<soap:address location="http://carlos:8080/FooSample/FooSample.asp"/>
</port>
</service>
</definitions>
以下是該執行個體文檔的總述:稍後我将詳細讨論每一部分的細節。
第一行申明該文檔是XML。盡管這并不是必需的,但它有助于XML解析器決定是否解析WSDL檔案或隻是報錯。第二行是WSDL文檔的根元素:<definitions>。一些屬性附屬于根元素,就像<schema>子元素對于<types>元素。
<types>元素包含了Types欄。如果沒有需要聲明的資料類型,這欄可以預設。在WSDL範例中,沒有應用程式特定的types聲明,但我仍然使用了Types欄,隻是為了聲明schema namespaces。
<message>元素包含了Messages欄。如果我們把操作看作函數,<message>元素定義了那個函數的參數。<message>元素中的每個<part>子元素都和某個參數相符。輸入參數在<message>元素中定義,與輸出參數相隔離--輸出參數有自己的<message>元素。兼作輸入、輸出的參數在輸入輸出的<message>元素中有它們相應的<part>元素。輸出<message>元素以"Response"結尾,就像以前所用的"fooResponse"。每個<part>元素都有名字和類型屬性,就像函數的參數有參數名和參數類型。
用于交換文檔時,WSDL允許使用<message>元素來描述交換的文檔。
<part>元素的類型可以是XSD基類型,也可以是SOAP定義類型(soapenc)、WSDL定義類型(wsdl)或是Types欄定義的類型。
一個PortTypes欄中,可以有零個、單個或多個<portType>元素。由于抽象PortType定義可以放置在分開的檔案中,在某個WSDL檔案中沒有<portType>元素是可能的。上面的例子裡隻是用了一個<portType>元素。而一個<portType>元素可在<operation>元素中定義一個或是多個操作。示例僅使用了一個名為"foo"的<operation>元素。這和某個函數名相同。<operation>元素可以有一個、兩個、三個子元素:<input>, <output> 和<fault>元素。每個<input>和<output>元素中的消息都引用Message欄中的相關的<message>元素。這樣,示例中的整個<portType>元素就和以下的C函數等效:
int foo(int arg);
這個例子足見XML和C相比要冗長的多。(包括<message>元素,XML在示例中共使用了12行代碼來表達相同的單行函數聲明。)
Bindings欄可以有零個、一個或者多個<binding>元素。它的意圖是制定每個<operation>通過網絡調用和回應。Services欄同樣可以有零個、一個、多個<service>元素。它還包含了<port>元素,每個<port>元素引用一個Bindings欄裡的<binding>元素。Bindings和Services欄都包含WSDL文檔。
Namespace
<definitions>和子節點<schema>都是namespace屬性:
<schema targetNamespace="http://tempuri.org/xsd"
</schema>
每個namespace屬性都聲明了一個縮略語,用在文檔中。例如"xmlns:xsd"就為 http://www.w3.org/2001/XMLSchema定義了一個縮略語(xsd)。這就允許對該namespace的引用隻需簡單的在名字前加上字首就可以了,如:"xsd:int"中的"xsd"就是合法的類型名。普通範圍規則可運用于縮略字首。也就是說,字首所定義的元素隻在元素中有效。
Namespace派什麼用?namespace的作用是要避免命名沖突。如果我建立一項Web Service,其中的WSDL檔案包含一個名為"foo"的元素,而你想要使用我的服務與另一項服務連接配接作為補充,這樣的話另一項服務的WSDL檔案就不能包含名為"foo"的元素。兩個伺服器程式隻有在它們在兩個事例中表示完全相同的東西時,才可以取相同的名字。如果有了表示差別的namespace,我的網絡服務裡的"foo"就可以表示完全不同于另一個網絡服務裡"foo"的含義。在你的用戶端裡,你隻要加以限制就可以引用我的"foo"。
見下例:http://www.infotects.com/fooService#foo 就是完全限制的名字,相當于"carlos:foo",如果我聲明了carlos作為http://www.infotects.com/fooService的快捷方式。請注意namespace中的URL是用來确定它們的唯一性的,同時也便于定位。URL所指向的地方不必是實際存在的網絡位址,也可以使用GUID來代替或補充URL。例如,GUID"335DB901-D44A-11D4-A96E-0080AD76435D"就是一個合法的namespace指派。
targetNamespace屬性聲明了一個namespace,元素中所有的聲明的名字都列于其内。在WSDL示例中,<definitions>的targetNamespace 是http://tempuri.org/wsdl。這意味着所有在WSDL文檔中聲明的名字都屬于這個namespace。<schema>元素有自己的targetNamespace屬性,其值為 http://tempuri.org/xsd ,在<schma>元素中定義的所有名字都屬于這個namespace而不是main的target namespace。
<schema>元素的以下這行聲明了預設的namespace。Schema中所有有效的名字都屬于這個namespace。
SOAP消息
對于使用WSDL的客戶機和服務機來說,研究WSDL檔案的一種方法就是決定什麼來接受所發送的資訊。盡管SOAP使用底層協定,如IP和HTTP等,但應用程式決定了伺服器與客戶機之間互動的進階協定。也就是說,進行一項操作,比如"echoint"把輸入的整數送回,參數的數目、每個參數的類型、以及參數如何傳送等因素決定了應用程式特定的協定。有很多方法可以确定此類協定,但我相信最好的方法就是使用WSDL。如果我們用這種視角來看待它,WSDL不隻是一種接口協定,而且是一種協定特定的語言。它就是我們超越"固定"協定(IP、HTTP等)所需要的應用程式特定協定。
WSDL可以确定SOAP消息是否遵從RPC或文檔風格。RPC風格的消息(就是示例中所用的)看起來像是函數調用。而文檔風格的消息則更普通,嵌套層次更小。下面的XML消息就是示例WSDL檔案解析後的發送/接受效果,解析使用的是MS SOAP Toolkit 2.0(MSTK2)中的SoapClient對象。
從用戶端調用"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>
兩函數都調用了消息,其回應是有效的XML。SOAP消息由幾部分組成,首先是<Envelop>元素,包含一個可選的<Header>元素以及至少一個<body>元素。Rpc函數所調用的消息體有一個根據操作"foo"命名的元素,而回應資訊體有一個"fooResponse"元素。Foo元素有一個部分<arg>,就和WSDL中描述的一樣,是單參數的。fooResponse也相應的有一個<result>的部分。注意encodingStyle、envelope和message的namespace和WSDL Bindings欄中的預定義的一緻,重複如下:
<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>
</output>
</operation>
WSDL的Types欄和Messages欄中的XML Schema
WSDL資料類型是基于"XML Schema: Datatypes"(XSD)的,現在已經被W3C推薦。這一文檔共有三個版本(1999,2000/10,2001),是以必須在namespace屬性的<definitions>元素中指明所使用的是哪一個版本。
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
在本文中,我将隻考慮2001版本。WSDL标準的推薦者強烈建議使用2001版。
在本欄和以後各部分,需使用以下簡縮或字首
字首
代表的Namespace
描述
Soapenc
http://schemas.xmlsoap.org/soap/encoding
SOAP 1.1 encoding
Wsdl
http://schemas.xmlsoap.org/wsdl/soap
WSDL 1.1
Xsd
http://www.w3.org/2001/XMLSchema
XML Schema
XSD基類型
下表是直接從MSTK2文檔中取出的,列舉了MSTK2所支援的所有XSD基類型。它也告訴在用戶端或伺服器端的WSDL讀取程式如何把XSD類型映射到在VB、C++和IDL中相應的類型。
XSD (Soap)類型
變量類型
VB
C++
IDL
Comments
anyURI
VT_BSTR
String
BSTR
base64Binary
VT_ARRAY | VT_UI1
Byte()
SAFEARRAY
SAFEARRAY(unsigned char)
Boolean
VT_BOOL
VARIANT_BOOL
Byte
VT_I2
Integer
short
轉換時驗證範圍有效性
Date
VT_DATE
DATE
時間設為 oo:oo:oo
DateTime
Double
VT_R8
double
Duration
不轉換和生效
ENTITIES
ENTITY
Float
VT_R4
Single
float
GDay
GMonth
GMonthDay
GYear
GYearMonth
ID
IDREF
IDREFS
Int
VT_I4
Long
long
VT_DECIMAL
Variant
DECIMAL
轉換時範圍生效
Language
Name
NCName
negativeInteger
NMTOKEN
NMTOKENS
nonNegativeIntege
nonPositiveInteger
DECIMA
normalizedString
NOTATION
Number
positiveInteger
Qname
Short
Time
日設為1899年12月30日
Token
unsignedByte
VT_UI1
unsigned char
UnsignedInt
unsignedLong
unsignedShort
VT_UI4
XSD定義了兩套内建的資料類型:原始的和派生的。在下文中查閱内建資料類型的層次十分有益:
http://www.w3.org/TR/2001/PR-xmlschema-2-20010330
complex類型
XML schema允許complex類型的定義,就像C裡是struct。例如,為了定義類似如下的C的struct類型:
typedef struct {
string firstName;
string lastName;
long ageInYears;
float weightInLbs;
float heightInInches;
} PERSON;
我們可以寫XML schema:
<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>
不過,complex類型可以表達比struct更多的資訊。除了<sequence>以外,它還可以有其他的子元素,比如<all>
<xsd:all>
</xsd:all>
這意味着<element>的成員變量可以以任何順序排列,每一個都是可選的。這和C中的struct類型不太一樣。
注意内建資料類型string, int, float。C的string也是XML的string,float也類似。但C中的long類型在XML中是int(上表中)。
在WSDL檔案中,像上面的complex類型可以在Types欄聲明。例如,我可以用以下方式聲明PERSON類型并用在Messages欄。
<definitions … >
<schema targetNamespace="someNamespace"
xmlns:typens="someNamespace" >
<message name="addPerson">
<part name="person" type="typens:PERSON"/>
<message name="addPersonResponse">
上例中第一個消息由"adperson",并且有一個<part>,其類型為"PERSON"。PERSON類型是在Types欄聲明的。
如果我們使用完整的WSDL檔案包含以上的部分,并以之初始化MSTK2 SoapClient,它将成功的解析該檔案。當然,它不會去調用<addPerson>。這是因為SoapClient本身并不知道如何處理complex類型,它需要定制類型映射來處理complex類型。MSTK2文檔中有包含定制類型映射的示例。
還有另一種方法可以把<part>元素聯系到類型聲明。這就是使用元素。下例中我将Types欄中聲明兩個元素("Person"和"Gendr"),然後我将在"addPerson"<message>中使用元素屬性來引用它們。
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>
</element>
<element name="Gender">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Male" />
<xsd:enumeration value="Female" />
</xsd:restriction>
</xsd:simpleType>
<part name="who" element="typens:Person"/>
<part name="sex" element="typens:Gender"/>
Types欄中的Gender<element>裡嵌入了枚舉類型,其枚舉值為"Male""Female"。然後我又在"addPerson"<message>中通過元素屬性而不是類型屬性來引用它。
"元素屬性"和"類型屬性"在把某特定類型關聯到<part>時有什麼不同呢?使用元素屬性,我們可以描述一個部分,它可以假定幾個類型(就像變量一樣),而是用類型屬性我們就無法這樣做。下例說明了這一點。
xmlns:typens="someNamespace">
<xsd:complexType name="femalePerson">
<xsd:complexContent>
<xsd:extension base="typens:PERSON" >
<xsd:element name="favoriteLipstick" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
<xsd:complexType name="malePerson">
<xsd:extension base="typens:PERSON" >
<xsd:element name="favoriteShavingLotion" type="xsd:string" />
<xsd:complexType name="maleOrFemalePerson">
<xsd:choice>
<xsd:element name="fArg" type="typens:femalePerson" >
<xsd:element name="mArg" type="typens:malePerson" />
</xsd:choice>
<part name="person" type="typens:maleOrFemalePerson"/>
上例也告訴我們extension的派生。"femailPerson"和"malePerson"都是從"PERSON"派生出來的。它們各有一些額外的元素:"femalePerson"有"favoriteLipstick"元素,"malePerson"有"favoriteShavingLotion"元素。兩派生類型都歸入一個complex類型"maleOrFemalePerson",使用的是<choice>構造。最後,在"adperson"<message>中,新類型有"person"<part>引用。這樣,參數或<part>就可以是"femalePerson"或"malePerson"了。
數組
XSD提供<list>結構來聲明一個數組,元素之間有空格界定。不過SOAP不是使用XSD來編碼數組的,它定義了自己的數組類型--"SOAP-ENC: Array"。下列的例子揭示了從這一類型派生出一位整數數組的方法:
<xsd:complexType name="ArrayOfInt">
<xsd:restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:int[]"/>
新的complex類型從soapenc:array限制派生。然後又聲明了complex類型的一個屬性。引用"soapenc:arrayType"實際上是這樣完成的:
<xsd:attribute name="arrayType" type="xsd:string"/>
wsdl:arrayType屬性值決定了數組每個成員的類型。數組的成員也可以是Complex類型。:
<xsd:complexType name="ArrayOfPERSON">
<xsd:restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType"
wsdl:arrayType="typens:PERSON[]"/>
</xsd:restriction>
WSDL要求數組的類型由"ArrayOf"和每個數組元素的類型串聯而成。很顯然,顧名思義,"ArrayOfPERSON"是PERSON結構的數組。下面我将使用ArrayOfPERSON來聲明一個<message>,并加入不止一個PERSON:
<message name="addPersons">
<part name="person" type="typens:ArrayOfPERSON"/>
<portType>和<operation>元素
PortType定義了一些抽象的操作。PortType中的operation元素定義了調用PortType中所有方法的文法,每一個operation元素聲明了方法的名稱、參數(使用<message>元素)和各自的類型(<part>元素要在所有<message>中聲明)。
在一篇WSDL文檔中可以有幾個<PortType>元素,每一個都和一些相關操作放在一起,就和COM和一組操作的接口相似。
在<operation>元素中,可能會有至多一個<input>元素,一個<output>元素,以及一個<fault>元素。三個元素各有一個名字和一個消息屬性。
<input>, <output>, <fault>元素屬性的名字有何含義呢?它們可以用來差別兩個同名操作(重載)。例如,看下面兩個C函數:
void foo(int arg);
void foo(string arg);
這種重載在WSDL中可以這樣表示:
<definitions name="fooDescription"
xmlns:stk="http://schemas.microsoft.com/soap-toolkit/wsdl-
extension"
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" >
<message name="foo1">
<message name="foo2">
<part name="arg" type="xsd:string"/>
<portType name="fooSamplePortType">
<operation name="foo" parameterOrder="arg " >
<input name="foo1" message="wsdlns:foo1"/>
<input name="foo2" message="wsdlns:foo2"/>
<binding name="fooSampleBinding" type="wsdlns:fooSamplePortType">
<soap:operation soapAction="http://tempuri.org/action/foo1"/>
<input name="foo1">
<soap:body use="encoded" namespace="http://tempuri.org/message/"
</input>
<soap:operation soapAction="http://tempuri.org/action/foo2"/>
<input name="foo2">
namespace="http://tempuri.org/message/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
/>
<service name="FOOService">
<port name="fooSamplePort" binding="fooSampleBinding">
<soap:address
location="http://carlos:8080/fooService/foo.asp"/>
</port>
到目前為止,還沒有一種SOAP的實作支援重載。這對基于JAVA的用戶端十分重要,因為JAVA伺服器使用的接口用到JAVA的重載特性。而對基于COM的用戶端,就不那麼重要,因為COM是不支援重載的。
<binding>和<operation>元素
Binding欄是完整描述協定、序列化和編碼的地方,Types, Messages和PortType欄處理抽象的資料内容,而Binding欄是處理資料傳輸的實體實作。Binding欄把前三部分的抽象定義具體化。
把相關的資料制定和消息聲明分開,這意味着同一類型服務的提供者可以把一系列的操作标準化。每個提供者可以提供定制的binding來互相區分。WSDL也有一個重要的結構,使抽象定義可以放在分離的檔案中,而不是和Bindings和Services在一起,這樣可在不同的服務提供者之間提供标準化的抽象定義,這很有幫助。例如,銀行可以用WSDL文檔來标準化一些銀行的操作。每個銀行仍然可以自由的訂制下層的協定、串行優化,及編碼。
下面是重載的WSDL示例 的Binding欄,重複在此以便讨論:
transport="http://schemas.xmlsoap.org/soap/http"/>
<soap:operation soapAction="http://tempuri.org/action/foo1"/>
<input name="foo1">
encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/" />
<input name="foo2">
<soap:body use="encoded"
namespace="http://tempuri.org/message/"
encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/" />
<binding>元素已經取了一個名字(本例中"fooSampleBinding"),這樣就可以被Services欄的<port>元素引用了。它有一個"type"的屬性引用<portType>,本例中就是"wsdlns:fooSamplePortType"。第二行是MSTK2的擴充元素<stk:binding>,它指定了preferredEncoding屬性為"UTF-8"。
<soap:binding>元素指定了所使用的風格("rpc")和傳輸方式。Transport屬性應用了一個namespace,正是這個namespace指明使用HTTP SOAP協定。
有兩個同以"foo"命名的<operation>元素。唯一不同的是它們各自的<input>名字,分别為"foo1"和"foo2"。兩個<operation>元素中的<soap:operation>元素有同樣的"soapAction"屬性,是URI。soapAction屬性是SOAP特定的URI,它隻是簡單的使用于SOAP消息。所産生的SOAP消息有一個SOAPAction頭,而URI也僅在<soap:operation>元素裡才起作用。soapAction屬性在HTTP的binding中是必需的,但在其他非HTTP
binding中卻不要提供。目前它的使用并不清楚,但它似乎有助于本例中的兩個"foo"操作。SOAP 1.1指明soapAction用來确定消息的"意圖"。似乎伺服器可以在不解析整個消息的情況下就能使用這一屬性來發送消息。實際上,它的使用多種多樣。<soap:operation>元素也可以包含另一屬性,即"style"屬性,在有必要沖突<soap:binding>元素指定的風格時可以使用。
<operation>屬性可以包含<input>, <output> 和<fault>的元素,它們都對應于PortType欄中的相同元素。隻有<input>元素在上例中提供。這三個元素中的每一個可有一個可選的"name"屬性,在本例中,我們用這種方法來區分同名操作。在本例的<input>元素中有一個<soap:body>元素,它指定了哪些資訊被寫進SOAP消息的資訊體中。該元素有以下屬性:
Use
用于制定資料是"encoded"還是"literal"。"Literal"指結果SOAP消息包含以抽象定義(Types, Messages, 和PortTypes)指定格式存在的資料。"Encoded"指"encodingStyle"屬性決定了編碼方式。
Namespace
每個SOAP消息體可以有其自己的namespace來防止命名沖突。這一屬性制定的URI在結果SOAP消息中逐字使用。
EncodingStyle
對SOAP編碼,它應該有以下URI值:
"http://schemas.xmlsoap.org/soap/encoding"
文檔風格實作
在前幾欄中,<soap:binding>元素有一個類型屬性,設為"rpc"。此屬性設為"document"時會改變傳輸時消息的串行化。不同于函數簽名,現在的消息是文檔傳輸的。在這類binding中,<message>元素定義文檔格式,而不是函數簽名。作為例子,考慮以下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>
<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"/>
…
schema有兩個元素:SimpleElement和CompositeElement,還有一個類型聲明(CompositeType)。唯一聲明的<message>元素有四個部分:p1:Composite型;p2:int型;p3:SimpleElement型;p4:CompositeElement型。以下有一個表,對四種類型的use/type決定的binding作一比較:rpc/literal, document/literal, rpc/encoded, 以及document/encoded。表指明了每種binding的表現。
<service>和<port>元素
service是一套<port>元素。在一一對應形式下,每個<port>元素都和一個location關聯。如果同一個<binding>有多個<port>元素與之關聯,可以使用額外的URL位址作為替換。
一個WSDL文檔中可以有多個<service>元素,而且多個<service>元素十分有用,其中之一就是可以根據目标URL來組織端口。這樣,我就可以友善的使用另一個<service>來重定向我的股市查詢申請。我的用戶端程式仍然工作,因為這種根據協定歸類的服務不随服務而變化。多個<service>元素的另一個作用是根據特定的協定劃分端口。例如,我可以把所有的HTTP端口放在同一個<service>中,所有的SMTP端口放在另一個<service>裡。我的客戶可以搜尋與它可以處理的協定相比對的<service>。
<soap:address
location="http://carlos:8080/fooService/foo.asp"/>
在一個WSDL文檔中,<service>的name屬性用來區分不同的service。因為同一個service中可以有多個端口,它們也有"name"屬性。
總結
本文中我描述了WSDL文檔關于SOAP方面的最顯著的特點。不過應該說明的是WSDL并不僅限于HTTP上的SOAP。WSDL用來描述HTTP-POST、HTTP-GET、SMTP及其他協定時非常清晰。使用了WSDL,SOAP更加容易處理了,無論是開發者還是使用者。我相信WSDL和SOAP一起将會開創網絡應用程式世界的新時代。
WSDL的namespace裡有一系列的XML元素。下表概述了那些元素、它們的屬性和内容。
元素
屬性
内容(子元素)
<definitions>
name
targetNamespace
xmlns (other namespaces)
<message>
<portType>
<binding>
<service>
(none)
<xsd:schema>
<part>
<operation>
type
<port>
(empty)
parameterOrder
<fault>
message
binding
<soap:address>
資源: