在“使用 WSDL 部署 Web 服務”系列中,Bilal 將研究創建、部署和發佈 Web 服務的所有主要技術方面 ― 從 Web 服務描述語言(WSDL),到簡單對象訪問協議(SOAP)以及通用描述、發現和集成(UDDI)註冊中心。第 1 部分集中講述了 WSDL 創建:您將學習如何手工創建 WSDL 接口,然後將您的成果與 WSDL 編寫工具的輸出作比較。
可互操作的基於 Web 分佈式應用程序的思想並非新近出現。僅舉一例,電子數據交換(EDI)市場需求早在 B2B 在線電子商務獲得任何有意義的實現之前就存在了 ― 並且隨着 B2B 電子市場的普及,互操作性已經成爲最迫切的 EDI 需求。
以任何在線電子市場爲例。存在着許多企業,各自提供特有的“服務( services
)”(讓我們稱之爲“Web 服務( Web services
)”)。在當今的電子商務中,尚不存在一種機制,使一個業務能自動發現其預期夥伴提供的服務。所謂的 下一代 .com還是提供這種自動的發現機制。
這種新的 .com 需要一種解決方案來描述它所提供的服務(Web 服務)。具體而言,這意味着您需要一種格式或某種類型的語法,使您可以通過使用它們來描述下列問題的答案:
- 您的在線業務提供什麼服務?
- 您如何調用業務服務?
- 當用戶調用您的業務服務時,該業務服務需要他/她提供什麼信息?
- 用戶將如何提供這些必需信息?
- 服務將以什麼格式發送返回給用戶的信息?
很幸運,WSDL 提供了完成所有這些作業的機制。
|
爲更好理解 WSDL 是如何工作的,我將首先描述 SOAP 和 HTTP 是如何使用 WSDL 工作的。WSDL 的用途是“描述”您的 Web 服務。業務之間將通過交換 WSDL 文件來理解對方的服務。一旦知道您夥伴的服務並希望調用它們,SOAP 就派上用場了。可以將服務看作是通過 SOAP 訪問的對象。
最有可能的情況是,您將通過因特網或電子郵件與潛在夥伴通信。當然,因特網使用 HTTP 而電子郵件以 SMTP 方式工作,這使得 HTTP 和 SMTP 成爲作爲 SOAP 的“傳輸服務提供者”的有利候選人。
|
現在,我將講述爲 Web 服務編寫 WSDL 的過程。目的是公開現有的 Web 服務。您所處的情況也許就是下列情況之一:
- 您有一個現存的服務(例如,一個網站),並希望表示它的功能性。
- 您有一個 WSDL,並且希望依照已經決定表示的功能性來實現 Web服務器端的邏輯。(有些人也許會認爲這是一個不可能的方案,但是 UDDI 的指紋概念使它變得極爲可能;我將在本系列的第四部分討論 UDDI)。
- 您正在從零開始,並且既無網站又無 WSDL 界面。
本文中所涵蓋的信息適用於這些可能性中的任意一種或全部。
|
我將把 WSDL 編寫分成四個簡單步驟。遵循每個步驟,您的 Web 服務將準備就緒用於部署。
您將構建一個移動電話銷售公司的服務接口作爲樣本項目(我將這個服務稱爲 MobilePhoneService
)。該公司銷售不同型號的移動電話,所以公司 Web 服務的後端數據存儲庫中將包含一個具有兩列( model number
和 price
)的表格。(爲了將焦點保持在 WSDL 本身,我保持該表格的簡單性)。有兩個關於要使用 WSDL 表示的服務的方法:
- getListOfModels ()
- getPrice (modelNumber)
GetListOfModels
方法提供了一個字符串數組,其中每個字符串表示一種移動電話的型號。 GetPrice
獲得型號,然後返回它的價格。WSDL 將這些方法作爲操作調用。現在將開始構建“WSDL 接口文件( WSDL interface file
)”。
每個 WSDL 文件的根元素都是 <definitions>
,必須在其中提供服務的完整描述。首先,必須在 <definitions>
元素中提供各種名稱空間的聲明。三個必須做的外部名稱空間聲明是 WSDL、SOAP 和 XSD(XML 模式定義)。還有一個名稱空間 ― TNS,它指您的 MobilePhoneService(這表示 TNS(targetNamespace 的縮寫)包含專爲 MobilePhoneService
定義的所有元素和屬性的名稱)。但是 WSDL 是您將在 WSDL 編寫中使用得最多的主要名稱空間。在本系列文章中使用到其它名稱空間時,我將提到它們的效用。
關於名稱空間只要注意一點:WSDL 廣泛地使用名稱空間這一概念。我鼓勵您到 W3C 的官方網站去學習關於名稱空間的更多知識(請參閱 參考資料)。WSDL 是這種思想的一種實現,因爲名稱空間提供了無限的靈活性,而這恰恰是用於電子數據交換的可移植格式所需要的。
<definitions>
元素包含一個或多個 <portType>
元素,實際上,每個元素都是您希望表示的一系列 operation
。或者,您也可以將單個 portType 元素看作是將各種方法組成類的一個邏輯分組。例如,如果您的供應鏈管理解決方案需要在客戶和供應商之間進行交互,您最可能做的是分別定義與他們交互的功能性;也就是說,您將爲用戶和供應商各定義一個 portType。應該將每個 portType 稱爲 服務,因此整個 WSDL 文件將成爲一個服務集合。
必須爲每個服務提供一個名稱。在本例中,僅有一個服務(因此只有一個 <portType>
)。 需要使用該 portType 元素的 name
屬性爲移動電話銷售服務指定名稱。
在每個服務內可以有幾個方法、或者 operation
,WSDL 通過 <operation>
元素來引用它們。樣本應用程序有兩個要表示的方法: getListOfModels
和getPrice
。因此,您需要提供兩個 <operation>
元素,每個元素有一個 name
。 我已經使用 <operation>
元素的 name
屬性命名了每個操作。
此時,WSDL 文件看上去象 清單 1。
清單 1:定義操作
<?xml version="1.0" encoding="UTF-8" ?> <definitions name="MobilePhoneService" targetNamespace="www.mobilephoneservice.com/MobilePhoneService-interface" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.mobilephoneservice.com/MobilePhoneService" xmlns:xsd="http://www.w3.org/1999/XMLSchema"> <portType name="MobilePhoneService_port"> <operation name="getListOfModels "> ....... ....... </operation> <operation name="getPrice"> ....... ....... </operation> </portType> </definitions> |
定義好操作(或方法)以後,現在需要指定將向它們發送和從它們返回的參數。在 WSDL 術語中,所有參數稱爲“消息”。認爲您是在遞送消息而結果得到返回的消息是有用的。方法調用是這樣一種操作:它準備返回“消息”來響應進入的消息。
請回憶,在第一步驟中有兩個操作要表示。第一個操作 getListOfModels
不必獲得任何參數並且返回一個字符串數組,其中每個字符串表示移動電話的型號。因此,必須定義一個包含字符串數組的 <message>
元素。
看看 清單 2 中的各種 <message>
元素。其中的第一個元素有一個等於 ListOfPhoneModels
的名稱屬性(該消息的邏輯名稱),以及名稱爲 models 的單個<part>
元素,這意味着該 ListOfPhoneModels
是一個“只含有一個 part 的”消息,其中僅有的這個 part 的名稱是“models”。消息可以有任意多個 part ― 只要爲它們起不同的名稱,以唯一標識。
我已包括了 <part>
元素的另一個屬性,它就是 type
。將這個“type”屬性當作 C++ 或 Java 中的數據類型。我已經將 models
的數據類型指定爲 tns:Vector。(請回憶,我在 <definitions>
根元素中指定了一些名稱空間,其中之一是 tns
。)這個類型即指 MobilePhoneService
名稱空間。這意味着當編寫 WSDL 時,您可以創建自己的名稱空間。現在您也許會問兩個邏輯問題:爲什麼?和怎麼做?
要回答 爲什麼,讓我們以 getListOfModels
操作返回的字符串數組爲例。WSDL 使用 XML 模式定義(XSD)定義的一些原始數據類型(諸如 int、float、long、short、byte、string、Boolean 等等),並允許您直接使用它們,或者以這些原始數據類型構建複雜數據類型後,在消息中使用它們。這就是爲什麼當引用複雜數據類型時,您需要定義自己的名稱空間。在本例中,需要爲 array of strings
構建一個複雜數據類型。
現在來看 怎麼做問題,您將使用 XSD 創建自己的名稱空間。爲實現這個目的,我在 <types>
元素中使用了 xsd:complexType 元素用來定義稱爲 Vector
的數據類型。 Vector
使用兩個原始數據類型:string(元素數據)和 Integer(元素計數)。因此, Vector
成爲名稱空間的一部分並可以通過別名 tns
來引用。
在 清單 2 中,我以類似的方式定義了另外兩個消息 PhoneModel
和 PhoneModelPrice
。這兩個消息只使用了 xsd 名稱空間中的原始數據類型 string,因此您不必爲使用它們而定義任何更復雜的數據類型。
您也許已經注意到當創建 <message>
元素時,沒有指定這些消息是進入參數還是返回值。這是一個您將在 <portType>
元素內的 <operation>
元素中完成的工作。因此,正如您在 清單 2 中所看到的,我已經將 <input>
和 <output>
元素都添加到這兩個操作中。每個 input 元素通過消息名來引用它並將它當作用戶調用該操作時要提供的參數。類似地,每個 <output>
元素引用一個消息;它將該消息當作操作調用的返回值。
至今, 清單 2準確地限定了目前的討論的範圍。
我以一種抽象方式定義了操作和消息,而不考慮實現的細節。實際上,WSDL 的任務是定義或描述 Web 服務,然後提供一個對外部框架的引用來定義 WSDL 用戶將如何實現這些服務。可以將這個框架當作 WSDL 抽象定義和它們的實現之間的“綁定( binding
)”。
當前,最流行的綁定( binding
)技術是使用簡單對象訪問協議(SOAP)。WSDL 將指定能夠訪問 Web 服務實際實現的 SOAP 服務器,並且從那時起 SOAP 的整個任務就是將用戶從 WSDL 文件帶到它的實現。SOAP 是本系列文章中下一部分的主題,所以我將暫時避免討論 SOAP 細節而繼續集中講述 WSDL 編寫。
WSDL 編寫的第三個步驟是描述將 SOAP 與 WSDL 文件綁定到一起的過程。您將把 <binding>
元素包括到 <definitions>
元素內。這個 binding 元素應該有name
和 type
屬性。 name
將標識這個綁定而 type
將標識您希望與這個綁定相關聯的 portType(一組操作)。在 清單 3 中,您會發現 <portType>
元素的name
與 <binding>
元素的 type 屬性值相匹配。
WSDL binding 元素包含您將用於綁定用途的外部技術的聲明。因爲正在使用 SOAP,所以這裏將使用 SOAP 的名稱空間。WSDL 術語中,對外部名稱空間的使用稱爲 extensibility
元素。
在 清單 3 中,您將看見一個空的 <soap:binding/>
元素。該元素的用途是聲明將把 SOAP 作爲綁定和傳輸服務使用。
<soap:binding>
元素有兩個屬性:style 和 transport。style 是一個可選屬性,它描述該綁定內操作的性質。transport 屬性指定 HTTP 作爲該綁定將使用的級別較低的傳輸服務。
SOAP 客戶機將從 WSDL 文件中讀取 SOAP 結構並與另一端的 SOAP 服務器協調,所以必須特別關注 interoperability
。我打算在本系列文章的第三部分詳細講述該問題。
在空的 <soap:binding/>
元素後面,有兩個 WSDL <operation>
元素,分別表示步驟 1 的操作。每個 <operation>
元素提供各自操作的綁定細節。因此,我提供了另一個 extensibility
元素,即 <soap:operation/>
(仍然是一個空元素,與它發生的那個操作相關)。該 <soap:operation/>
元素有一個 soapAction 屬性,SOAP 客戶機將使用該屬性創建 SOAP 請求。
請回憶步驟 2 中, getListOfModels
操作只有輸出而無任何輸入。因此,必須爲該操作提供一個 <output>
元素。該輸出包含 <soap:body/>
元素(仍然是一個空元素,與它發生的那個操作相關)。SOAP 客戶機需要該信息來創建 SOAP 請求。 <soap:body/>
的名稱空間屬性值應該與您將部署到 SOAP 服務器上的service
的名稱相對應,SOAP 服務器將在在本系列文章的下一部分中講述。
您已幾乎要完成步驟 3 了。只要將下一個操作複製到這個操作的後面,您將完成 清單 3。
您已經生成了一個完整描述服務 interface
的 WSDL 文件。現在,WSDL 需要一個附加步驟來創建該 WSDL 文件的概要。WSDL 將該文件稱爲implementation
文件,在本系列文章的第四部分中,當您在 UDDI 註冊中心發佈 Web 服務時,會使用它。請看 清單 4― 這個 WSDL 實現文件。它的主要特性如下:
- 除了
清單 4(實現文件)引用不同的targetNamespace
去引用實現文件以外,<definitions>
根元素和 清單 3(WSDL 接口文件)中的完全相同。 - 有一個
<import>
元素,該元素引用 清單 3的接口文件(文件名 MobilePhoneService-interface.wsdl)和它的名稱空間。 - 有一個
<service>
標記,其中有一個表示該服務的邏輯名name
。在 service 元素內有一個引用在 清單 3中創建的 SOAP 綁定的 port 元素。
|
將 IBM 的 Web Services ToolKit(WSTK)用於 WSDL 編寫
現在,Web 服務已經完全就緒用於部署。我已經展示瞭如何手工創建這些文件(使用象 emacs 這樣的簡單文本編輯器)。可以使用諸如 IBM 的 WSTK(請參閱 參考資料以獲得該工具箱以及本文提到的其它參考資料的鏈接)之類的 Web 服務編寫工具來生成相同的這些文件。
WSTK 可以使用嚮導幫助過程來生成這些文件。用戶可以生成與我在以上教程中演示的同樣兩種方法的 WSDL 文件,並將 WSTK 文件和 清單 3和 4中的 WSDL 文件作比較。
您將注意到下列差異:
- WSTK
依照邏輯規則創建了所有名稱屬性;在本示例中,我使用了自己視爲方便的名稱。 - WSTK 爲每個操作至少生成一個 input
標記,即使該操作不必獲得任何輸入。listAllPhoneModels
操作沒有任何 input 元素,但是如果使用 WSTK 生成相同文件,它將因爲包含這個方法的一個空 input 元素。 - WSTK 產生了除已生成的兩個文件以外的第三個文件。這第三個文件是
SOAP 引擎用於服務部署的 SOAP 部署描述符。我將在本系列文章中討論服務部署。
在這部分中,我演示了手工進行 WSDL 編寫以創建接口和實現文件,並與 IBM 的 Web Services ToolKit 生成的文件作了比較。在本系列的下一部分中,我將討論在 SOAP 服務器上部署這個 WSDL 服務。