台灣最大程式設計社群網站
線上人數
2128
 
會員總數:246224
討論主題:189786
歡迎您免費加入會員
討論區列表 >> 專欄文章 >> 網路服務描述語言 (WSDL) 詳解(微軟文件)
[]  
[我要回覆]
回應主題 加入我的關注話題 檢舉此篇討論 將提問者加入個人黑名單
網路服務描述語言 (WSDL) 詳解(微軟文件)
價值 : 0 QP  點閱數:1957 回應數:0
樓主

站務人員 站長
門外漢
0 1580
542 9
發送站內信

捐贈 VP 給 站務人員
Infotects

2001 年 7 月

摘要:只要使用 WSDL,即可以真正不受語言與平台限制的方式,自動為網路服務產生 Proxy。(列印共 28 頁)

內容
使用 WSDL 的原因
WSDL 文件結構
WSDL 範例檔案
命名空間
SOAP 訊息
WSDL 類型與訊息區段中的 XML 結構描述
<portType> 與 <operation> 元素
<binding> 與 <operation> 元素
文件樣式繫結
<service> 與 <port> 元素
總結
資源

使用 WSDL 的原因
網路通訊協定之類的標準,到底是某當權機構強制施行的;或是,人們真的認為,確實遵守的優點遠勝於必須付出的代價?歷來所提出的標準,許多都無疾而終。有時候,從未被大眾所使用的標準,法律或政府規定還是硬要使用:好比說,Ada 程式語言。

我相信,遵守標準所能獲得的優點,才是使標準普及的原因。例如,鐵路服務的重點是,即使不同公司所建造的列車軌道,也可以接駁在一起;也就是說,不同公司的產品必須能整合使用。因此,幾家廠商便共同推出了 SOAP 這個標準。WSDL (Web Services Description Language,網路服務描述語言) 可輕易將網路服務提供廠商與服務的使用者結合起來,輕鬆獲取 SOAP 的優點。不同公司所建造的列車軌道比較容易整合;畢竟,必須同意的標準不過是兩條鐵軌之間的距離而已。但對網路服務而言,情況則複雜得多了。首先必須取得的共識是,指定介面的標準格式。

有個論點一直認為,SOAP 並不需要介面描述語言。若 SOAP 純粹是溝通內容的標準,那麼它需要的便是描述該內容的語言。SOAP 訊息確實可傳遞類型資訊,也因此 SOAP 允許以動態的方式決定類型。但除非知道函數的名稱、參數、與類型,否則根本無法正確呼叫任何函數。若不使用 WSDL,還是可以從所提供的文件或檢查線路訊息,來確定呼叫的語法。但這兩種方式都需要人力介入,也因此可能在過程中出現錯誤。若使用 WSDL,即可以真正不受語言與平台限制的方式,自動為網路服務產生 Proxy。類似 CORBA 或 COM 的 IDL 檔案,WSDL 檔案也是一種客戶端與伺服端之間的合約。

