WSDL文件详解(转贴)上
2024-07-21 02:21:56
供稿:网友
作者:carlos c. tapang
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>