DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> AJAX入門 >> AJAX詳解 >> 用AJAX調用SOAP Web服務:構建Web服務客戶機
用AJAX調用SOAP Web服務:構建Web服務客戶機
編輯:AJAX詳解     
【導讀】本文介紹如何使用異步 Javascript 和 XML (Asynchronous JavaScript and XML, AJax) 設計模式來實現基於 Web 浏覽器的 SOAP Web 服務客戶機。

AJax 已普遍用於許多知名的 Web 應用程序服務,例如 GMail、Google Maps、Flickr 和 Odeo.com。通過使用異步 XML 消息傳遞,AJax 為 Web 開發人員提供了一種擴展其 Web 應用程序價值和功能的途徑。這裡介紹的 Web Services JavaScript Library 擴展了該基礎機制,其通過引入對調用基於 SOAP 的 Web 服務的支持來增強 AJax 設計模式。

從浏覽器中調用 Web 服務

從 Web 浏覽器中調用 SOAP Web 服務可能會比較麻煩,這是因為大多數流行的 Web 浏覽器在生成和處理 XML 方面都略有不同。所有浏覽器都一致實現且用於 XML 處理的標准 API 或功能少之又少。

浏覽器實現人員一致支持的機制之一是 XMLHttpRequest API,它是 AJax 設計模式的核心。developerWorks 網站最近發布的另一篇由 Philip McCarthy 撰寫的的文章詳細介紹了該 API。XMLHttpRequest 是一個用於執行異步 HTTP 請求的 JavaScript 對象。Philip McCarthy 在其文章中描述了一個順序圖(請參見圖 1),此圖對於理解 XMLHttpRequest 對象如何支持 AJax 設計非常有幫助(請參閱參考資料,以獲得指向全文的鏈接)。



圖 1. Philip McCarthy 的 AJax 順序圖

從此圖中,您可以清楚地看到 XMLHttpRequest 對象是如何工作的。一些運行在 Web 浏覽器內的 JavaScript 創建了一個 XMLHttpRequest 實例和一個用於異步回調的函數。然後,該腳本使用 XMLHttpRequest 對象對服務器執行 HTTP 操作。在接收到響應後,調用回調函數。在該回調函數內,可能處理返回的數據。如果返回的數據碰巧是 XML,則 XMLHttpRequest 對象將自動使用浏覽器中內置的 XML 處理機制來解析該數據。

遺憾的是,使用 AJax 方法的主要難題在於 XMLHttpRequest 對象自動解析 XML 的詳細過程。例如,假設我正在請求的數據是一個 SOAP 信封,其包含來自許多不同 XML 命名空間的元素,並且我希望提取 yetAnotherElement 中屬性 attr 的值。(請參見清單 1)

清單 1. 一個包含多個命名空間的 SOAP 信封

<s:Envelope 

xmlns:s="http://schemas.XMLsoap.org/soap/envelope/"

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

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<s:Header/>

<s:Body>

<m:someElement XMLns:m="http://example">

<n:someOtherElement

XMLns:n="http://example"

XMLns:m="urn:example">

<m:yetAnotherElement

n:attr="abc"

XMLns:n="urn:foo"/>

</n:someOtherElement>

</m:someElement>

</s:Body>

</s:Envelope>

在 Mozilla 浏覽器和 Firefox 浏覽器中,提取 attr 屬性值非常簡單,如清單 2所示。

清單 2. 在 Mozilla 和 Firefox 中檢索 attr 屬性值的方法不能運用在 Internet Explorer 中

var m = el.getElementsByTagNameNS(

'urn:example',

'yetAnotherElement')[0].

getAttributeNS(

'urn:foo',

'attr')

alert(m); // displays 'abc'

關於安全性

由於涉及許多實際安全問題,因此在缺省情況下,大多數 Web 浏覽器中的 XMLHttpRequest 對象都限制為只能與用戶正在查看的 Web 頁所在的域中承載的資源和服務進行交互。例如,如果我正在訪問一個位於 http://example.com/myapp/ 的頁面,則 XMLHttpRequest 將只允許訪問位於 example.com 域中的資源。對於阻止惡意應用程序代碼潛在地對其不應該訪問的信息進行不適當的訪問,這種預防措施非常必要。因為這裡介紹的 Web 服務客戶機基於 XMLHttpRequest,所以這種限制同樣適用於您將會調用的 Web 服務。