請注意,雖然 WSDL 的設計目的是,對 SOAP 以外的通訊協定顯示繫結;但本文的主旨則是在 HTTP 上與 SOAP 有關連的 WSDL。而且雖然目前 SOAP 的主要用途是遠端程序或函數呼叫,但 WSDL 已經可以在 SOAP 下,指定傳輸的文件。WSDL 1.1 已經以 Note (通知書) 的方式 (請參閱 http://www.w3.org/TR/wsdl.html),提交至 W3C 。

WSDL 文件結構
若欲瞭解任何 XML 文件,區塊圖是很有助益的。下圖說明 WSDL 的結構;它是一種 XML 文件,可顯示 WSDL 文件五個組成區段之間的關係。

WSDL 文件可分成兩個區段群組。上群組是由抽象定義 (Abstract Definitions) 所組成;而下群組則是由具體定義 (Concrete Descriptions) 所組成。抽象區段定義 SOAP 訊息的方式是,排除平台與語言的限制;因此它們不含任何電腦或語言特有的元素。如此一來,不同的網站皆可實作它所定義的服務。諸如序列化等網站特有的資訊,則交由含具體描述的下區段處理。

抽象定義
Types (類型)
不受電腦與語言限制的類型定義。
Messages (訊息)
內含函數參數 (輸入與輸出分離) 或文件描述。
PortTypes (埠類型)
根據 Messages 區段中的訊息定義,說明函數簽章 (作業名稱、輸入參數、輸出參數)。
具體定義
Bindings (繫結)
在 PortTypes 區段中,指定每個作業的繫結。
Services (服務)
指定每個繫結的傳輸埠位址。
在下圖中,箭頭連接代表,在文件的不同區段之間有關聯性存在。點與箭頭連接代表「參照」或「使用」關係。雙箭頭連接代表「修改 (modifier)」關係。3-D 箭頭連接代表「包含 (contains)」關係。因此,Messages 區段使用 Types 區段的定義;PortTypes 區段使用 Messages 區段的定義;Bindings 區段參照 PortTypes 區段;Services 區段參照 Bindings 區段。PortTypes 與 Bindings 區段內含作業元素,而 Services 區段則內含埠元素。Bindings 區段中的作業元素,會修改或進一步說明 PortTypes 區段中的作業元素。

以此為基礎,本文將使用標準 XML 技術,說明 WSDL 文件。「元素」一詞是指 XML 元素,而「屬性」一詞則是指元素屬性。因此:

<element attribute="attribute-value">contents</element>

內容可以遞迴的方式,由一個以上的元素組成。根元素是最頂端的元素,文件中其它所有元素皆歸在其下。子元素永遠附屬於其它的父元素。

請注意,只可以有一個 Types 區段,甚或根本沒有此區段。其它所有區段可以有零、一、或多個父元素。例如,Messages 區段可以有零、或多個 <message> 元素。WSDL 結構描述規定,所有區段必須依指定順序排列:匯入、類型、訊息、portType、繫結、與服務。每個抽象區段可能各自位於不同的檔案,並分別匯入至主文件中。



[圖 1] 抽象與具體定義

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"/>
</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>

下列為此範例文件的大略說明。稍後,會就每個區段詳細討論。

第一行宣告,本文件為 XML。雖然非是必要,但它可協助 XML 剖析器決定,應該剖析此 WSDL 檔案,或發出錯誤訊號。第二行是 WSDL 文件中的根元素:<definitions>。有幾個命名空間屬性 (命名空間宣告),附屬於此根元素以及 <types> 元素的 <schema> 子元素中。

<types> 元素是由 Types 區段組成。若無資料類型須要宣告,則此區段可以省略。在範例 WSDL 中,並無應用程式專屬的類型須要宣告,但還是使用 Types 區段,以宣告本文件中結構描述的命名空間。

<message> 元素是由 Messages 區段所組成。若將作業視為函數,則 <message> 元素可將參數定義至該函數。<message> 元素中的每個 <part> 子元素,都對應一個參數。請將參數定義輸入至單一的 <message> 元素之中,並與位於自己 <message> 元素中的輸出參數分開。同時是輸入與輸出的參數,在輸入與輸出 <message> 元素中,各有與自己相對應的 <part> 元素。根據慣例,輸出 <message> 元素的名稱,如同「fooResponse」,會以「Response」結束。如同函數參數需有名稱與類型一樣,每個 <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> 元素中的訊息屬性,都會參照 Messages 區段中的相關 <message> 元素。因此,範例中的整個 <portType> 元素,相當於下列的 C 函數宣告:

int foo(int arg);

此範例正足以說明,相較於 C,XML 是多麼冗長的語言。 (包括 <message> 元素在內,此範例共使用 12 行的 XML 進行函數宣告;而相同的動作, C 只要一行即可。)

Bindings 區段可有零、一或多個 <binding> 元素。而其目的則是,指定每個 <operation> 呼叫與回應,在線上傳送的方式。Services 區段也可有零、一或多個 <service> 元素。它所含的 <port> 元素,每個都參照 Bindings 區段中的一個 <binding> 元素。Bindings 與 Services 區段都是由,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」可定義一個速記法 (xsd),代表命名空間 http://www.w3.org/2001/XMLSchema。如此一來,稍後即可在文件中參照此命名空間;其方式很簡單,只要在名稱前嵌入字首「xsd」,則「xsd:int」即成為合格的類型名稱。一般的領域設定規則,皆可套用至此速記字首。也就是說,在一個元素中所定義的字首,僅在該元素中使用。

使用命名空間的原因何在?命名空間的目的在避免命名衝突。若我建立了一個網路服務,其 WSDL 檔案中含有一個名為「foo」的元素,而您想要將我的網路服務,與另一個互補性的服務結合起來使用;假若沒有使用命名空間,則另一個網路服務,在其 WSDL 檔案中,便絕對不能使用「foo」這個名稱。除非在兩者的執行個體中,都是指完全相同的東西,否則這兩個服務不可以使用相同的名稱。但若使用兩個不同的命名空間,則我網路服務的「foo」所代表的意義,便與另一個網路服務的「foo」不同。在客戶端,您便必須以嵌入字首 (prefixing 或 qualifying) 的方式,參照我的「foo」。例如,若我宣告 http://www.infotects.com/fooService 的速記法是 carlos,則 http://www.infotects.com/fooService#foo 這個完全合格的名稱,可以等於「carlos:foo」。請注意,若使用 URI 作為命名空間,不但可確保其獨特性,更可允許在文件中使用定址器。URI 所指向的位址,不必對應真正的網路位址。也可以使用 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> 元素中,它所宣告的是預設的命名空間。結構中所有無嵌入字首 (unqualified ) 的名稱,都屬於此命名空間。

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

SOAP 訊息
對使用 WSDL 檔案的客戶端與伺服端而言,它所代表的意義之一是,可決定線上傳送的內容。雖然 SOAP 使用的是,諸如 IP 與 HTTP 等低層次的通訊協定,但此應用程式卻可決定,特定客戶端與特定伺服端之間所使用的高層次通訊協定。換句話說,若以「echoInt」作業為例,使用回波傳回輸入整數,則參數總計、每個參數的類型、與參數傳過線路的方式 (序列化) 等,便會構成一個應用程式特有的通訊協定。指定這類通訊協定的方式很多,但我認為使用 WSDL 才是最佳的方式。若就此觀點而言,WSDL 便不只是一種「介面合約」,它更是一種通訊協定指定語言。若超出了諸如 IP 與 HTTP 等固定通訊協定的範圍,進入應用程式專屬通訊協定的領域,WSDL 正好符合我們所需。

WSDL 可以指定,SOAP 訊息是否符合 rpc 或文件樣式。正如範例中所使用的一樣,rpc 樣式訊息的外觀就像是,一個有零或多個參數的函數呼叫。文件樣式訊息則較扁平 (flatter) 且不需要那麼多的巢狀階層。下列 XML 訊息的傳送與接收,是使用 MS SOAP Toolkit 2.0 (MSTK2) 的 SoapClient 物件,剖析 WSDL 範例檔案的結果。

自客戶端執行函數呼叫「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 訊息是由 <Envelope> 元素所組成,其中含有一個選擇性的 <Header> 元素,與至少一個的 <body> 元素。傳送與接收訊息兩者,在主 <Envelope> 元素中,都有只一個 <Body> 元素。rpc 函數呼叫訊息的主體,有一個依作業名稱「foo」命名的元素,而回應主體中則有一個名為「fooResponse」元素。這個 foo 元素有個如範例 WSDL 所示的單一引數,其名稱為 <arg>。同樣地,fooResponse 也有一個 <result>。在此處重複出現的 WSDL Bindings 區段中,請注意 encodingStyle、信封、與訊息命名空間的指定方式。

<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 類型與訊息區段中的 XML 結構描述
WSDL 的資料類型,是根據目前 W3C Recommendation 的「XML Schema: Datatypes」(XSD)。此文件共有三種不同的版本 (1999、2000/10、與 2001),若欲指定特定 WSDL 檔案所使用的版本,請在 <definitions> 元素中,將其宣告為命名空間的屬性。方法如下:

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

本文僅以 2001 版為考量。WSDL 標準的擁護者,也大力建議使用 2001 版。

在本節與後續章節中,採用的字首或命名空間速記法如下:

字首 對應的命名空間 說明
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 Schema

XSD 基本類型
下表直接取自 MSTK2 文件,列舉了 MSTK2 支援的所有 XSD 基本類型。該表說明,位於客戶端與伺服端的 WSDL 讀者,如何在 VB、C++、與 IDL 中,將 XSD 類型對應至不同與對等的類型。

XSD (Soap) 類型 不同的類型 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 不執行驗證或轉換
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 不執行驗證或轉換
nonNegativeInteger VT_DECIMAL Variant DECIMAL DECIMAL 轉換時驗證範圍。
nonPositiveInteger VT_DECIMAL Variant DECIMAL 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 Day 設定成 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-20010330,檢視內建類型的階層架構。

複雜類型
XML Schema 可定義複雜類型,也就是 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>

不過,<complexType> 所能表示的,絕不僅止於 struct 的對應而已。除了 <sequence> 之外,它還可以有其它的子元素。若不用 <sequence>,也可以使用 <all>:

<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> 可以任何順序輸入,且每一項都具有選擇性。這點便與 C struct 的使用方式不同了。

請注意範例中,string (字串)、int (整數)、float (浮點數) 等內建資料類型的使用方式。C 的字串在 XML 中也是字串,且浮點數還是浮點數。但 C 的 long (長整數),在 XML 則是 int (請參考上表)。

在 WSDL 檔案中,Types 區段是宣告上述複雜類型的位置。例如,PERSON 類型可以下列方式宣告,並將其用於 Messages 區段中:

<?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>。在 Types 區段中,類型 PERSON 會被宣告為複雜類型。

起始 MSTK2 SoapClient 時,若在上述片段使用完整的 WSDL 檔案,它便可成功地剖析該檔案。不過,它還是不能將函數呼叫,傳送至 <addPerson>。這是因為 SoapClient 本身不知道如何處理複雜類型;它需要自訂的類型對應器 (mapper) 才能處理複雜類型。在 MSTK2 文件中有一個範例應用程式,它含有自訂的類型對應器。

另外還有一個方法,可將 <part> 元素,關連至類型宣告。這個方法使用的是元素,而非類型屬性。下個範例會在 Types 區段中,先宣告兩個元素 (「Person」與「Gender」);然後在「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>

在 Types 區段的 Gender <element> 中,內嵌著一個匿名的列舉類型,其值可以是「Male」或「Female」。然後在「addPerson」的 <message> 中,再使用元素屬性 (不用類型屬性) 參照該元素。

若欲關連特定類型至 <part>,使用「元素」與「類型」屬性有何不同?若使用「類型」屬性,part 可描述成能採取數種類型 (就像變數一樣);但若使用「元素」屬性,便不能這麼做。請參考下列範例的說明。

<?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>

此範例也說明了副檔名的衍生用法。「femalePerson」與「malePerson」兩者,都是衍生自「PERSON」。它們都各有一個額外的元素:「femalePerson」的「favoriteLipstick」以及「malePerson」的「favoriteShavingLotion」。使用 <choice> 的 construct,這兩個衍生類型又可結合成一個複雜類型「maleOrFemalePerson」。最後,在「addPerson」的 <message> 中,此結合類型又可供「person」的 <part> 參照。而此 <part> 或參數,可以是「femalePerson」或「malePerson」。

陣列
XSD 可提供 <list> construct,以宣告空白所分隔的項目陣列。但是,SOAP 並不使用 XSD 清單為陣列編碼;而是為陣列定義自己的類型,即「SOAP-ENC:Array」。下列範例說明,如何為單一維度的整數陣列,依其法則導出此種類型:

<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 屬性值,可決定每個陣列成員的類型。陣列項目也可以是複雜類型:

<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 的要求是,陣列的類型名稱必須是,「ArrayOf」與陣列項目類型的的串連 (concatenation)。也因此,單從名稱即可得知,「ArrayOfPERSON」是 PERSON struct 的陣列。在下例中,只要使用 ArrayOfPERSON 宣告一個 <message>,即可新增多個 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 中的作業元素,可定義呼叫所有 PortType 方法的語法。每個作業元素都會宣告,方法的名稱、參數 (使用 <message> 元素)、類型 (每個 <message> 中所宣告的 <part> 元素)。

在 WSDL 文件中,可有多個 <portType> 元素。每個 <portType> 元素,群組化多個相關作業的方式,與 COM 介面群組化方法的方式非常類似。

在一個 <operation> 元素中,最多可有一個 <input> 元素、一個 <output> 元素、與一個 <fault> 元素。這三個元素都各有一個名稱與訊息屬性。

在 <input>、<output>、與 <fault> 元素中使用名稱屬性的目的為何?原來是為了,區別具相同名稱 (多載) 的兩項作業。例如,下列兩個 C 函數,即具有相同的名稱,但不同的參數。

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 實作,能進行作業名稱的多載動作。這對使用 Java 的客戶端是很重要的,因為使用 Java 的伺服端所使用的介面,會利用 Java 的多載功能。但這對使用 COM 的客戶端倒無所謂,因為 COM 並不支援多載。

<binding> 與 <operation> 元素
Binding 區段是,指定通訊協定、序列化、與編碼的地方。若 Types、Messages、與 PortType 等區段負責抽象的資料內容,那麼 Binding 區段便負責處理資料傳輸的具體細節。Binding 區段負責使前三個區段的抽象具體化。

將繫結規格從資料與訊息宣告中分離出來的意義是,執行相同業務的服務提供者,可將一組作業 (portType) 標準化。若欲凸顯自身的差異,每個提供者可另外提供自訂繫結。讓 WSDL 擁有匯入 construct 是有好處的;因為如此一來,即可遠離 Bindings 與 Services 區段,將抽象定義置於自身的檔案中;並可將其散佈於服務提供者間,讓他們把抽象定義設定成標準。例如,銀行可標準化,一組抽象 WSDL 文件詳盡說明的銀行作業。但每家銀行還是可以自由地,「自訂」基礎通訊協定、序列最佳化、與編碼。

下列為 Binding 區段的 WSDL 多載範例,於此重複以方便詳細討論:

<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」),以方便 Services 區段的 <port> 元素參照。它有個參照 <portType> 的「type」屬性,在本例中為「wsdlns:fooSamplePortType」。第二行是 MSTK2 擴充元素 <stk:binding>,它可指定 preferredEncoding,或稱「UTF-8」。

