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 | 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 | 不轉換和生效 |
ENTITIES | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
ENTITY | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
Float | VT_R4 | Single | float | float | |
GDay | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
GMonth | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
GMonthDay | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
GYear | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
GYearMonth | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
ID | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
IDREF | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
IDREFS | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
Int | VT_I4 | Long | long | long | |
Integer | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉換時範圍生效 |
Language | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
Long | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉換時範圍生效 |
Name | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
NCName | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
negativeInteger | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉換時範圍生效 |
NMTOKEN | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
NMTOKENS | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
nonNegativeIntege | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉換時範圍生效 |
nonPositiveInteger | VT_DECIMAL | Variant | DECIMA | DECIMAL | 轉換時範圍生效 |
normalizedString | VT_BSTR | String | BSTR | BSTR | |
NOTATION | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
Number | VT_DECIMAL | Variant | DECIMAL | DECIMAL | |
positiveInteger | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉換時範圍生效 |
Qname | VT_BSTR | String | BSTR | BSTR | 不轉換和生效 |
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 | 不轉換和生效 |
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定義了兩套內建的數據類型:原始的和派生的。在下文中查閱內建數據類型的層次十分有益:
http://www.w3.org/TR/2001/PR-xmlschema-2-20010330complex類型 XML schema允許complex類型的定義,就像C裏是struct。例如,爲了定義類似如下的C的struct類型:
我們可以寫XML schema:
不過,complex類型可以表達比struct更多的信息。除了<sequence>以外,它還可以有其他的子元素,比如<all>
這意味着<element>的成員變量可以以任何順序排列,每一個都是可選的。這和C中的struct類型不太一樣。 注意內建數據類型string, int, float。C的string也是XML的string,float也類似。但C中的long類型在XML中是int(上表中)。 在WSDL文件中,像上面的complex類型可以在Types欄聲明。例如,我可以用以下方式聲明PERSON類型並用在Messages欄。
上例中第一個消息由"adperson",並且有一個<part>,其類型爲"PERSON"。PERSON類型是在Types欄聲明的。 如果我們使用完整的WSDL文件包含以上的部分,並以之初始化MSTK2 SoapClient,它將成功的解析該文件。當然,它不會去調用<addPerson>。這是因爲SoapClient本身並不知道如何處理complex類型,它需要定製類型映射來處理complex類型。MSTK2文檔中有包含定製類型映射的示例。 還有另一種方法可以把<part>元素聯繫到類型聲明。這就是使用元素。下例中我將Types欄中聲明兩個元素("Person"和"Gendr"),然後我將在"addPerson"<message>中使用元素屬性來引用它們。
Types欄中的Gender<element>裏嵌入了枚舉類型,其枚舉值爲"Male""Female"。然後我又在"addPerson"<message>中通過元素屬性而不是類型屬性來引用它。 "元素屬性"和"類型屬性"在把某特定類型關聯到<part>時有什麼不同呢?使用元素屬性,我們可以描述一個部分,它可以假定幾個類型(就像變量一樣),而是用類型屬性我們就無法這樣做。下例說明了這一點。
上例也告訴我們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"。下列的例子揭示了從這一類型派生出一位整數數組的方法:
新的complex類型從soapenc:array限制派生。然後又聲明瞭complex類型的一個屬性。引用"soapenc:arrayType"實際上是這樣完成的:
wsdl:arrayType屬性值決定了數組每個成員的類型。數組的成員也可以是Complex類型。:
WSDL要求數組的類型由"ArrayOf"和每個數組元素的類型串聯而成。很顯然,顧名思義,"ArrayOfPERSON"是PERSON結構的數組。下面我將使用ArrayOfPERSON來聲明一個<message>,並加入不止一個PERSON:
PortType定義了一些抽象的操作。PortType中的operation元素定義了調用PortType中所有方法的語法,每一個operation元素聲明瞭方法的名稱、參數(使用<message>元素)和各自的類型(<part>元素要在所有<message>中聲明)。 在一篇WSDL文檔中可以有幾個<PortType>元素,每一個都和一些相關操作放在一起,就和COM和一組操作的接口相似。 在<operation>元素中,可能會有至多一個<input>元素,一個<output>元素,以及一個<fault>元素。三個元素各有一個名字和一個消息屬性。 <input>, <output>, <fault>元素屬性的名字有何含義呢?它們可以用來區別兩個同名操作(重載)。例如,看下面兩個C函數:
這種重載在WSDL中可以這樣表示:
到目前爲止,還沒有一種SOAP的實現支持重載。這對基於JAVA的客戶端十分重要,因爲JAVA服務器使用的接口用到JAVA的重載特性。而對基於COM的客戶端,就不那麼重要,因爲COM是不支持重載的。 <binding>和<operation>元素 Binding欄是完整描述協議、序列化和編碼的地方,Types, Messages和PortType欄處理抽象的數據內容,而Binding欄是處理數據傳輸的物理實現。Binding欄把前三部分的抽象定義具體化。 把相關的數據制定和消息聲明分開,這意味着同一類型服務的提供者可以把一系列的操作標準化。每個提供者可以提供定製的binding來互相區分。WSDL也有一個重要的結構,使抽象定義可以放在分離的文件中,而不是和Bindings和Services在一起,這樣可在不同的服務提供者之間提供標準化的抽象定義,這很有幫助。例如,銀行可以用WSDL文檔來標準化一些銀行的操作。每個銀行仍然可以自由的訂製下層的協議、串行優化,及編碼。 下面是重載的WSDL示例 的Binding欄,重複在此以便討論:
<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值:
|
文檔風格實現
在前幾欄中,<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> </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> |
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>。
<service name="FOOService"> <port name="fooSamplePort" binding="fooSampleBinding"> <soap:address location="http://carlos:8080/fooService/foo.asp"/> </port> </service> |
在一個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) |
<types> <message> <portType> <binding> <service> |
<types> | (none) | <xsd:schema> |
<message> | Name | <part> |
<portType> | Name | <operation> |
<binding> | name type |
<operation> |
<service> | name | <port> |
<part> | name type |
(empty) |
<operation> | name parameterOrder |
<input> <output> <fault> |
<input> | name message |
(empty) |
<output> | name message |
(empty) |
<fault> | name message |
(empty) |
<port> | name binding |
<soap:address> |
資源:
1. WSDL 1.1
2. SOAP 1.1
3. XML Schema Primer
4. MS SOAP Toolkit Download Site
5. A tool for translating IDL to WSDL
6. Free Web Services resources including a WSDL to VB proxy generator
7. PocketSOAP: SOAP related components, tools & source code