如果您需要能夠訪問位於另一個域中的 Web 服務,您可以使用以下兩種合理的解決方案:

  • 對 Javascript 進行數字簽名。通過對 JavaScript 腳本進行數字簽名,您就告訴了 Web 浏覽器可以信任該腳本不會執行任何惡意的活動,並且對 XMLHttpRequest 可以訪問的數據的限制也應該取消。
  • 使用代理。一個簡單的解決方案是,通過位於加載的頁面所在的域中的代理資源來傳遞所有來自 XMLHttpRequest 的請求。該代理將 XMLHttpRequest 的請求轉發到遠程位置,並將結果返回給浏覽器。從 XMLHttpRequest 對象的角度來看,這種交互發生在現有的安全配置之內。

遺憾的是,以上代碼無法在 Internet Explorer Version 6 中運行,因為該浏覽器不僅沒有實現 getElementsByTagNameNS 功能,而且事實上還使用了一種很糟糕的方法——將 XML 命名空間的前綴作為其元素和屬性名稱的一部分來對待。

Internet Explorer 缺少對 XML 命名空間的支持,這使得它很難處理命名空間密集的 XML 格式,例如采用獨立於浏覽器的方式的 SOAP。即使要執行一些像提取結果中的屬性值這樣簡單的操作,您也必須編寫能夠在多個浏覽器中實現一致預期行為的特殊代碼。幸運的是,這種特殊代碼可以封裝並重用。

為了從 Web 浏覽器中調用 Web 服務,並可靠地處理 SOAP 消息,您需要首先了解一些安全問題(請參見側欄“關於安全性”)。此外,您還需要編寫一個 JavaScript 腳本庫(圖 2),以便將底層浏覽器 XML 實現中的不一致情況抽象出來,從而使您能夠直接處理 Web 服務數據。

圖 2. 在使用 Web Services Javascript Library 的 Web 浏覽器中通過 Javascript 調用 Web 服務

圖 2 中的 Web Services Javascript Library (ws.JS) 是一組 JavaScript 對象和實用功能,它們為基於 SOAP 1.1 的 Web 服務提供了基本的支持。Ws.JS 定義了下列對象:

•WS.Call:一個包裝了 XMLHttpRequest 的 Web 服務客戶機

•WS.QName:XML 限定名實現

•WS.Binder:自定義 XML 序列化器/反序列化器的基礎

•WS.Handler:請求/響應處理程序的基礎

•SOAP.Element:包裝了 XML DOM 的基本 SOAP 元素

•SOAP.Envelope:SOAP Envelope 對象擴展了 SOAP.Element

•SOAP.Header:SOAP Header 對象擴展了 SOAP.Element

•SOAP.Body:SOAP Body 對象擴展了 SOAP.Element

•XML:用於處理 XML 的跨平台實用方法

ws.JS 的核心是 WS.Call 對象,該對象提供了調用 Web 服務的方法。WS.Call 主要負責與 XMLHttpRequest 對象進行交互,並處理 SOAP 響應。

WS.Call 對象公開了以下三個方法:

•add_handler。向處理鏈添加請求/響應處理程序。處理程序對象在調用 Web 服務的前後被調用,以支持可擴展的預調用處理和後調用處理。

•invoke。將指定的 SOAP.Envelope 對象發送給 Web 服務,然後在接收到響應後調用回調函數。當調用使用文本 XML 編碼的文檔樣式的 Web 服務時,請使用此方法。

•invoke_rpc。創建一個封裝 RPC 樣式請求的 SOAP.Envelope,並將其發送到 Web 服務。當接收到響應時,調用回調函數。

在通常情況下,WS.Call 對象只不過是位於 XMLHttpRequest 對象頂層的瘦包裝器 (thin wrapper),該包裝器能夠執行許多簡化處理的操作。這些操作包括設置 SOAP 1.1 規范要求的 SOAPAction HTTP Header。

使用 ws.JS


Web services JavaScript Library 提供的 API 非常簡單。

SOAP.* 對象(SOAP.Element、SOAP.Envelope、SOAP.Header 和 SOAP.Body)提供了構建和讀取 SOAP 信封的方法,如清單 3 所示,因而處理 XML 文檔對象模型的底層細節就順利地抽象出來。

清單 3. 構建一個 SOAP 信封

