Web Service描述語言 WSDL 詳解 (1)

爲什麼使用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"/>
</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>。一些屬性附屬於根元素,就像<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屬性:

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

  每個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。

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

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欄中的預定義的一致,重複如下:

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

 

 

發佈了24 篇原創文章 · 獲贊 2 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章