<soap:binding> 元素可指定,使用的樣式 (rpc) 與傳輸。傳輸屬性會參照命名空間,以確定所使用的 HTTP SOAP 通訊協定。

有兩個 <operation> 元素具有相同的名稱「foo」。這兩個作業不同之處在於,兩個不同的 <input> 名稱:「foo1」與「foo2」。在兩個 <operation> 元素中,<soap:operation> 元素的「soapAction」屬性皆相同,都是一個 URI。soapAction 屬性是 SOAP 專屬的 URI,並逐字依 SOAP 訊息使用。以此方式取得的 SOAP 訊息,會有個 SOAPAction 標頭;而且還會以 <soap:operation> 元素中的 URI 為其值。雖然 HTTP 繫結需要 soapAction 屬性,但非 HTTP 繫結則不用。本文刊出時,其用途仍然不明。就本範例而言,它似乎可用來區別兩個不同的「foo」作業。SOAP 1.1 宣稱,soapAction 可用來識別訊息的「目的 (intent)」。它更建議伺服端,可用此屬性路由訊息,而不用剖析整個訊息。實務上,其用途差異很大。<soap:operation> 元素也可以含另一個名為「style」的屬性;若特定作業需要覆寫 <soap:binding> 元素中所指定的樣式,則可使用此屬性。

<operation> 元素所含的 <input>、<output>、與 <fault> 元素,可對應 PortTypes 區段的相同元素。在上例中出現的只有 <input> 元素。在本例中,這三個元素都各有一個選擇性的「name」屬性,可用以區別名稱相同的作業。在範例的 <input> 元素中,有個 <soap:body> 元素,它可指定輸入結果 SOAP 訊息之 <body> 的內容。此元素有下列屬性:

Use
它是用以指定資料採「編碼 (encoded)」或「常值 (literal)」。「常值」的意義是,結果 SOAP 訊息所含資料的格式,完全依照抽象定義 (Types、Messages、與 PortTypes 區段) 的指定規格 。「編碼」的意義是,「encodingStyle」屬性 (參考下文) 會指定編碼方式。
Namespace
每個 SOAP 訊息的主體,都有自己的命名空間,以防止名稱衝突。此屬性所指定的 URI,會逐字使用於結果 SOAP 訊息之中。
EncodingStyle
以 SOAP 的編碼方式而言,URI 的值應該是「http://schemas.xmlsoap.org/soap/encoding」。
文件樣式繫結
在上一區段中,<soap:binding> 元素有個類型屬性被設定成「rpc」。若將此屬性設定成「document」,即可改變訊息在線上的排序。此時這些訊息便成了文件傳輸,而不再是函數簽章。在此繫結類型中,<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>