var envelope = new SOAP.Envelope();

var body = envelope.create_body();

var el = body.create_child(new WS.QName('method','urn:foo'));

el.create_child(new WS.QName('param','urn:foo')).set_value('bar');

清單 4 顯示了由 清單 3 中的代碼生成的 SOAP 信封。

清單 4. 構建一個 SOAP 信封

<Envelope xmlns="http://schemas.XMLsoap.org">

<Body>

<method XMLns="urn:foo">

<param>bar</param>

</method>

</Body>

</Envelope>

如果您正在創建的 SOAP 信封代表一個 RPC 樣式的請求,則 SOAP.Body 元素提供了一個簡便方法 set_rpc(如清單 5 所示),該方法能夠構造一個完整的 RPC 請求——包含一個指定的操作名稱、一個指定的輸入參數數組和一個 SOAP 編碼樣式的 URI。

清單 5. 構建一個 RPC 請求信封

var envelope = new SOAP.Envelope();

var body = envelope.create_body();

body.set_rpc(

new WS.QName('param','urn:foo'),

new Array(

{name:'param',value:'bar'}

), SOAP.NOENCODING

);

每個參數都作為一個 JavaScript 對象結構進行傳遞,且可能帶有以下屬性:

•name。一個指定參數名稱的字符串或 WS.QName 對象。必需。

•value。參數的值。如果該值不是一個簡單數據類型(例如,字符串、整數或其他),則應該指定一個能將該值序列化為適當的 XML 結構的 WS.Binder。必需。

•xsitype:標識參數的 XML 模式實例類型的 WS.QName(例如,xsi:type="int" 對應 xsitype:new WS.QName('int','http://www.w3.org/2000/10/XMLSchema'))。可選。

•encodingstyle:標識參數所使用的 SOAP 編碼樣式的 URI。可選。

•binder:能夠將參數序列化為 XML 的 WS.Binder 實現。可選。

例如,如果要指定的參數名為“abc”、XML 命名空間為“urn:foo”、xsi:type 為“int”且值為“3”,則我會使用以下代碼:new Array({name:new WS.QName('abc','urn:foo'), value:3, xsitype:new WS.QName('int','http://www.w3.org/2000/10/XMLSchema')})。

一旦我為服務請求構建了 SOAP.Envelope,我就會將該 SOAP.Envelope 傳遞到 WS.Call 對象的 invoke 方法,以便調用該信封內編碼的方法: (new WS.Call(service_uri)).invoke(envelope, callback)另一種可選方案是手動構建 SOAP.Envelope。我會將參數 WS.QName、參數數組和編碼樣式傳遞到 WS.Call 對象的 invoke_rpc 方法,如清單 6 所示。

清單 6. 使用 WS.Call 對象調用 Web 服務

var call = new WS.Call(serviceURI);

var nsuri = 'urn:foo';

var qn_op = new WS.QName('method',nsuri);

var qn_op_resp = new WS.QName('methodResponse',nsuri);

call.invoke_rpc(

qn_op,

new Array(

{name:'param',value:'bar'}

),SOAP.NOENCODING,

function(call,envelope) {

// envelope is the response SOAP.Envelope

// the XML Text of the response is in arguments[2]

}

);

在調用 invoke 方法或 invoke_rpc 方法時,WS.Call 對象會創建一個基本的 XMLHttpRequest 對象,用包含 SOAP 信封的 XML 元素進行傳遞,並接收和解析響應,然後調用提供的回調函數。

為了能夠擴展 SOAP 消息的預處理和後處理,WS.Call 對象允許您注冊一組 WS.Handler 對象,如清單 7 所示。對於調用周期內的每個請求、每個響應和每個錯誤,都將調用這些對象。可以通過擴展 WS.Handler JavaScript 對象來實現新的處理程序。

清單 7. 創建和注冊響應/響應處理程序

var MyHandler = Class.create();

MyHandler.prototype = (new WS.Handler()).extend({

on_request : function(envelope) {

// pre-request processing

},

on_response : function(call,envelope) {

// post-response, pre-callback processing

},

on_error : function(call,envelope) {

}

});

var call = new WS.Call(...);

call.add_handler(new MyHandler());

處理程序對插入或提取正在傳遞的 SOAP 信封中的信息最有用。例如,您可以設想一個處理程序自動向 SOAP Envelope 的 Header 插入適當的 Web 服務尋址 (Web Services Addressing) 元素,如清單 8 中的示例所示。

