SOAP 和 WSDL
我在本系列文章的 第 1 部分介紹了 WSDL。WSDL 描述了 Web 服務(wù)的接口。Web 服務(wù)所有者將用 SOAP 來(lái)實(shí)現(xiàn)他們的接口。因此, WSDL 服務(wù)實(shí)際上作為 SOAP 服務(wù)一樣存在。一旦 Web 服務(wù)用戶擁有 WSDL 文件,他或者她就知曉接口的細(xì)節(jié)。他或者她就會(huì)用 SOAP 來(lái)與 Web 服務(wù)通信。
可以把 Web 服務(wù)考慮為對(duì)象,可以通過(guò) WSDL 接口公開并且使用 SOAP 通過(guò)因特網(wǎng)遠(yuǎn)程訪問。既然服務(wù)是對(duì)象,那么肯定有每種服務(wù)的相關(guān)屬性和每種服務(wù)調(diào)用的行為。SOAP 消息是 XML 文檔,可通過(guò) HTTP 工作。
為什么用 SOAP?[color=darkred][/color]
B2B(Business-to-business)和 A2A(application-to-application )需求表明企業(yè)之間為交換信息而相互通信。這種概念被用在 B2B、工作流和跨企業(yè)集成中。例如,設(shè)想一條垂直供應(yīng)鏈,在鏈上一家企業(yè)為了滿足它的客戶需求而需要調(diào)用其提供者的服務(wù)。而一些提供者需要沿供應(yīng)鏈進(jìn)一步下行來(lái)調(diào)用其它企業(yè)的服務(wù)。
很明顯,在此應(yīng)用程序中互操作性是最為重要的。任何單個(gè)企業(yè)只能實(shí)現(xiàn) SOAP 通信通道的一端。另一端將是因特網(wǎng)上 任何地方的實(shí)體。
在最近幾年里,企業(yè)之間的集成和互操作性已經(jīng)成為軟件工程師和企業(yè)的一個(gè)挑戰(zhàn)性任務(wù)。平臺(tái)相關(guān)性也成為取得集成和互操作性的一個(gè)大問題。SOAP 依然是在企業(yè)間取得集成和互操作性最簡(jiǎn)單的機(jī)制。
SOAP 體系結(jié)構(gòu)
有了對(duì) SOAP 和它的用途的基本理解,我現(xiàn)在就展開對(duì)其體系結(jié)構(gòu)的討論以了解一些深層知識(shí)。請(qǐng)參閱 圖 1, 在此圖里面您可以識(shí)別典型 SOAP 通信體系結(jié)構(gòu)中的一些組件:
SOAP 客戶機(jī)
SOAP 服務(wù)器
實(shí)際服務(wù)
圖 1. 一個(gè)典型 SOAP 通信體系結(jié)構(gòu)的組件
讓我解釋上面所提到的每個(gè)實(shí)體的體系結(jié)構(gòu)角色。下面的討論參照 圖 1。
SOAP 客戶機(jī)
SOAP 客戶機(jī)是一臺(tái)有 SOAP 機(jī)制的機(jī)器,它可以產(chǎn)生 SOAP 請(qǐng)求并通過(guò) HTTP 發(fā)送到服務(wù)器。一條 SOAP 請(qǐng)求是一種類型的 SOAP 消息,通常只有兩種類型的 SOAP 消息:一條 SOAP 請(qǐng)求就是一臺(tái) SOAP 客戶機(jī)發(fā)送給 SOAP 服務(wù)器的內(nèi)容,一條 SOAP 響應(yīng)就是 SOAP 服務(wù)器對(duì) SOAP 客戶機(jī)響應(yīng)的內(nèi)容。 清單 1是典型的 SOAP 請(qǐng)求,請(qǐng)參閱 清單 2來(lái)回顧 SOAP 響應(yīng)。
清單 1:一條簡(jiǎn)單的 SOAP 請(qǐng)求
<SOAP-ENV:Envelope? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >
?? <SOAP-ENV:Body>
??? <m:getListOfModels xmlns:m = "uri reference" >
??? </m:getListOfModels>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP 服務(wù)器
SOAP 服務(wù)器也是一臺(tái)有 SOAP 機(jī)制的機(jī)器,能夠接收來(lái)自 SOAP 客戶機(jī)的請(qǐng)求,并對(duì)之作出適當(dāng)?shù)捻憫?yīng)。這些編過(guò)碼的響應(yīng)會(huì)返回發(fā)出請(qǐng)求的 SOAP 客戶機(jī)。在 SOAP 服務(wù)器內(nèi)部有三個(gè)實(shí)體:
服務(wù)管理器
被部署服務(wù)的列表
XML 轉(zhuǎn)換程序
服務(wù)管理器負(fù)責(zé)根據(jù)請(qǐng)求管理服務(wù)。請(qǐng)參閱 清單 1 的服務(wù)請(qǐng)求,在這里元素 <m:getListOfModels xmlns:m="urn:MobilePhoneservice" > 包含了服務(wù)的名稱。服務(wù)管理器會(huì)讀取 SOAP 客戶機(jī)想調(diào)用的 SOAP 服務(wù)的名稱并檢查所需的服務(wù)實(shí)際上是否駐留于這臺(tái) SOAP 服務(wù)器上。此后,它會(huì)查詢被部署服務(wù)的列表(SOAP 服務(wù)器所托管的所有服務(wù)的列表)。若存在,服務(wù)管理器將把 SOAP 請(qǐng)求傳送給 XML 轉(zhuǎn)換程序。XML 轉(zhuǎn)換程序就負(fù)責(zé)將 SOAP 請(qǐng)求的 XML 結(jié)構(gòu)轉(zhuǎn)換成程序員用來(lái)實(shí)現(xiàn)實(shí)際服務(wù)的編程語(yǔ)言(例如,Java 編程語(yǔ)言)的結(jié)構(gòu)。還要負(fù)責(zé)將來(lái)自實(shí)際服務(wù)的響應(yīng)轉(zhuǎn)換回 SOAP 響應(yīng)的 XML 結(jié)構(gòu)。請(qǐng)參閱 清單 2獲得 SOAP 響應(yīng)的說(shuō)明。
清單 2:一條簡(jiǎn)單的 SOAP 響應(yīng)
<SOAP-ENV:Envelope? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
?? <SOAP-ENV:Body>
??? <m:getListOfModelsResponse xmlns:m="urn:MobilePhoneservice">
??????? <Model>M1</Model>
??????? <Model>M2</Model>
??????? <Model>M3</Model>
??? </m:getPriceResponse>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
實(shí)際服務(wù)
圖 1中標(biāo)有 actual service的框就是實(shí)際服務(wù)駐留的位置。服務(wù)實(shí)現(xiàn)可以是:例如,COM 組件或 JavaBeans 組件的形式。XML 轉(zhuǎn)換程序負(fù)責(zé)將 XML 結(jié)構(gòu)轉(zhuǎn)換成合適的方法調(diào)用。當(dāng) XML 轉(zhuǎn)換程序調(diào)用了實(shí)際服務(wù)實(shí)現(xiàn)的某個(gè)方法時(shí),這個(gè)方法就會(huì)完成它的工作并且將結(jié)果信息返回 XML 轉(zhuǎn)換程序。
請(qǐng)看一看 圖 1中連接 XML translator 和 actual service 的箭頭。箭頭的兩端同在一個(gè)企業(yè)內(nèi),這意味著同一個(gè)組織控制著通信兩端的接口。與穿過(guò)企業(yè)邊界的在 SOAP 客戶機(jī)和 SOAP 服務(wù)器之間的箭頭相比,這正是 SOAP 的目的所在。
SOAP 請(qǐng)求響應(yīng)機(jī)制
當(dāng) SOAP 客戶機(jī)向 SOAP 服務(wù)器發(fā)送 SOAP 消息時(shí),用 HTTP 協(xié)議傳輸。這就叫做 SOAP 與 HTTP 綁定。當(dāng) SOAP 服務(wù)器收到消息時(shí),將消息交給服務(wù)管理器。服務(wù)管理器檢查被部署服務(wù)的列表,查找在 SOAP 消息中所需的服務(wù)。若沒有查找到所請(qǐng)求的服務(wù),它將請(qǐng)求失敗返回給 SOAP 客戶機(jī)。但是若此項(xiàng)服務(wù)可以提供,控制權(quán)由服務(wù)管理器轉(zhuǎn)移給 XML 轉(zhuǎn)換程序(轉(zhuǎn)換程序完成合適語(yǔ)言的轉(zhuǎn)換并訪問實(shí)際服務(wù)實(shí)現(xiàn))。服務(wù)實(shí)現(xiàn)會(huì)處理請(qǐng)求并將結(jié)果返回給 XML 轉(zhuǎn)換程序。XML 轉(zhuǎn)換程序?qū)⒔Y(jié)果轉(zhuǎn)換成 SOAP 客戶機(jī)能夠理解的 SOAP 響應(yīng)(XML 文檔)。然后又一次用 HTTP 綁定來(lái)傳輸 SOAP 響應(yīng)。現(xiàn)在讓我們看一下 SOAP 與 HTTP 的綁定細(xì)節(jié)。
SOAP 與 HTTP 綁定
當(dāng)您將 SOAP 和 HTTP 綁定在一起或在 HTTP 上操作 SOAP 時(shí),您實(shí)際上將 HTTP 報(bào)頭加到了 SOAP 請(qǐng)求和響應(yīng)上了。 清單 1是典型 SOAP 請(qǐng)求的結(jié)構(gòu),而清單 3、 4、 5和 6都是完整的 HTTP 請(qǐng)求,用來(lái)演示如何將 HTTP 報(bào)頭添加到 清單 1上。相似地, 清單 7是一條完整的 HTTP 響應(yīng),針對(duì)于來(lái)自 清單 2的 SOAP 響應(yīng)。
無(wú)論您何時(shí)在 HTTP 上使用 SOAP,Content-Type 字段必須是 text/xml。現(xiàn)在您可以察看 清單 3到 清單 7的詳情。
使用 HTTP 的 SOAP 請(qǐng)求
您可以將 SOAP 和 HTTP 的 POST請(qǐng)求方法連用。為了發(fā)送一條 SOAP HTTP 請(qǐng)求,您需要在 HTTP 中提供一個(gè) SOAPAction 報(bào)頭字段。 SOAPAction 指定了 SOAP 請(qǐng)求的目的。服務(wù)器(例如過(guò)濾 HTTP 中 SOAP 請(qǐng)求消息的防火墻)可以用字段 SOAPAction 的值來(lái)做決定。
HTTP 客戶機(jī)在發(fā)送一條 SOAP HTTP 請(qǐng)求時(shí)必須用此報(bào)頭字段。SOAPAction 可以有如下幾種值:SOAPAction:"URI-Reference"
SOAPAction:"filename"
SOAPAction:""
SOAPAction:
清單 3:演示 SOAPAction 報(bào)頭字段中的 URI 引用
POST /Vendors HTTP/1.1
Host: www.mobilephoneservice.com
Content-Type:"text/xml";Charset="utf-8"
Content-Length: nnnn
SOAPACtion:"www.mobilephoneservice.com/Vendors/MobilePhoneservice#getListOfModels"
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"? >
??? <SOAP-ENV:Body>
??????? <m:getListOfModels xmlns:m="urn:MobilePhoneservice" >
??????? </m:getListOfModels>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 3 在 SOAPAction 中包括如下 URI 引用: www.mobilephoneservice.com/Vendors/MobilePhoneservice#getListOfModels
這個(gè) SOAPAction 展示了兩部分內(nèi)容。第一部分是一個(gè)特別 SOAP 部署的地址: www.mobilephoneservice.com/Vendors/MobilePhoneservice
第二部分是一個(gè)片段標(biāo)識(shí)符,它給出了我們感興趣的方法的名字(#getListOfModels)。
清單 4:演示 SOAPAction 報(bào)頭字段中的一個(gè)文件名
POST /Vendors HTTP/1.1
Host: www.mobilephoneservice.com
Content-Type:"text/xml";Charset="utf-8"
Content-Length: nnnn
SOAPAction:"MobilePhoneservice#getListOfModels"
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"? >
??? <SOAP-ENV:Body>
??????? <m:getListOfModels xmlns:m="urn:MobilePhoneservice" >
??????? </m:getListOfModels>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 4 在 SOAPAction 中包含一個(gè)文件名( MobilePhoneservice#getListOfModels )。 MobilePhoneservice 文件必須出現(xiàn)在主機(jī) URI( www.mobilephoneservice.com/Vendors )中。 這個(gè)主機(jī) URI 是在 HTTP 報(bào)頭中 host 字段( www.mobilephoneservice.com )和文件夾名( /Vendors )的結(jié)合。
清單 5:演示 SOAPAction 報(bào)頭中的空字符串
POST /Vendors HTTP/1.1
Host: www.mobilephoneservice.com
Content-Type:"text/xml";Charset="utf-8"
Content-Length: nnnn
SOAPAction:""
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"? >
??? <SOAP-ENV:Body>
??????? <m:getListOfModels xmlns:m="urn:MobilePhoneservice" >
??????? </m:getListOfModels>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 5 在 SOAPAction 中包含一個(gè)空字符串("")。空字符串值表明 SOAP 的目的和 Host URI( www.mobilephoneservice.com/Vendors )的目的是一樣的。
清單 6:演示無(wú)值 SOAPAction 報(bào)頭
POST /Vendors HTTP/1.1
Host: www.mobilephoneservice.com
Content-Type:"text/xml";Charset="utf-8"
Content-Length: nnnn
SOAPAction:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"? >
??? <SOAP-ENV:Body>
??????? <m:getListOfModels xmlns:m ="urn:MobilePhoneservice" >
??????? </m:getListOfModels>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 6沒有包含 SOAPAction 值。這表明沒有關(guān)于消息目的的信息。
用 HTTP 的 SOAP 響應(yīng)
響應(yīng)將可能是兩種類型的 SOAP 響應(yīng)中的一種:
一個(gè)成功的 SOAP 操作產(chǎn)生 SOAP 結(jié)果
一個(gè)不成功的 SOAP 操作產(chǎn)生一條 SOAP 錯(cuò)誤消息
清單 7:一條帶有 HTTP 報(bào)頭的成功 SOAP 響應(yīng)
HTTP/1.1 Content-Type:"text/xml"; Charset="utf-8"
Content-Length: nnnn
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >
<SOAP-ENV:Body>
??? <m:getListOfModelsResponse xmlns:m = "URI-Reference">
??????? <model>m1</model>
??????? <model>m2</model>
??? </m:getListOfModels>
</SOAP-ENV:Body>
清單 7是第一種情況,在此可以從 SOAP 服務(wù)器取得有意義的結(jié)果。
清單 8是一條典型的 SOAP 錯(cuò)誤消息。SOAP HTTP 響應(yīng)遵循 HTTP 中通信狀態(tài)信息的 HTTP 狀態(tài)碼的語(yǔ)義。若在處理一條請(qǐng)求時(shí)發(fā)生一個(gè) SOAP 錯(cuò)誤,SOAP HTTP 服務(wù)器必須發(fā)出一條 HTTP 500 "Internal Server Error" 響應(yīng),同時(shí)在響應(yīng)中包括一條帶有 SOAP 出錯(cuò)元素的 SOAP 消息。
清單 8:一條帶有 HTTP 報(bào)頭的典型 SOAP 錯(cuò)誤消息
HTTP/1.1 500 Internal Server Error
Content-Type: "text/xml"; Charset="utf-8"
Content-Length: nnnn
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >
??? <SOAP-ENV:Body>
??????? <SOAP-ENV:Fault>
??????? <faultstring>Failed to process the request</faultstring>
??????? </SOAP-ENV:Fault>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
使用電子郵件的 SOAP
HTTP 不是唯一綁定 SOAP 消息的解決方案。若 HTTP 不合適,您可以用諸如 SMTP 的其它機(jī)制來(lái)用于 SOAP 綁定。將 SOAP 和 SMTP 綁定,您可以建立一條單向傳輸路由。兩條單向消息可以用來(lái)建立請(qǐng)求/響應(yīng)通信。 用 SMTP 來(lái)發(fā)送一條 SOAP 消息,您需要遵從以下步驟:
使用 MIME-Version 報(bào)頭字段
MIME-Version用一個(gè)版本號(hào)來(lái)區(qū)別不同的 MIME 版本。它應(yīng)用郵件處理代理(例如一個(gè) POP 服務(wù)器)來(lái)區(qū)別舊版本和新版本所生成的郵件消息。請(qǐng)參閱 清單 9,它使用了一個(gè) MIME-Version 報(bào)頭字段。
清單 9:一個(gè)使用電子郵件的 SOAP 示例
TO: <info@waxsys.com>
From: <abc@punjab.com>
Reply-To: <abc@punjab.com>
Date: SAT, 2 Feb 2002 16:00:00
Message-Id: <4FAB345C8D93E93B7A6E9@punjab.com> MIME-Version: 1.0
Content-Type: text/xml; charset=utf-8
Content-Transfer-Encoding: QUOTED-PRINTABLE
<?xml version ="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
??? <SOAP-ENV:Body>
??????? <prnt:echoString xmlns:prnt="http://waxsys.com">
??????????? <msgString>Put your mail Message</msgString>
??????? </prnt:echoString>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
使用 Content-Type 報(bào)頭字段:
Content-Type用來(lái)標(biāo)識(shí)消息主體中的數(shù)據(jù)類型。對(duì)于 SOAP 消息 Content-Type 應(yīng)該有一個(gè)值“text/xml”。請(qǐng)參閱 清單 9,它使用了 Content-Type。
使用 Content-Transfer-Encoding 字段:
Content-Transfer-Encoding 用來(lái)指定傳輸編碼的類型,也就是您所要傳輸?shù)臄?shù)據(jù)是字符格式還是二進(jìn)制格式。 清單 9使用 Quoted-Printable 編碼,這種編碼符合依照 ASCII 字符集的可打印字符。這種對(duì)數(shù)據(jù)的編碼方式使郵件傳輸代理不可能修改結(jié)果八位元。請(qǐng)參閱 清單 9,它使用了 Content-Transfer-Encoding 。
SOAP 模式與實(shí)現(xiàn)
SOAP 消息
一條 SOAP 消息只是一個(gè) XML 文檔,由一個(gè)強(qiáng)制性的 SOAP Envelope 組成,SOAP Envelope 有一個(gè)可選的 SOAP Header 和一個(gè)必須有的 SOAP Body。
SOAP 模式的元素:
Envelope
Header
Body
Fault
Envelope:
Envelope 是表示一條 SOAP 消息的頂層元素。為了發(fā)送一條 SOAP 消息,必須包括此元素。Envelope 使用必要的 SOAP 名稱空間標(biāo)識(shí)符( http://schemas.xmlsoap.org/soap/envelope/ )。若 Envelope 包含了錯(cuò)誤的名稱空間,會(huì)產(chǎn)生一個(gè)關(guān)于 Envelope 名稱空間版本的錯(cuò)誤。 清單 10是一個(gè)空 Envelope。稱其為“空 Envelope”是為了強(qiáng)調(diào)在通過(guò)“投遞”發(fā)出它之前,它最終應(yīng)該包含一封“信”(也許是商業(yè)信)。SOAP 模式中的“信”就是指“SOAP Body”,HTTP POST(在 HTTP 與 SOAP 的綁定一部分討論過(guò))就是傳輸機(jī)制。
清單 10:一個(gè)空 SOAP Envelope
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
</SOAP-ENV:Envelope>
Header:
SOAP Header 是可選的。您可以直接將 SOAP Body 放到 SOAP Envelope 中并完全忽略報(bào)頭。報(bào)頭提供了一個(gè)擴(kuò)展 SOAP 消息功能的機(jī)制。例如,認(rèn)證就是由 SOAP Header 條目所提供的一種典型擴(kuò)展。在此情況下,將有一個(gè)認(rèn)證框架,它會(huì)使用 SOAP 作為更低級(jí)別的傳輸。請(qǐng)參閱 清單 11來(lái)查看在 SOAP 中的報(bào)頭實(shí)現(xiàn)。
清單 11:在一個(gè) SOAP Envelope 中的報(bào)頭實(shí)現(xiàn)
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
??? <m:Order xmlns:m="some URI" SOAP-ENV:mustUnderstand="1">
??? </m:Order>
</SOAP-ENV:Header>
</SOAP-ENV:Envelope>
Body:
Body 元素包含您實(shí)際要發(fā)送的消息。它是一個(gè)強(qiáng)制性的元素且其子元素通常屬于一個(gè)用戶定義的名稱空間。 清單 12 展示了一條引用一個(gè)用戶定義的名稱空間 “u” 的 SOAP 消息。Body 元素是必要信息的容器。這個(gè)元素必須在 SOAP 消息中出現(xiàn)并且必須是 SOAP Envelope 元素的一個(gè)直接子元素。它也必須直接跟在 SOAP Header 元素的后面。若沒有 Header 元素,那么它應(yīng)直接跟在 Envelope 元素的后面。主體可以包含子元素并且子元素可能是受限于名稱空間的。
清單 12: SOAP Envelope 內(nèi)有 Header,還有 Body
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
??? <m:Order xmlns:m="some URI" SOAP-ENV:mustUnderstand="1">
??? </m:Order>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
??? <u:GetPrice xmlns:u="some URI" >
??????? <model>m1</model>
??? </u:GetPrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Fault:
這個(gè)元素表明一條錯(cuò)誤消息。它應(yīng)作為一個(gè)主體條目出現(xiàn)并且不能在 Body 元素中出現(xiàn)一次以上。通常,F(xiàn)ault 元素會(huì)在一條 SOAP 響應(yīng)消息中出現(xiàn),以表明在 SOAP 請(qǐng)求中出現(xiàn)錯(cuò)誤。
Fault 的子元素:
faultcode (錯(cuò)誤的標(biāo)識(shí))
faultstring (錯(cuò)誤的描述)
faultactor (標(biāo)識(shí)由誰(shuí)導(dǎo)致的錯(cuò)誤)
detail (錯(cuò)誤細(xì)節(jié)。通常是一個(gè)應(yīng)用程序特定錯(cuò)誤,也就是說(shuō),它相當(dāng)于在 SOAP 請(qǐng)求主體中用到地用戶定義的名稱空間)
清單 13是一條典型的錯(cuò)誤消息。
清單 13: 當(dāng)應(yīng)用程序出現(xiàn)錯(cuò)誤時(shí),SOAP Fault 的使用
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<m:Order xmlns:m="some URI" SOAP-ENV:mustUnderstand="1">
</m:Order>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Not necessary information</faultstring>
<detail>
??? <d:faultdetail xmlns:d = "uri-referrence">
??????? <msg>
??????????? application is not responding properly.
??????? </msg>
??????? <errorcode>12</errorcode>
??? </d:faultdetail>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
來(lái)自第 1 部分的一條對(duì) WSDL 文件的 SOAP 請(qǐng)求
已經(jīng)解釋了 SOAP 消息(請(qǐng)求和響應(yīng))的常規(guī)語(yǔ)法,我將展示如何對(duì)本系列 第 1 部分中的 MobilePhoneservice 開發(fā)一條 SOAP 請(qǐng)求。在第 1 部分中您設(shè)計(jì)一個(gè)完整的 WSDL 接口來(lái)解釋 MobilPhoneservice。移動(dòng)公司在 MobilePhoneservice 中提供了兩種方法,一種是 getListOfModels() ,另一種是 getPrice(modelNumber) 。 GetListOfModels() 沒有參數(shù)但是返回手機(jī)型號(hào)的一張列表,而 getPrice(modelNumber) 有一個(gè)參數(shù) modelNumber 并返回需求型號(hào)的 price 。您將用 SOAP 請(qǐng)求格式對(duì)它作成文檔,但是首先讓我展示給您一般的 SOAP 請(qǐng)求和響應(yīng)格式。
清單 14:SOAP 請(qǐng)求的一般格式
<SOAP-ENV:Envelope xmlns:SOAP-ENV ="SOAP schema's URI"
<SOAP-ENV:Body>
??? <Instance:"Method Name" xmlns:Instance= "URI where method is located">
??????? <parameter1>value</parameter1>
??????? <parametern>value</parametern>
??? </Instance:"Method Name">
</SOAP_Envelop:Body>
</SOAP-ENV:Envelope>
一條簡(jiǎn)單的 SOAP 請(qǐng)求或響應(yīng)只能表明一種服務(wù)的一個(gè)方法。包含一條 SOAP 請(qǐng)求的 Envelope 的一般格式遵從 清單 14。將這種一般格式與 清單 16 中的 getListOfModels() 的方法調(diào)用請(qǐng)求比較。在清單 16 中,我已經(jīng)提供了方法和 URI 的名稱。既然在 getListOfModels() 中不需要參數(shù),所以 <m:getListOfModels> 在 清單 16中是一個(gè)空元素。
清單 15:一條 SOAP 響應(yīng)的一般格式
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
??? <Instance:"Method Name"+"Response"
??? xmlns:Instance="URI where method is located">
??? <return>
??????? <responseparameter1>value</responseparameter1>
??????? <responseparametern>value</responseparametern>
??? </return>
??? </Instance: "Method Name"+"Response">
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 15 是一條一般的 SOAP 響應(yīng)。Apache SOAP 服務(wù)器在方法名稱的后面增加了"Response"關(guān)鍵字并將返回值封入元素 <return> 中作為一個(gè)直接子方法元素。若返回值是復(fù)合型結(jié)構(gòu),那么 <return> 元素包含一個(gè)或多個(gè) <item> 元素。將 清單 15與 清單 17 相比,清單 17 是來(lái)自 getListOfModels() 的實(shí)際響應(yīng)。 清單 17包含一系列項(xiàng)目,作為 Vector 數(shù)據(jù)類型,它是返回參數(shù)。 相似地, 清單 18和 19 展示了針對(duì) MobilePhoneservice 的方法 getPrice() 的 SOAP 請(qǐng)求和響應(yīng)。
清單 16:調(diào)用 getListOfModels() 方法的 SOAP 請(qǐng)求
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
??? <m:getListOfModels xmlns:m = "www.mobilphoneservice.com" >
??? </m:getListOfModels>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 17:針對(duì)于來(lái)自清單 16 請(qǐng)求的 SOAP 響應(yīng)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
??? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
??? xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
??? xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
??? <ns1:getListOfModelsResponse xmlns:ns1="urn:MobilePhoneservice"
??? SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
??? <return xmlns:ns2="http://xml.apache.org/xml-soap"
??????? xsi:type="ns2:Vector">
??????? <item xsi:type="xsd:string">M1</item>
??????? <item xsi:type="xsd:string">M2</item>
??????? <item xsi:type="xsd:string">M3</item>
??????? <item xsi:type="xsd:string">M4</item>
??????? <item xsi:type="xsd:string">M5</item>
??? </return>
??? </ns1:getListOfModelsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 18:對(duì)于 getPrice 方法的 SOAP 請(qǐng)求
<SOAP-ENV:Envelope
??? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
??? xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
??? xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
??? <m:getPrice xmlns:m ="www.mobilphoneservice.com">
??????? <modelNumber xsi:type ="xsd:String">M1</modelNumber>
??? </m:getPrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 19:對(duì)于來(lái)自清單 18 請(qǐng)求的 SOAP 響應(yīng)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
??? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
??? xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
??? xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
??? <ns1:getPriceResponse xmlns:ns1="urn:MobilePhoneservice"
??????? SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
??????????? <return xsi:type="xsd:string"> 5000 </return>
??? </ns1:getPriceResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
在 SOAP 服務(wù)器上部署基于 WSDL 的服務(wù)
在此部分您將在 Apache SOAP 服務(wù)器上部署來(lái)自第 1 部分的 WSDL 服務(wù)。Apache SOAP 工具箱將 WSDL 服務(wù)信息保存在一個(gè)部署描述符文件里面。部署描述符包含了 WSDL 服務(wù)的名稱和它擁有的所有方法。在運(yùn)行時(shí)部署描述符會(huì)將這些名稱提供給 SOAP 服務(wù)器。同樣的部署描述符文件還包含了實(shí)現(xiàn)接口的 JavaBean 組件的地址。
清單 20:一個(gè)部署描述符的框架
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
??? id="URN:SERVICE-URN">
<isd:provider type="java"
??? scope="Request"
??? methods="EXPOSED-METHODS">
??? <isd:java class="IMPLEMENTING-CLASS"/>
</isd:provider>
<isd:faultListener>org.apache.soap.server.DOMFaultListener
</isd:faultListener>
</isd:service>
清單 20是一個(gè)部署描述符的框架,為了作為基于 WSDL 服務(wù)的部署描述符使用,它需要三項(xiàng)信息( URN:SERVICE-URN、EXPOSED-METHODS和 IMPLEMENTING-CLASS)。 URN:SERVICE-URN 是被部署服務(wù)的名稱。在此例中它是 “urn:MobilePhoneservice” 。 EXPOSED-METHODS 是一個(gè)單空格分隔的由服務(wù)提供的方法的列表。 在此部署中它是 getListOfModels getPrice 。
IMPLEMENTING-CLASS 是帶有全路徑的 Java 類名稱。例如, samples.phonequote.MobilePhoneservice 。 在此例中測(cè)試應(yīng)用程序時(shí),您有如下目錄結(jié)構(gòu):
Apache SOAP 服務(wù)器: C:\foo\SOAP-2_2
Mobile phone 服務(wù)實(shí)現(xiàn):
C:\foo\SOAP-2_2\samples\phonequote\MobilePhoneservice
因此,IMPLEMENTING-CLASS 路徑請(qǐng)參照您安裝 SOAP 工具箱的目錄。 我沒有提供 Java 類的實(shí)際實(shí)現(xiàn)。 它取決于業(yè)務(wù)邏輯并且可以是任何東西。
清單 21:MobilePhoneservice 的部署描述符
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:MobilePhoneservice">
<isd:provider type="java"
??? scope="Request"
??? methods="getListOfModels getPrice">
??? <isd:java class="samples.phonequote.MobilePhoneservice"/>
</isd:provider>
<isd:faultListener>
??? org.apache.soap.server.DOMFaultListener
</isd:faultListener>
</isd:service>
清單 21是來(lái)自第 1 部分對(duì) WSDL 文件的完整部署描述符。
SOAP 客戶機(jī)與 SOAP 服務(wù)器的通信
我已經(jīng)提供過(guò)一個(gè)應(yīng)用程序樣本來(lái)演示一臺(tái) SOAP 客戶機(jī)與一臺(tái) SOAP 服務(wù)器的通信。 為此我給過(guò)三個(gè)列表:Startup.html( 清單 22)、Operation.html( 清單 23)和 Execute.jsp( 清單 24)。
StartUp.html( 清單 22)是一個(gè)簡(jiǎn)單的 HTML 文件,提供給用戶一個(gè) GUI 并詢問他將要調(diào)用哪一個(gè) SOAP 方法。用戶會(huì)選擇一個(gè)他需要的方法。
清單 22:一個(gè)作為前端的簡(jiǎn)單 HTML 頁(yè)
<HTML>
<BODY bgcolor="Teal">
<br/>
<p align="center">
??? <font size="5" face="Arial" color="white"><b>
??? SOAP method invocation demo </b></font>
</p>
<hr/>
<font face="Arial" color="whitesmoke" size="3">
<br/><b>
??? Click any of the method name to execute.<br/>
??? 1. Get the List of all Models that we manufacture....
??????? <a href="execute.jsp?index=1">
??????? <font color="orange"> GetListOfModels </font></a> <br/>
??? 2. Get the Price of any particular model......................
??????? <a href="operation.html">
??????? <font color="orange"> GetPrice </font></a>
</b>
</BODY>
</HTML>
Operation.html( 清單 23)將詢問客戶提供方法調(diào)用所需的參數(shù)。
清單 23:根據(jù)他或她所選擇的方法給予客戶一個(gè) GUI
<HTML>
<BODY bgcolor="Teal">
<br/>
<p align="center">
<font size="5" face="Arial" color="white"><b>
??? GetPrice Operation input Form </b>
</font></p>
<hr/>
<p align="center">
<form action="execute.jsp" method="POST">
<input type="hidden" name="index" value="0">
<table textColor="white">
<tr><td>
<font color="whitesmoke"><b>Description :</b></font>
</td><td><font color="whitesmoke">
??? Method GetPrice is used to Get Price of given Model Number</font>
</td></tr>
<tr><td>
<font color="whitesmoke"><b>Parameter(s)</b></font></td><td>
</td></tr>
<tr><td><font color="whitesmoke">Model Number </td></font>
<td><font color="whitesmoke">
??? <input type="text" name="parameter" size="30">
??? (required) </font>
</td></tr>
<tr><td>
??? </td><td><input type="Submit" value="Invoke">
</td></tr>
</font>
</table>
</form>
</p>
</BODY>
</HTML>
Execute.jsp( 清單 24)包含了所有的令人感興趣的代碼。它檢測(cè)所調(diào)用的方法和所傳遞的參數(shù)。然后發(fā)送給遠(yuǎn)程服務(wù)器一個(gè)方法調(diào)用。
清單 24:檢測(cè)方法并發(fā)送給遠(yuǎn)程服務(wù)器一個(gè)調(diào)用
<%@ page language="java" import="java.util.Vector" %>
<%@ page import="java.net.MalformedURLException, java.net.URL" %>
<%@ page import="java.util.Vector" %>
<%@ page import="org.apache.soap.SOAPException,
??????? org.apache.soap.Constants" %>
<%@ page import="org.apache.soap.rpc.Call, org.apache.soap.rpc.Response,
??????? org.apache.soap.rpc.Parameter" %>
<%@ page import="org.apache.soap.transport.http.SOAPHTTPConnection" %>
<%@ page import="org.apache.soap.Fault" %>
<HTML>
<BODY bgcolor="Teal">
<br/>
<p align="center">
<font color="whitesmoke">
<%
??? boolean isParameter = false ;
??? SOAPHTTPConnection soapTransport = new SOAPHTTPConnection();
??? // Address of the remote server.
??? // Normally this should be dynamically passed and detected.
??? // We have hard coded it only for demonstration.
??? URL url = new URL ("http://localhost:8080/soap/servlet/rpcrouter");
??? // Build the call.
??? Call call = new Call ();
??? call.setTargetObjectURI ("urn:MobilePhoneservice");
??? call.setSOAPTransport (soapTransport);
??? call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
??? // We'll detect which method user selected
??? // and give a call accordingly.
??? // We'll pass parameters if present.
??? if (request.getParameter("parameter")!=null)
??? isParameter = true;
??? if (request.getParameter("index").equals("0"))
??? {
??????? call.setMethodName("getPrice");
??????? Vector params = new Vector();
??????? String message = new String (request.getParameter("parameter"));
??????? params.addElement (new Parameter("message", String.class,
??????? message , null));
??????? call.setParams(params);
??? }
??? else
??????? call.setMethodName("getListOfModels");
??? Response resp = call.invoke ( url, /* actionURI */ "" );
??? out.println("<p align=left>
??? <font size=\"4\" face=\"Arial\" color=\"white\">
??? Response of [ "+call.getMethodName()+" ]
??? </font><hr/>");
???
??? // Check the response.
??? if (resp.generatedFault ()) {
??????? Fault fault = resp.getFault ();
??????? out.println("<b>Fault is:</b>"+ fault.getFaultCode ()
??????? +" ["+fault.getFaultString ()+"]");
??? } else {
??????? Parameter result = resp.getReturnValue ();
??????? out.println("<b>Response is: </b>"+ result.getValue ()+"");
??? }
%>
<font>
</p>
</BODY>
</HTML>
為了運(yùn)行此應(yīng)用程序,您需要兩臺(tái) Apache SOAP 服務(wù)器。 一臺(tái)服務(wù)器將用來(lái)與用戶通信并托管 清單 22、 23和 24。 另一臺(tái)服務(wù)器(也稱為遠(yuǎn)程服務(wù)器)就是我們需要部署第 1 部分所講的基于 WSDL 服務(wù)的地方(在前一節(jié)描述,“ 在 SOAP 服務(wù)器上基于 WSDL 服務(wù)的部署”)。 僅僅是為了演示,遠(yuǎn)程服務(wù)器的地址 http://localhost:8080/soap/servlet/rpcrouter 已經(jīng)硬編碼在 Execute.jsp( 清單 24)中。在實(shí)際操作中您可以從 WSDL 文件中讀取它。
SOAP 中的簡(jiǎn)單與復(fù)合數(shù)據(jù)類型
在此節(jié)中,我將從解釋簡(jiǎn)單與復(fù)合數(shù)據(jù)類型的不同開始。然后展示如何在 SOAP 中對(duì)它們編碼。
簡(jiǎn)單類型包括字符串、浮點(diǎn)數(shù)、整數(shù)、枚舉等。 例如一部手機(jī)的“name”的數(shù)據(jù)類型就是 “string” 。 復(fù)合類型由簡(jiǎn)單類型組成但只代表一個(gè)實(shí)體。例如, “Student” 類型記錄可以有不同的屬性,如 “studentName” 屬于類型 “string” , “studentRollNumber” 屬于類型 “int” 但都只代表一個(gè)實(shí)體 “Student” 。
清單 25 包含了一個(gè)名稱為 “Mobile” 的復(fù)合數(shù)據(jù)類型。 您會(huì)在后面的 SOAP 請(qǐng)求中用到。
清單 25: “Mobile”類型的模式定義結(jié)構(gòu)
1<? xml version="1.0" ?>
2<xsd:schema xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
3?? xmlns:xsd="http://www.w3.org/1999/XMLSchema">
4?? targetNameSpace= "www.mobilephoneservice.com/phonequote">
5 <xsd:element name ="Mobile">
6 <xsd:complexType>
7?? <xsd:element name="modelNumber" type="xsd:int">
8?? <xsd:element name="modelName" type="xsd:string">
9?? <xsd:element name="modelWeight" type="xsd:int">
10? <xsd:element name="modelSize" type="xsd:int">
11? <xsd:element name="modelColor">
12????? <simpleType base="xsd:string">
13????? <enumeration value="white" />
14????? <enumeration value="blue" />
15????? <enumeration value="black" />
16????? <enumeration value="red" />
17????? <enumeration value="pink" />
18????? </simpleType>
19? </xsd:element>
20 </complexType>
21 </xsd:element>
22</xsd:schema>
在 清單 25 中的第 5 行展示了我們的類型名稱(Mobile),而第 6 行說(shuō)明它是復(fù)合數(shù)據(jù)類型。因復(fù)合數(shù)據(jù)類型有屬性,所以在第 7 行到第 12 行展示了定義為子元素的 “Mobile” 數(shù)據(jù)類型的屬性。
第 7 行聲明的元素展示了 “Mobile” 類型有一個(gè)名稱為 “modelNumber” 的屬性且其類型為 “int” (也就是說(shuō), “modelNumber” 只能采用整數(shù)值)。 類似的,第 9 行和第 10 行聲明的元素具有同樣的類型但有不同的屬性名稱。在第 8 行定義的元素具有名稱為 “modelName” 的屬性且其類型是 “string” 。
第 11 行的元素因有位于第 12 行的、名稱為 “simpleType” 的子元素,所以需要更好的理解。這里您在復(fù)合類型 Mobile 中定義了一個(gè)簡(jiǎn)單類型。simpleType 的名稱為 “modelColor” 且它的類型是 “enumeration” 。它有一個(gè)屬性 “base” 具有的值為 "xsd:string" ,這表明簡(jiǎn)單類型 “modelColor” 具有在 SOAP 模式中定義的類型 “string” 的功能。在第 13 行到第 17 行中的每一個(gè) <enumeration> 標(biāo)記都具有一個(gè)屬性: “value” ( "white" "blue" 、 "black" 、 "red" 和 "pink" )。 枚舉類型使我們能夠從多項(xiàng)選項(xiàng)中選擇一個(gè)值。
在 SOAP 請(qǐng)求中使用復(fù)合數(shù)據(jù)類型
清單 26 演示了在 SOAP 請(qǐng)求中復(fù)合類型的使用。 它展示了一個(gè)在 Body 元素中的攜帶請(qǐng)求的 Envelope,在 Body 元素中,您正調(diào)用 “m” 名稱空間的 addModel 方法。 清單 26 使用數(shù)據(jù)類型 “Mobile” ,此數(shù)據(jù)類型在 清單 25中定義。
AddModel 方法攜帶一個(gè)類型為 “Mobile” 的參數(shù)。我們以 “msd” 名稱空間引用來(lái)引用 “Mobile” 結(jié)構(gòu)。請(qǐng)參閱清單 26 的 <SOAP-ENV:Envelope> 元素中的 "xmlns:msd" 聲明。 這是一個(gè)在 SOAP 請(qǐng)求中使用用戶定義數(shù)據(jù)類型的示例。
清單 26:實(shí)現(xiàn)在清單 25 中定義的“Mobile”結(jié)構(gòu)
1 <SoAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
2 xmlns:xsd="http://www.w3.org/1999/XMLSchema"
3 xmlns:msd="www.mobilephoneservice.com/phonequote">
4 <SOAP-ENV:Body>
5?? <m:addModel xmlns:m="www.mobilephoneservice.com">
6?? <msd:Mobile>
7?????? <modelNumber>1</modelNumber>
8?????? <modelName>mlr97</modelName>
9?????? <modelWeight>10</modelWeight>
10????? <modelSize>4</modelSize>
11????? <modelColor>white</modelColor>
12? </msd:Mobile>
13? </m:addModel>
14 </SOAP-ENV:Body>
15<SOAP-ENV:Envelope>
總結(jié)
在這一部分中,您已經(jīng)學(xué)習(xí)了 SOAP 語(yǔ)法、請(qǐng)求、響應(yīng)、HTTP 綁定和使用電子郵件的 SOAP 使用。您也了解了服務(wù)于 Apache SOAP 客戶機(jī)的 Apache SOAP 服務(wù)器。最后,我簡(jiǎn)述了用戶定義數(shù)據(jù)類型的主題,它是一個(gè)需要我們仔細(xì)學(xué)習(xí)的高級(jí)主題。在此系列文章的下一部分,您會(huì)學(xué)習(xí)更多的用戶定義數(shù)據(jù)類型示例。我還會(huì)檢驗(yàn) SOAP 的互操作性(也就是說(shuō),怎樣使來(lái)自不同供應(yīng)商的 SOAP 實(shí)現(xiàn)相互協(xié)調(diào))。
我在本系列文章的 第 1 部分介紹了 WSDL。WSDL 描述了 Web 服務(wù)的接口。Web 服務(wù)所有者將用 SOAP 來(lái)實(shí)現(xiàn)他們的接口。因此, WSDL 服務(wù)實(shí)際上作為 SOAP 服務(wù)一樣存在。一旦 Web 服務(wù)用戶擁有 WSDL 文件,他或者她就知曉接口的細(xì)節(jié)。他或者她就會(huì)用 SOAP 來(lái)與 Web 服務(wù)通信。
可以把 Web 服務(wù)考慮為對(duì)象,可以通過(guò) WSDL 接口公開并且使用 SOAP 通過(guò)因特網(wǎng)遠(yuǎn)程訪問。既然服務(wù)是對(duì)象,那么肯定有每種服務(wù)的相關(guān)屬性和每種服務(wù)調(diào)用的行為。SOAP 消息是 XML 文檔,可通過(guò) HTTP 工作。
為什么用 SOAP?[color=darkred][/color]
B2B(Business-to-business)和 A2A(application-to-application )需求表明企業(yè)之間為交換信息而相互通信。這種概念被用在 B2B、工作流和跨企業(yè)集成中。例如,設(shè)想一條垂直供應(yīng)鏈,在鏈上一家企業(yè)為了滿足它的客戶需求而需要調(diào)用其提供者的服務(wù)。而一些提供者需要沿供應(yīng)鏈進(jìn)一步下行來(lái)調(diào)用其它企業(yè)的服務(wù)。
很明顯,在此應(yīng)用程序中互操作性是最為重要的。任何單個(gè)企業(yè)只能實(shí)現(xiàn) SOAP 通信通道的一端。另一端將是因特網(wǎng)上 任何地方的實(shí)體。
在最近幾年里,企業(yè)之間的集成和互操作性已經(jīng)成為軟件工程師和企業(yè)的一個(gè)挑戰(zhàn)性任務(wù)。平臺(tái)相關(guān)性也成為取得集成和互操作性的一個(gè)大問題。SOAP 依然是在企業(yè)間取得集成和互操作性最簡(jiǎn)單的機(jī)制。
SOAP 體系結(jié)構(gòu)
有了對(duì) SOAP 和它的用途的基本理解,我現(xiàn)在就展開對(duì)其體系結(jié)構(gòu)的討論以了解一些深層知識(shí)。請(qǐng)參閱 圖 1, 在此圖里面您可以識(shí)別典型 SOAP 通信體系結(jié)構(gòu)中的一些組件:
SOAP 客戶機(jī)
SOAP 服務(wù)器
實(shí)際服務(wù)
圖 1. 一個(gè)典型 SOAP 通信體系結(jié)構(gòu)的組件

讓我解釋上面所提到的每個(gè)實(shí)體的體系結(jié)構(gòu)角色。下面的討論參照 圖 1。
SOAP 客戶機(jī)
SOAP 客戶機(jī)是一臺(tái)有 SOAP 機(jī)制的機(jī)器,它可以產(chǎn)生 SOAP 請(qǐng)求并通過(guò) HTTP 發(fā)送到服務(wù)器。一條 SOAP 請(qǐng)求是一種類型的 SOAP 消息,通常只有兩種類型的 SOAP 消息:一條 SOAP 請(qǐng)求就是一臺(tái) SOAP 客戶機(jī)發(fā)送給 SOAP 服務(wù)器的內(nèi)容,一條 SOAP 響應(yīng)就是 SOAP 服務(wù)器對(duì) SOAP 客戶機(jī)響應(yīng)的內(nèi)容。 清單 1是典型的 SOAP 請(qǐng)求,請(qǐng)參閱 清單 2來(lái)回顧 SOAP 響應(yīng)。
清單 1:一條簡(jiǎn)單的 SOAP 請(qǐng)求
<SOAP-ENV:Envelope? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >
?? <SOAP-ENV:Body>
??? <m:getListOfModels xmlns:m = "uri reference" >
??? </m:getListOfModels>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP 服務(wù)器
SOAP 服務(wù)器也是一臺(tái)有 SOAP 機(jī)制的機(jī)器,能夠接收來(lái)自 SOAP 客戶機(jī)的請(qǐng)求,并對(duì)之作出適當(dāng)?shù)捻憫?yīng)。這些編過(guò)碼的響應(yīng)會(huì)返回發(fā)出請(qǐng)求的 SOAP 客戶機(jī)。在 SOAP 服務(wù)器內(nèi)部有三個(gè)實(shí)體:
服務(wù)管理器
被部署服務(wù)的列表
XML 轉(zhuǎn)換程序
服務(wù)管理器負(fù)責(zé)根據(jù)請(qǐng)求管理服務(wù)。請(qǐng)參閱 清單 1 的服務(wù)請(qǐng)求,在這里元素 <m:getListOfModels xmlns:m="urn:MobilePhoneservice" > 包含了服務(wù)的名稱。服務(wù)管理器會(huì)讀取 SOAP 客戶機(jī)想調(diào)用的 SOAP 服務(wù)的名稱并檢查所需的服務(wù)實(shí)際上是否駐留于這臺(tái) SOAP 服務(wù)器上。此后,它會(huì)查詢被部署服務(wù)的列表(SOAP 服務(wù)器所托管的所有服務(wù)的列表)。若存在,服務(wù)管理器將把 SOAP 請(qǐng)求傳送給 XML 轉(zhuǎn)換程序。XML 轉(zhuǎn)換程序就負(fù)責(zé)將 SOAP 請(qǐng)求的 XML 結(jié)構(gòu)轉(zhuǎn)換成程序員用來(lái)實(shí)現(xiàn)實(shí)際服務(wù)的編程語(yǔ)言(例如,Java 編程語(yǔ)言)的結(jié)構(gòu)。還要負(fù)責(zé)將來(lái)自實(shí)際服務(wù)的響應(yīng)轉(zhuǎn)換回 SOAP 響應(yīng)的 XML 結(jié)構(gòu)。請(qǐng)參閱 清單 2獲得 SOAP 響應(yīng)的說(shuō)明。
清單 2:一條簡(jiǎn)單的 SOAP 響應(yīng)
<SOAP-ENV:Envelope? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
?? <SOAP-ENV:Body>
??? <m:getListOfModelsResponse xmlns:m="urn:MobilePhoneservice">
??????? <Model>M1</Model>
??????? <Model>M2</Model>
??????? <Model>M3</Model>
??? </m:getPriceResponse>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
實(shí)際服務(wù)
圖 1中標(biāo)有 actual service的框就是實(shí)際服務(wù)駐留的位置。服務(wù)實(shí)現(xiàn)可以是:例如,COM 組件或 JavaBeans 組件的形式。XML 轉(zhuǎn)換程序負(fù)責(zé)將 XML 結(jié)構(gòu)轉(zhuǎn)換成合適的方法調(diào)用。當(dāng) XML 轉(zhuǎn)換程序調(diào)用了實(shí)際服務(wù)實(shí)現(xiàn)的某個(gè)方法時(shí),這個(gè)方法就會(huì)完成它的工作并且將結(jié)果信息返回 XML 轉(zhuǎn)換程序。
請(qǐng)看一看 圖 1中連接 XML translator 和 actual service 的箭頭。箭頭的兩端同在一個(gè)企業(yè)內(nèi),這意味著同一個(gè)組織控制著通信兩端的接口。與穿過(guò)企業(yè)邊界的在 SOAP 客戶機(jī)和 SOAP 服務(wù)器之間的箭頭相比,這正是 SOAP 的目的所在。
SOAP 請(qǐng)求響應(yīng)機(jī)制
當(dāng) SOAP 客戶機(jī)向 SOAP 服務(wù)器發(fā)送 SOAP 消息時(shí),用 HTTP 協(xié)議傳輸。這就叫做 SOAP 與 HTTP 綁定。當(dāng) SOAP 服務(wù)器收到消息時(shí),將消息交給服務(wù)管理器。服務(wù)管理器檢查被部署服務(wù)的列表,查找在 SOAP 消息中所需的服務(wù)。若沒有查找到所請(qǐng)求的服務(wù),它將請(qǐng)求失敗返回給 SOAP 客戶機(jī)。但是若此項(xiàng)服務(wù)可以提供,控制權(quán)由服務(wù)管理器轉(zhuǎn)移給 XML 轉(zhuǎn)換程序(轉(zhuǎn)換程序完成合適語(yǔ)言的轉(zhuǎn)換并訪問實(shí)際服務(wù)實(shí)現(xiàn))。服務(wù)實(shí)現(xiàn)會(huì)處理請(qǐng)求并將結(jié)果返回給 XML 轉(zhuǎn)換程序。XML 轉(zhuǎn)換程序?qū)⒔Y(jié)果轉(zhuǎn)換成 SOAP 客戶機(jī)能夠理解的 SOAP 響應(yīng)(XML 文檔)。然后又一次用 HTTP 綁定來(lái)傳輸 SOAP 響應(yīng)。現(xiàn)在讓我們看一下 SOAP 與 HTTP 的綁定細(xì)節(jié)。
SOAP 與 HTTP 綁定
當(dāng)您將 SOAP 和 HTTP 綁定在一起或在 HTTP 上操作 SOAP 時(shí),您實(shí)際上將 HTTP 報(bào)頭加到了 SOAP 請(qǐng)求和響應(yīng)上了。 清單 1是典型 SOAP 請(qǐng)求的結(jié)構(gòu),而清單 3、 4、 5和 6都是完整的 HTTP 請(qǐng)求,用來(lái)演示如何將 HTTP 報(bào)頭添加到 清單 1上。相似地, 清單 7是一條完整的 HTTP 響應(yīng),針對(duì)于來(lái)自 清單 2的 SOAP 響應(yīng)。
無(wú)論您何時(shí)在 HTTP 上使用 SOAP,Content-Type 字段必須是 text/xml。現(xiàn)在您可以察看 清單 3到 清單 7的詳情。
使用 HTTP 的 SOAP 請(qǐng)求
您可以將 SOAP 和 HTTP 的 POST請(qǐng)求方法連用。為了發(fā)送一條 SOAP HTTP 請(qǐng)求,您需要在 HTTP 中提供一個(gè) SOAPAction 報(bào)頭字段。 SOAPAction 指定了 SOAP 請(qǐng)求的目的。服務(wù)器(例如過(guò)濾 HTTP 中 SOAP 請(qǐng)求消息的防火墻)可以用字段 SOAPAction 的值來(lái)做決定。
HTTP 客戶機(jī)在發(fā)送一條 SOAP HTTP 請(qǐng)求時(shí)必須用此報(bào)頭字段。SOAPAction 可以有如下幾種值:SOAPAction:"URI-Reference"
SOAPAction:"filename"
SOAPAction:""
SOAPAction:
清單 3:演示 SOAPAction 報(bào)頭字段中的 URI 引用
POST /Vendors HTTP/1.1
Host: www.mobilephoneservice.com
Content-Type:"text/xml";Charset="utf-8"
Content-Length: nnnn
SOAPACtion:"www.mobilephoneservice.com/Vendors/MobilePhoneservice#getListOfModels"
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"? >
??? <SOAP-ENV:Body>
??????? <m:getListOfModels xmlns:m="urn:MobilePhoneservice" >
??????? </m:getListOfModels>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 3 在 SOAPAction 中包括如下 URI 引用: www.mobilephoneservice.com/Vendors/MobilePhoneservice#getListOfModels
這個(gè) SOAPAction 展示了兩部分內(nèi)容。第一部分是一個(gè)特別 SOAP 部署的地址: www.mobilephoneservice.com/Vendors/MobilePhoneservice
第二部分是一個(gè)片段標(biāo)識(shí)符,它給出了我們感興趣的方法的名字(#getListOfModels)。
清單 4:演示 SOAPAction 報(bào)頭字段中的一個(gè)文件名
POST /Vendors HTTP/1.1
Host: www.mobilephoneservice.com
Content-Type:"text/xml";Charset="utf-8"
Content-Length: nnnn
SOAPAction:"MobilePhoneservice#getListOfModels"
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"? >
??? <SOAP-ENV:Body>
??????? <m:getListOfModels xmlns:m="urn:MobilePhoneservice" >
??????? </m:getListOfModels>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 4 在 SOAPAction 中包含一個(gè)文件名( MobilePhoneservice#getListOfModels )。 MobilePhoneservice 文件必須出現(xiàn)在主機(jī) URI( www.mobilephoneservice.com/Vendors )中。 這個(gè)主機(jī) URI 是在 HTTP 報(bào)頭中 host 字段( www.mobilephoneservice.com )和文件夾名( /Vendors )的結(jié)合。
清單 5:演示 SOAPAction 報(bào)頭中的空字符串
POST /Vendors HTTP/1.1
Host: www.mobilephoneservice.com
Content-Type:"text/xml";Charset="utf-8"
Content-Length: nnnn
SOAPAction:""
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"? >
??? <SOAP-ENV:Body>
??????? <m:getListOfModels xmlns:m="urn:MobilePhoneservice" >
??????? </m:getListOfModels>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 5 在 SOAPAction 中包含一個(gè)空字符串("")。空字符串值表明 SOAP 的目的和 Host URI( www.mobilephoneservice.com/Vendors )的目的是一樣的。
清單 6:演示無(wú)值 SOAPAction 報(bào)頭
POST /Vendors HTTP/1.1
Host: www.mobilephoneservice.com
Content-Type:"text/xml";Charset="utf-8"
Content-Length: nnnn
SOAPAction:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"? >
??? <SOAP-ENV:Body>
??????? <m:getListOfModels xmlns:m ="urn:MobilePhoneservice" >
??????? </m:getListOfModels>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 6沒有包含 SOAPAction 值。這表明沒有關(guān)于消息目的的信息。
用 HTTP 的 SOAP 響應(yīng)
響應(yīng)將可能是兩種類型的 SOAP 響應(yīng)中的一種:
一個(gè)成功的 SOAP 操作產(chǎn)生 SOAP 結(jié)果
一個(gè)不成功的 SOAP 操作產(chǎn)生一條 SOAP 錯(cuò)誤消息
清單 7:一條帶有 HTTP 報(bào)頭的成功 SOAP 響應(yīng)
HTTP/1.1 Content-Type:"text/xml"; Charset="utf-8"
Content-Length: nnnn
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >
<SOAP-ENV:Body>
??? <m:getListOfModelsResponse xmlns:m = "URI-Reference">
??????? <model>m1</model>
??????? <model>m2</model>
??? </m:getListOfModels>
</SOAP-ENV:Body>
清單 7是第一種情況,在此可以從 SOAP 服務(wù)器取得有意義的結(jié)果。
清單 8是一條典型的 SOAP 錯(cuò)誤消息。SOAP HTTP 響應(yīng)遵循 HTTP 中通信狀態(tài)信息的 HTTP 狀態(tài)碼的語(yǔ)義。若在處理一條請(qǐng)求時(shí)發(fā)生一個(gè) SOAP 錯(cuò)誤,SOAP HTTP 服務(wù)器必須發(fā)出一條 HTTP 500 "Internal Server Error" 響應(yīng),同時(shí)在響應(yīng)中包括一條帶有 SOAP 出錯(cuò)元素的 SOAP 消息。
清單 8:一條帶有 HTTP 報(bào)頭的典型 SOAP 錯(cuò)誤消息
HTTP/1.1 500 Internal Server Error
Content-Type: "text/xml"; Charset="utf-8"
Content-Length: nnnn
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >
??? <SOAP-ENV:Body>
??????? <SOAP-ENV:Fault>
??????? <faultstring>Failed to process the request</faultstring>
??????? </SOAP-ENV:Fault>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
使用電子郵件的 SOAP
HTTP 不是唯一綁定 SOAP 消息的解決方案。若 HTTP 不合適,您可以用諸如 SMTP 的其它機(jī)制來(lái)用于 SOAP 綁定。將 SOAP 和 SMTP 綁定,您可以建立一條單向傳輸路由。兩條單向消息可以用來(lái)建立請(qǐng)求/響應(yīng)通信。 用 SMTP 來(lái)發(fā)送一條 SOAP 消息,您需要遵從以下步驟:
使用 MIME-Version 報(bào)頭字段
MIME-Version用一個(gè)版本號(hào)來(lái)區(qū)別不同的 MIME 版本。它應(yīng)用郵件處理代理(例如一個(gè) POP 服務(wù)器)來(lái)區(qū)別舊版本和新版本所生成的郵件消息。請(qǐng)參閱 清單 9,它使用了一個(gè) MIME-Version 報(bào)頭字段。
清單 9:一個(gè)使用電子郵件的 SOAP 示例
TO: <info@waxsys.com>
From: <abc@punjab.com>
Reply-To: <abc@punjab.com>
Date: SAT, 2 Feb 2002 16:00:00
Message-Id: <4FAB345C8D93E93B7A6E9@punjab.com> MIME-Version: 1.0
Content-Type: text/xml; charset=utf-8
Content-Transfer-Encoding: QUOTED-PRINTABLE
<?xml version ="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
??? <SOAP-ENV:Body>
??????? <prnt:echoString xmlns:prnt="http://waxsys.com">
??????????? <msgString>Put your mail Message</msgString>
??????? </prnt:echoString>
??? </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
使用 Content-Type 報(bào)頭字段:
Content-Type用來(lái)標(biāo)識(shí)消息主體中的數(shù)據(jù)類型。對(duì)于 SOAP 消息 Content-Type 應(yīng)該有一個(gè)值“text/xml”。請(qǐng)參閱 清單 9,它使用了 Content-Type。
使用 Content-Transfer-Encoding 字段:
Content-Transfer-Encoding 用來(lái)指定傳輸編碼的類型,也就是您所要傳輸?shù)臄?shù)據(jù)是字符格式還是二進(jìn)制格式。 清單 9使用 Quoted-Printable 編碼,這種編碼符合依照 ASCII 字符集的可打印字符。這種對(duì)數(shù)據(jù)的編碼方式使郵件傳輸代理不可能修改結(jié)果八位元。請(qǐng)參閱 清單 9,它使用了 Content-Transfer-Encoding 。
SOAP 模式與實(shí)現(xiàn)
SOAP 消息
一條 SOAP 消息只是一個(gè) XML 文檔,由一個(gè)強(qiáng)制性的 SOAP Envelope 組成,SOAP Envelope 有一個(gè)可選的 SOAP Header 和一個(gè)必須有的 SOAP Body。
SOAP 模式的元素:
Envelope
Header
Body
Fault
Envelope:
Envelope 是表示一條 SOAP 消息的頂層元素。為了發(fā)送一條 SOAP 消息,必須包括此元素。Envelope 使用必要的 SOAP 名稱空間標(biāo)識(shí)符( http://schemas.xmlsoap.org/soap/envelope/ )。若 Envelope 包含了錯(cuò)誤的名稱空間,會(huì)產(chǎn)生一個(gè)關(guān)于 Envelope 名稱空間版本的錯(cuò)誤。 清單 10是一個(gè)空 Envelope。稱其為“空 Envelope”是為了強(qiáng)調(diào)在通過(guò)“投遞”發(fā)出它之前,它最終應(yīng)該包含一封“信”(也許是商業(yè)信)。SOAP 模式中的“信”就是指“SOAP Body”,HTTP POST(在 HTTP 與 SOAP 的綁定一部分討論過(guò))就是傳輸機(jī)制。
清單 10:一個(gè)空 SOAP Envelope
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
</SOAP-ENV:Envelope>
Header:
SOAP Header 是可選的。您可以直接將 SOAP Body 放到 SOAP Envelope 中并完全忽略報(bào)頭。報(bào)頭提供了一個(gè)擴(kuò)展 SOAP 消息功能的機(jī)制。例如,認(rèn)證就是由 SOAP Header 條目所提供的一種典型擴(kuò)展。在此情況下,將有一個(gè)認(rèn)證框架,它會(huì)使用 SOAP 作為更低級(jí)別的傳輸。請(qǐng)參閱 清單 11來(lái)查看在 SOAP 中的報(bào)頭實(shí)現(xiàn)。
清單 11:在一個(gè) SOAP Envelope 中的報(bào)頭實(shí)現(xiàn)
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
??? <m:Order xmlns:m="some URI" SOAP-ENV:mustUnderstand="1">
??? </m:Order>
</SOAP-ENV:Header>
</SOAP-ENV:Envelope>
Body:
Body 元素包含您實(shí)際要發(fā)送的消息。它是一個(gè)強(qiáng)制性的元素且其子元素通常屬于一個(gè)用戶定義的名稱空間。 清單 12 展示了一條引用一個(gè)用戶定義的名稱空間 “u” 的 SOAP 消息。Body 元素是必要信息的容器。這個(gè)元素必須在 SOAP 消息中出現(xiàn)并且必須是 SOAP Envelope 元素的一個(gè)直接子元素。它也必須直接跟在 SOAP Header 元素的后面。若沒有 Header 元素,那么它應(yīng)直接跟在 Envelope 元素的后面。主體可以包含子元素并且子元素可能是受限于名稱空間的。
清單 12: SOAP Envelope 內(nèi)有 Header,還有 Body
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
??? <m:Order xmlns:m="some URI" SOAP-ENV:mustUnderstand="1">
??? </m:Order>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
??? <u:GetPrice xmlns:u="some URI" >
??????? <model>m1</model>
??? </u:GetPrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Fault:
這個(gè)元素表明一條錯(cuò)誤消息。它應(yīng)作為一個(gè)主體條目出現(xiàn)并且不能在 Body 元素中出現(xiàn)一次以上。通常,F(xiàn)ault 元素會(huì)在一條 SOAP 響應(yīng)消息中出現(xiàn),以表明在 SOAP 請(qǐng)求中出現(xiàn)錯(cuò)誤。
Fault 的子元素:
faultcode (錯(cuò)誤的標(biāo)識(shí))
faultstring (錯(cuò)誤的描述)
faultactor (標(biāo)識(shí)由誰(shuí)導(dǎo)致的錯(cuò)誤)
detail (錯(cuò)誤細(xì)節(jié)。通常是一個(gè)應(yīng)用程序特定錯(cuò)誤,也就是說(shuō),它相當(dāng)于在 SOAP 請(qǐng)求主體中用到地用戶定義的名稱空間)
清單 13是一條典型的錯(cuò)誤消息。
清單 13: 當(dāng)應(yīng)用程序出現(xiàn)錯(cuò)誤時(shí),SOAP Fault 的使用
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<m:Order xmlns:m="some URI" SOAP-ENV:mustUnderstand="1">
</m:Order>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Not necessary information</faultstring>
<detail>
??? <d:faultdetail xmlns:d = "uri-referrence">
??????? <msg>
??????????? application is not responding properly.
??????? </msg>
??????? <errorcode>12</errorcode>
??? </d:faultdetail>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
來(lái)自第 1 部分的一條對(duì) WSDL 文件的 SOAP 請(qǐng)求
已經(jīng)解釋了 SOAP 消息(請(qǐng)求和響應(yīng))的常規(guī)語(yǔ)法,我將展示如何對(duì)本系列 第 1 部分中的 MobilePhoneservice 開發(fā)一條 SOAP 請(qǐng)求。在第 1 部分中您設(shè)計(jì)一個(gè)完整的 WSDL 接口來(lái)解釋 MobilPhoneservice。移動(dòng)公司在 MobilePhoneservice 中提供了兩種方法,一種是 getListOfModels() ,另一種是 getPrice(modelNumber) 。 GetListOfModels() 沒有參數(shù)但是返回手機(jī)型號(hào)的一張列表,而 getPrice(modelNumber) 有一個(gè)參數(shù) modelNumber 并返回需求型號(hào)的 price 。您將用 SOAP 請(qǐng)求格式對(duì)它作成文檔,但是首先讓我展示給您一般的 SOAP 請(qǐng)求和響應(yīng)格式。
清單 14:SOAP 請(qǐng)求的一般格式
<SOAP-ENV:Envelope xmlns:SOAP-ENV ="SOAP schema's URI"
<SOAP-ENV:Body>
??? <Instance:"Method Name" xmlns:Instance= "URI where method is located">
??????? <parameter1>value</parameter1>
??????? <parametern>value</parametern>
??? </Instance:"Method Name">
</SOAP_Envelop:Body>
</SOAP-ENV:Envelope>
一條簡(jiǎn)單的 SOAP 請(qǐng)求或響應(yīng)只能表明一種服務(wù)的一個(gè)方法。包含一條 SOAP 請(qǐng)求的 Envelope 的一般格式遵從 清單 14。將這種一般格式與 清單 16 中的 getListOfModels() 的方法調(diào)用請(qǐng)求比較。在清單 16 中,我已經(jīng)提供了方法和 URI 的名稱。既然在 getListOfModels() 中不需要參數(shù),所以 <m:getListOfModels> 在 清單 16中是一個(gè)空元素。
清單 15:一條 SOAP 響應(yīng)的一般格式
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
??? <Instance:"Method Name"+"Response"
??? xmlns:Instance="URI where method is located">
??? <return>
??????? <responseparameter1>value</responseparameter1>
??????? <responseparametern>value</responseparametern>
??? </return>
??? </Instance: "Method Name"+"Response">
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 15 是一條一般的 SOAP 響應(yīng)。Apache SOAP 服務(wù)器在方法名稱的后面增加了"Response"關(guān)鍵字并將返回值封入元素 <return> 中作為一個(gè)直接子方法元素。若返回值是復(fù)合型結(jié)構(gòu),那么 <return> 元素包含一個(gè)或多個(gè) <item> 元素。將 清單 15與 清單 17 相比,清單 17 是來(lái)自 getListOfModels() 的實(shí)際響應(yīng)。 清單 17包含一系列項(xiàng)目,作為 Vector 數(shù)據(jù)類型,它是返回參數(shù)。 相似地, 清單 18和 19 展示了針對(duì) MobilePhoneservice 的方法 getPrice() 的 SOAP 請(qǐng)求和響應(yīng)。
清單 16:調(diào)用 getListOfModels() 方法的 SOAP 請(qǐng)求
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
??? <m:getListOfModels xmlns:m = "www.mobilphoneservice.com" >
??? </m:getListOfModels>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 17:針對(duì)于來(lái)自清單 16 請(qǐng)求的 SOAP 響應(yīng)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
??? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
??? xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
??? xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
??? <ns1:getListOfModelsResponse xmlns:ns1="urn:MobilePhoneservice"
??? SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
??? <return xmlns:ns2="http://xml.apache.org/xml-soap"
??????? xsi:type="ns2:Vector">
??????? <item xsi:type="xsd:string">M1</item>
??????? <item xsi:type="xsd:string">M2</item>
??????? <item xsi:type="xsd:string">M3</item>
??????? <item xsi:type="xsd:string">M4</item>
??????? <item xsi:type="xsd:string">M5</item>
??? </return>
??? </ns1:getListOfModelsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 18:對(duì)于 getPrice 方法的 SOAP 請(qǐng)求
<SOAP-ENV:Envelope
??? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
??? xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
??? xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
??? <m:getPrice xmlns:m ="www.mobilphoneservice.com">
??????? <modelNumber xsi:type ="xsd:String">M1</modelNumber>
??? </m:getPrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
清單 19:對(duì)于來(lái)自清單 18 請(qǐng)求的 SOAP 響應(yīng)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
??? xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
??? xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
??? xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
??? <ns1:getPriceResponse xmlns:ns1="urn:MobilePhoneservice"
??????? SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
??????????? <return xsi:type="xsd:string"> 5000 </return>
??? </ns1:getPriceResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
在 SOAP 服務(wù)器上部署基于 WSDL 的服務(wù)
在此部分您將在 Apache SOAP 服務(wù)器上部署來(lái)自第 1 部分的 WSDL 服務(wù)。Apache SOAP 工具箱將 WSDL 服務(wù)信息保存在一個(gè)部署描述符文件里面。部署描述符包含了 WSDL 服務(wù)的名稱和它擁有的所有方法。在運(yùn)行時(shí)部署描述符會(huì)將這些名稱提供給 SOAP 服務(wù)器。同樣的部署描述符文件還包含了實(shí)現(xiàn)接口的 JavaBean 組件的地址。
清單 20:一個(gè)部署描述符的框架
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
??? id="URN:SERVICE-URN">
<isd:provider type="java"
??? scope="Request"
??? methods="EXPOSED-METHODS">
??? <isd:java class="IMPLEMENTING-CLASS"/>
</isd:provider>
<isd:faultListener>org.apache.soap.server.DOMFaultListener
</isd:faultListener>
</isd:service>
清單 20是一個(gè)部署描述符的框架,為了作為基于 WSDL 服務(wù)的部署描述符使用,它需要三項(xiàng)信息( URN:SERVICE-URN、EXPOSED-METHODS和 IMPLEMENTING-CLASS)。 URN:SERVICE-URN 是被部署服務(wù)的名稱。在此例中它是 “urn:MobilePhoneservice” 。 EXPOSED-METHODS 是一個(gè)單空格分隔的由服務(wù)提供的方法的列表。 在此部署中它是 getListOfModels getPrice 。
IMPLEMENTING-CLASS 是帶有全路徑的 Java 類名稱。例如, samples.phonequote.MobilePhoneservice 。 在此例中測(cè)試應(yīng)用程序時(shí),您有如下目錄結(jié)構(gòu):
Apache SOAP 服務(wù)器: C:\foo\SOAP-2_2
Mobile phone 服務(wù)實(shí)現(xiàn):
C:\foo\SOAP-2_2\samples\phonequote\MobilePhoneservice
因此,IMPLEMENTING-CLASS 路徑請(qǐng)參照您安裝 SOAP 工具箱的目錄。 我沒有提供 Java 類的實(shí)際實(shí)現(xiàn)。 它取決于業(yè)務(wù)邏輯并且可以是任何東西。
清單 21:MobilePhoneservice 的部署描述符
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:MobilePhoneservice">
<isd:provider type="java"
??? scope="Request"
??? methods="getListOfModels getPrice">
??? <isd:java class="samples.phonequote.MobilePhoneservice"/>
</isd:provider>
<isd:faultListener>
??? org.apache.soap.server.DOMFaultListener
</isd:faultListener>
</isd:service>
清單 21是來(lái)自第 1 部分對(duì) WSDL 文件的完整部署描述符。
SOAP 客戶機(jī)與 SOAP 服務(wù)器的通信
我已經(jīng)提供過(guò)一個(gè)應(yīng)用程序樣本來(lái)演示一臺(tái) SOAP 客戶機(jī)與一臺(tái) SOAP 服務(wù)器的通信。 為此我給過(guò)三個(gè)列表:Startup.html( 清單 22)、Operation.html( 清單 23)和 Execute.jsp( 清單 24)。
StartUp.html( 清單 22)是一個(gè)簡(jiǎn)單的 HTML 文件,提供給用戶一個(gè) GUI 并詢問他將要調(diào)用哪一個(gè) SOAP 方法。用戶會(huì)選擇一個(gè)他需要的方法。
清單 22:一個(gè)作為前端的簡(jiǎn)單 HTML 頁(yè)
<HTML>
<BODY bgcolor="Teal">
<br/>
<p align="center">
??? <font size="5" face="Arial" color="white"><b>
??? SOAP method invocation demo </b></font>
</p>
<hr/>
<font face="Arial" color="whitesmoke" size="3">
<br/><b>
??? Click any of the method name to execute.<br/>
??? 1. Get the List of all Models that we manufacture....
??????? <a href="execute.jsp?index=1">
??????? <font color="orange"> GetListOfModels </font></a> <br/>
??? 2. Get the Price of any particular model......................
??????? <a href="operation.html">
??????? <font color="orange"> GetPrice </font></a>
</b>
</BODY>
</HTML>
Operation.html( 清單 23)將詢問客戶提供方法調(diào)用所需的參數(shù)。
清單 23:根據(jù)他或她所選擇的方法給予客戶一個(gè) GUI
<HTML>
<BODY bgcolor="Teal">
<br/>
<p align="center">
<font size="5" face="Arial" color="white"><b>
??? GetPrice Operation input Form </b>
</font></p>
<hr/>
<p align="center">
<form action="execute.jsp" method="POST">
<input type="hidden" name="index" value="0">
<table textColor="white">
<tr><td>
<font color="whitesmoke"><b>Description :</b></font>
</td><td><font color="whitesmoke">
??? Method GetPrice is used to Get Price of given Model Number</font>
</td></tr>
<tr><td>
<font color="whitesmoke"><b>Parameter(s)</b></font></td><td>
</td></tr>
<tr><td><font color="whitesmoke">Model Number </td></font>
<td><font color="whitesmoke">
??? <input type="text" name="parameter" size="30">
??? (required) </font>
</td></tr>
<tr><td>
??? </td><td><input type="Submit" value="Invoke">
</td></tr>
</font>
</table>
</form>
</p>
</BODY>
</HTML>
Execute.jsp( 清單 24)包含了所有的令人感興趣的代碼。它檢測(cè)所調(diào)用的方法和所傳遞的參數(shù)。然后發(fā)送給遠(yuǎn)程服務(wù)器一個(gè)方法調(diào)用。
清單 24:檢測(cè)方法并發(fā)送給遠(yuǎn)程服務(wù)器一個(gè)調(diào)用
<%@ page language="java" import="java.util.Vector" %>
<%@ page import="java.net.MalformedURLException, java.net.URL" %>
<%@ page import="java.util.Vector" %>
<%@ page import="org.apache.soap.SOAPException,
??????? org.apache.soap.Constants" %>
<%@ page import="org.apache.soap.rpc.Call, org.apache.soap.rpc.Response,
??????? org.apache.soap.rpc.Parameter" %>
<%@ page import="org.apache.soap.transport.http.SOAPHTTPConnection" %>
<%@ page import="org.apache.soap.Fault" %>
<HTML>
<BODY bgcolor="Teal">
<br/>
<p align="center">
<font color="whitesmoke">
<%
??? boolean isParameter = false ;
??? SOAPHTTPConnection soapTransport = new SOAPHTTPConnection();
??? // Address of the remote server.
??? // Normally this should be dynamically passed and detected.
??? // We have hard coded it only for demonstration.
??? URL url = new URL ("http://localhost:8080/soap/servlet/rpcrouter");
??? // Build the call.
??? Call call = new Call ();
??? call.setTargetObjectURI ("urn:MobilePhoneservice");
??? call.setSOAPTransport (soapTransport);
??? call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
??? // We'll detect which method user selected
??? // and give a call accordingly.
??? // We'll pass parameters if present.
??? if (request.getParameter("parameter")!=null)
??? isParameter = true;
??? if (request.getParameter("index").equals("0"))
??? {
??????? call.setMethodName("getPrice");
??????? Vector params = new Vector();
??????? String message = new String (request.getParameter("parameter"));
??????? params.addElement (new Parameter("message", String.class,
??????? message , null));
??????? call.setParams(params);
??? }
??? else
??????? call.setMethodName("getListOfModels");
??? Response resp = call.invoke ( url, /* actionURI */ "" );
??? out.println("<p align=left>
??? <font size=\"4\" face=\"Arial\" color=\"white\">
??? Response of [ "+call.getMethodName()+" ]
??? </font><hr/>");
???
??? // Check the response.
??? if (resp.generatedFault ()) {
??????? Fault fault = resp.getFault ();
??????? out.println("<b>Fault is:</b>"+ fault.getFaultCode ()
??????? +" ["+fault.getFaultString ()+"]");
??? } else {
??????? Parameter result = resp.getReturnValue ();
??????? out.println("<b>Response is: </b>"+ result.getValue ()+"");
??? }
%>
<font>
</p>
</BODY>
</HTML>
為了運(yùn)行此應(yīng)用程序,您需要兩臺(tái) Apache SOAP 服務(wù)器。 一臺(tái)服務(wù)器將用來(lái)與用戶通信并托管 清單 22、 23和 24。 另一臺(tái)服務(wù)器(也稱為遠(yuǎn)程服務(wù)器)就是我們需要部署第 1 部分所講的基于 WSDL 服務(wù)的地方(在前一節(jié)描述,“ 在 SOAP 服務(wù)器上基于 WSDL 服務(wù)的部署”)。 僅僅是為了演示,遠(yuǎn)程服務(wù)器的地址 http://localhost:8080/soap/servlet/rpcrouter 已經(jīng)硬編碼在 Execute.jsp( 清單 24)中。在實(shí)際操作中您可以從 WSDL 文件中讀取它。
SOAP 中的簡(jiǎn)單與復(fù)合數(shù)據(jù)類型
在此節(jié)中,我將從解釋簡(jiǎn)單與復(fù)合數(shù)據(jù)類型的不同開始。然后展示如何在 SOAP 中對(duì)它們編碼。
簡(jiǎn)單類型包括字符串、浮點(diǎn)數(shù)、整數(shù)、枚舉等。 例如一部手機(jī)的“name”的數(shù)據(jù)類型就是 “string” 。 復(fù)合類型由簡(jiǎn)單類型組成但只代表一個(gè)實(shí)體。例如, “Student” 類型記錄可以有不同的屬性,如 “studentName” 屬于類型 “string” , “studentRollNumber” 屬于類型 “int” 但都只代表一個(gè)實(shí)體 “Student” 。
清單 25 包含了一個(gè)名稱為 “Mobile” 的復(fù)合數(shù)據(jù)類型。 您會(huì)在后面的 SOAP 請(qǐng)求中用到。
清單 25: “Mobile”類型的模式定義結(jié)構(gòu)
1<? xml version="1.0" ?>
2<xsd:schema xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
3?? xmlns:xsd="http://www.w3.org/1999/XMLSchema">
4?? targetNameSpace= "www.mobilephoneservice.com/phonequote">
5 <xsd:element name ="Mobile">
6 <xsd:complexType>
7?? <xsd:element name="modelNumber" type="xsd:int">
8?? <xsd:element name="modelName" type="xsd:string">
9?? <xsd:element name="modelWeight" type="xsd:int">
10? <xsd:element name="modelSize" type="xsd:int">
11? <xsd:element name="modelColor">
12????? <simpleType base="xsd:string">
13????? <enumeration value="white" />
14????? <enumeration value="blue" />
15????? <enumeration value="black" />
16????? <enumeration value="red" />
17????? <enumeration value="pink" />
18????? </simpleType>
19? </xsd:element>
20 </complexType>
21 </xsd:element>
22</xsd:schema>
在 清單 25 中的第 5 行展示了我們的類型名稱(Mobile),而第 6 行說(shuō)明它是復(fù)合數(shù)據(jù)類型。因復(fù)合數(shù)據(jù)類型有屬性,所以在第 7 行到第 12 行展示了定義為子元素的 “Mobile” 數(shù)據(jù)類型的屬性。
第 7 行聲明的元素展示了 “Mobile” 類型有一個(gè)名稱為 “modelNumber” 的屬性且其類型為 “int” (也就是說(shuō), “modelNumber” 只能采用整數(shù)值)。 類似的,第 9 行和第 10 行聲明的元素具有同樣的類型但有不同的屬性名稱。在第 8 行定義的元素具有名稱為 “modelName” 的屬性且其類型是 “string” 。
第 11 行的元素因有位于第 12 行的、名稱為 “simpleType” 的子元素,所以需要更好的理解。這里您在復(fù)合類型 Mobile 中定義了一個(gè)簡(jiǎn)單類型。simpleType 的名稱為 “modelColor” 且它的類型是 “enumeration” 。它有一個(gè)屬性 “base” 具有的值為 "xsd:string" ,這表明簡(jiǎn)單類型 “modelColor” 具有在 SOAP 模式中定義的類型 “string” 的功能。在第 13 行到第 17 行中的每一個(gè) <enumeration> 標(biāo)記都具有一個(gè)屬性: “value” ( "white" "blue" 、 "black" 、 "red" 和 "pink" )。 枚舉類型使我們能夠從多項(xiàng)選項(xiàng)中選擇一個(gè)值。
在 SOAP 請(qǐng)求中使用復(fù)合數(shù)據(jù)類型
清單 26 演示了在 SOAP 請(qǐng)求中復(fù)合類型的使用。 它展示了一個(gè)在 Body 元素中的攜帶請(qǐng)求的 Envelope,在 Body 元素中,您正調(diào)用 “m” 名稱空間的 addModel 方法。 清單 26 使用數(shù)據(jù)類型 “Mobile” ,此數(shù)據(jù)類型在 清單 25中定義。
AddModel 方法攜帶一個(gè)類型為 “Mobile” 的參數(shù)。我們以 “msd” 名稱空間引用來(lái)引用 “Mobile” 結(jié)構(gòu)。請(qǐng)參閱清單 26 的 <SOAP-ENV:Envelope> 元素中的 "xmlns:msd" 聲明。 這是一個(gè)在 SOAP 請(qǐng)求中使用用戶定義數(shù)據(jù)類型的示例。
清單 26:實(shí)現(xiàn)在清單 25 中定義的“Mobile”結(jié)構(gòu)
1 <SoAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
2 xmlns:xsd="http://www.w3.org/1999/XMLSchema"
3 xmlns:msd="www.mobilephoneservice.com/phonequote">
4 <SOAP-ENV:Body>
5?? <m:addModel xmlns:m="www.mobilephoneservice.com">
6?? <msd:Mobile>
7?????? <modelNumber>1</modelNumber>
8?????? <modelName>mlr97</modelName>
9?????? <modelWeight>10</modelWeight>
10????? <modelSize>4</modelSize>
11????? <modelColor>white</modelColor>
12? </msd:Mobile>
13? </m:addModel>
14 </SOAP-ENV:Body>
15<SOAP-ENV:Envelope>
總結(jié)
在這一部分中,您已經(jīng)學(xué)習(xí)了 SOAP 語(yǔ)法、請(qǐng)求、響應(yīng)、HTTP 綁定和使用電子郵件的 SOAP 使用。您也了解了服務(wù)于 Apache SOAP 客戶機(jī)的 Apache SOAP 服務(wù)器。最后,我簡(jiǎn)述了用戶定義數(shù)據(jù)類型的主題,它是一個(gè)需要我們仔細(xì)學(xué)習(xí)的高級(jí)主題。在此系列文章的下一部分,您會(huì)學(xué)習(xí)更多的用戶定義數(shù)據(jù)類型示例。我還會(huì)檢驗(yàn) SOAP 的互操作性(也就是說(shuō),怎樣使來(lái)自不同供應(yīng)商的 SOAP 實(shí)現(xiàn)相互協(xié)調(diào))。
使用 WSDL 部署 Web 服務(wù),第 2 部分: 簡(jiǎn)單對(duì)象訪問協(xié)議(SOAP)
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