此結構描述有 SimpleElement 與 CompositeElement 兩個元素,以及一個宣告類型 (CompositeType)。所宣告的唯一 <message> 元素有四個部分 (part):p1 是 CompositeType 類型;p2 是 int 類型;p3 是 SimpleElement;而 p4 是 CompositeElement。下表是四種繫結的比較,依照「使用/類型」分別為:rpc/literal、document/literal、rpc/encoded、與 document/encoded。本表可說明,每種繫結方式反映在線上的實際情況。

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>
hello
</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>
hello
</mns:CompositeType>
<soapenc:int>123</soapenc:int>
</soapenv:body>


<service> 與 <port> 元素
服務即是一組 <port> 元素。每個 <port> 元素,都以一對一的方式,關連一個位置與 <binding>。若有一個以上的 <port> 元素,與相同的 <binding> 關連,便可以使用其它 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」屬性,可區分出個別服務的不同。因為一個服務可能會有幾個傳輸埠,所以傳輸埠也必須有「name」屬性。

總結
本文已經說明了 WSDL 文件最顯著的 SOAP 特色。但應該聲明的是,WSDL 絕不僅只能在 HTTP 上說明 SOAP 而已。在說明 SOAP 上,WSDL 的表達能力絕對足以使用 HTTP-POST、HTTP-GET、SMTP、與其它通訊協定。只要使用 WSDL,不論是程式開發人員或一般使用者,都可輕鬆處理 SOAP。個人深信,WSDL 與 SOAP 的結合,將可引進全新的應用程式類別,以更徹底地利用各種網路服務。

在 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>

資源:
WSDL 1.1
SOAP 1.1
XML 結構描述入門
MS SOAP Toolkit 下載網站
IDL 至 WSDL 的轉譯工具
含 WSDL 至 VB 之 Proxy 產生器的免費網路資源
PocketSOAP:SOAP 之相關元件、工具、與原始程式碼


© 2001 Microsoft Corporation. All rights reserved. Legal Notices


本篇文章發表於2001-12-07 00:00
目前尚無任何回覆
   

回覆
如要回應,請先登入.