清單 8. 一個將 Web 服務尋址操作 Header 添加到請求中的處理程序示例

var WSAddressingHandler = Class.create();

WSAddressingHandler.prototype = (new WS.Handler()).extend({

on_request : function(call,envelope) {

envelope.create_header().create_child(

new WS.QName('Action','http://ws-addressing','wsa')

).set_value('http://www.example.com');

}

});

WS.Binder 對象(清單 9)執行 SOAP.Element 對象的自定義序列化和反序列化。WS.Binder 的實現必須提供以下兩個方法:

•to_soap_element。將 JavaScript 對象序列化為 SOAP.Element。傳入的第一個參數是要序列化的值。第二個參數是 SOAP.Element,必須將要序列化的值序列化為 SOAP.Element。該方法不返回任何值。

•to_value_object。將 SOAP.Element 反序列化為 JavaScript 對象。該方法必須返回反序列化的值對象。

清單 9. WS.Binding 實現示例

var MyBinding = Class.create();

MyBinding.prototype = (new WS.Binding()).extend({

to_soap_element : function(value,element) {

...

},

to_value_object : function(element) {

...

}

});

一個簡單示例

我已經提供了一個示例項目來闡釋 Web Services JavaScript Library 的基本功能。該演示所使用的 Web 服務(如清單 10 所示)已經在 WebSphere Application Server 中進行了實現,並提供了簡單的 Hello World 功能。

清單 10. 一個簡單的基於 Java 的“Hello World”Web 服務

package example;

public class HelloWorld {

public String sayHello(String name) {

return "Hello " + name;

}

}

在實現了該服務並將其部署到 WebSphere Application Server 後,該服務(清單 11)的 WSDL 描述定義了您需要傳遞的 SOAP 消息(用於調用 Hello World 服務)。

清單 11. HelloWorld.wsdl 的代碼片段

<wsdl:portType name="HelloWorld">

<wsdl:Operation name="sayHello">

<wsdl:input

message="impl:sayHelloRequest"

name="sayHelloRequest"/>

<wsdl:output

message="impl:sayHelloResponse"

name="sayHelloResponse"/>

</wsdl:Operation>

</wsdl:portType>


通過使用 Web Services JavaScript Library,您可以實現一個調用 Hello World 服務的方法,如清單 12所示。

清單 12. 使用 WS.Call 調用 HelloWorld 服務

<Html>

<head>

...

<script

type="text/Javascript"

src="scripts/prototype.JS"></script>

<script

type="text/Javascript"

src="scripts/ws.JS"></script>

<script type="text/Javascript">

function sayHello(name, container) {

var call = new WS.Call('/AJaxWS/services/HelloWorld');

var nsuri = 'http://example';

var qn_op = new WS.QName('sayHello',nsuri);

var qn_op_resp = new WS.QName('sayHelloResponse',nsuri); 

call.invoke_rpc(

qn_op,

new Array(

{name:'name',value:name}

),null,

function(call,envelope) {

var ret =

envelope.get_body().get_all_children()[0].

get_all_children()[0].get_value();

container.innerHtml = ret;

$('soap').innerHTML = arguments[2].escapeHtml();

}

);

}

</script>

</head>

...

然後,您可以在我們的 Web 應用程序中的任意位置通過調用 sayHello 函數來調用 Hello World 服務。請參見清單 13。

清單 13. 調用 sayHello 函數

<body>

<input name="name" id="name" />

<input value="Invoke the Web Service"

type="button"

/>

<div id="container">Result:

<div id="result">

</div>

<div id="soap">

</div>

</div>

</body>

調用成功後的結果如圖 3 所示。在 Mozilla、Firefox 和 Internet Explorer 中運行該示例應該會得到相同的結果。

圖 3. Firefox 中的 Hello World 示例

後續部分

使用 Web Services JavaScript Library,可以采用簡單的獨立於浏覽器的方式將基本的 SOAP Web 服務合並到 Web 應用程序中。在本系列的下一個部分中,您不僅可以探討如何使用該庫來調用更多基於 Web 服務資源框架 (WS-Resource Framework ) 系列規范的高級 Web 服務,而且還可以了解擴展該 Web 服務功能並將其集成到 Web 應用程序中的方法。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved