使用 AJAX 调用 SOAP Web 服务

来源:互联网 发布:百度阅读 json格式 编辑:程序博客网 时间:2024/05/21 04:23

1 部分: 构建 Web 服务客户机

 

本文介绍如何使用异步 JavaScript XML (Asynchronous JavaScript and XML, AJAX) 设计模式来实现基于 Web 浏览器的 SOAP Web 服务客户机。

本文是一篇短的系列文章的第 1 部分,演示了如何使用针对 Web 应用程序的 AJAX 设计模式来实现跨平台的基于 JavaScript SOAP Web 服务客户机。

AJAX 已普遍用于许多知名的 Web 应用程序服务,例如 GMailGoogle MapsFlickr 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 撰写的的文章详细介绍了该 APIXMLHttpRequest 是一个用于执行异步 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.QNameXML 限定名实现
  • WS.Binder:自定义 XML 序列化器/反序列化器的基础
  • WS.Handler:请求/响应处理程序的基础
  • SOAP.Element:包装了 XML DOM 的基本 SOAP 元素
  • SOAP.EnvelopeSOAP Envelope 对象扩展了 SOAP.Element
  • SOAP.HeaderSOAP Header 对象扩展了 SOAP.Element
  • SOAP.BodySOAP 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.ElementSOAP.EnvelopeSOAP.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"

       onclick="sayHello($('name').value,$('result'))" />

<div id="container">Result:

<div id="result">

</div>

<div id="soap">

</div>

</div>

</body>

 

调用成功后的结果如图 3 所示。在 MozillaFirefox Internet Explorer 中运行该示例应该会得到相同的结果。


3. Firefox 中的 Hello World 示例

 

2 部分: 扩展 Web 服务客户机

 

使用 Asynchronous JavaScript and XML (Ajax) 设计模式实现基于 Web 浏览器的 SOAP Web 服务。在本系列的第 1 部分使用 AJAX 调用 SOAP Web 服务,第 1 部分中,作者引入了一个简单的用于调用 SOAP Web 服务的基于 Web 浏览器的 JavaScript 库。在接下来的讨论中,作者将实现对 Web 服务寻址语言 (Web Services Addressing Language) Web 服务资源框架 (Web Services Addressing Language) 规范的支持,以便扩展 JavaScript 库的功能。

概述

在本系列的第 1 部分,我介绍了一个跨浏览器的 JavaScript 库,其中提供了一个简单的 SOAP Web 服务客户机,该客户机可以发出采用 RRC 编码和文档-文本样式的请求。该客户机包含对请求和响应处理程序、自定义 XML 序列化器/反序列化器以及 SOAP Header 的支持;所有这些支持都将在这个 WS-Addressing WS-ResourceFramework 实现中用到。

ws.js(在第 1 部分中引入的)中定义的主要对象包括:

  1. WS.Call:包装 XMLHttpRequest Web 服务客户机
  2. WS.QNameXML 限定名称实现
  3. WS.Binder:自定义 XML 序列化器/反序列化器的基对象
  4. WS.Handler:请求/响应处理程序的基对象
  5. SOAP.Element:包装 XML DOM 的基本 SOAP 元素
  6. SOAP.Envelope:扩展自 SOAP.Element SOAP Envelope 对象
  7. SOAP.Header:扩展自 SOAP.Element SOAP Header 对象
  8. SOAP.Body:扩展自 SOAP.Element SOAP Body 对象
  9. XML:用于处理 XML 的跨平台实用方法

这一组对象中有五个对象对 WS-Addressing WS-ResourceFramework 实现非常关键:WS.QNameSOAP.ElementWS.HandlerWS.Binder WS.Call。我强烈建议重新阅读一下第一篇文章,以回顾这些对象的基本功能。

在本文中,我将引入两个新的 JavaScript 文件。第一个文件定义支持 WS-Addressing 的对象 (wsa.js);而第二个文件定义支持 WS-ResourceFramework 的基本实现的对象 (wsrf.js)



1. Web 浏览器内使用 Web 服务 JavaScript 库调用 Web 服务资源框架服务

wsa.js 中定义的主要对象包括:

  1. WSA.EndpointReferenceWS-Addressing EndpointReference 对象。
  2. WSA.EndpointReference.ReferenceParametersWS-Addressing EPR 引用参数的容器。
  3. WSA.EndpointReference.BinderWSA.EndpointReference 对象的 XML 序列化器/反序列化器。
  4. WSA.MessageContextWS-Addressing SOAP 消息 Header 元数据的容器。
  5. WSA.Handler:将 WS-Addressing SOAP 消息 Header 插入 SOAP 信封的请求处理程序。

wsrf.js 中定义的主要对象包括:

  1. WSRF.Request.GetResourcePropertyWS-ResourceFramework GetResourceProperty 操作的包装对象。
  2. WSRF.Request.GetMultipleResourcePropertiesWS-ResourceFrame GetMultipleresourceProperties 操作的包装对象。
  3. WSRF.Resource:用于调用 WS-ResourceFramework 操作的客户机接口。

请注意,虽然这可能意味着要了解大量的新 JavaScript 对象,但它们所提供的 API 都经过了专门设计,以尽可能减少在实际调用 Web 服务时必须进行的工作量。例如,如果您跳到清单 8,您将发现,通过使用 API,您只需使用寥寥数行代码即可调用与 WS-ResourceFramework 兼容的 Web 服务中的方法--而无需受底层 SOAP 实现细节的困扰。

 

实现 WS-Addressing 支持

Web 服务寻址规范定义了用于向 SOAP 信封插入寻址信息的机制。WS-Addressing 的核心是一个称为 EndpointReference 的对象,该对象可作为对特定 Web 服务实例的引用和说明。(请参见清单 1。)除了 EndpointReference 之外,WS-Addressing 规范还定义了许多 SOAP 消息 Header,可以将其用于直接在 SOAP 信封中传递寻址信息。

wsa.js JavaScript 库提供了许多实现了对 WS-Addressing EndpointReference SOAP 消息 Header 元素的基本支持的对象。



清单 1. 一个简单的 WS-Addressing EndpointReference

<EndpointReference xmlns="http://www.w3.org/2005/08/addressing">

  <Address>http://www.example.org/services/HelloWorld</Address>

  <ReferenceParameters>

    <abc:foo xmlns:abc="urn:foo">This is a test</abc:foo>

  </ReferenceParameters>

</EndpointReference>

 

WSA.EndpointReference 对象用于表示 WS-Addressing EndpointReference,如清单 2 中所示。通过将此代码与上面的 XML 进行比较,您应当能够很好地理解 API 的操作方式。



清单 2. 创建与 WSA.js 相关的 EndpointReference

var epr =

  new WSA.EndpointReference(

    "http://www.example.org/services/HelloWorld");

var epr_rp = epr.create_reference_parameters();

epr_rp.create_child(

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

    set_value('This is a test');

 

WSA.EndpointReference API 目前支持 WS-Addressing 信息模型所定义的 Address ReferenceParameters 属性。目前尚未实现 Metadata 属性,因为这个属性对于此处实现的客户机基本功能并不重要。

WS-Addressing SOAP 消息 Header 应该设置在 Web 服务客户机发送给服务的 SOAP 信封上。由于在 ws.js JavaScript 库中定义的 WS.Call 对象将隐藏使用底层 SOAP 信封的细节,因此请使用 WS.Handler 来为您插入恰当的 Header

Web 服务客户机将针对每个请求、响应和错误调用 WS.Handler 对象的各个方法。对于 WS-Addressing 实现,提供了一个 WSA.Handler,以使用相应的 WSA.MessageContext 对象(其中包含要插入到消息中的信息)。清单 3 演示了这一过程。



清单 3. 使用 WS-Addressing 上下文和处理程序

var address = 'http://www.example.com/services/HelloWorld';

var ctx     = new WSA.MessageContext();

ctx.to      = new WSA.EndpointReference(address);

ctx.replyto = new WSA.EndpointReference(WSA.ANONYMOUS);

ctx.action  = address + '#SayHello'

 

var handler = new WSA.Handler();

handler.set_context(ctx);

 

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

call.add_handler(handler);

 

WSA.MessageContext 对象中的属性与每个 WS-Addressing SOAP 消息 Header 相对应:

  • to:一个 WSA.EndpointReference 对象,其 Address 指定表示信息目的地的绝对 URI
  • from:一个 WSA.EndpointReference 对象,标识消息的发送方。
  • replyto:一个 WSA.EndpointReference 对象,标识回复应送达的位置。
  • faultto:一个 WSA.EndpointReference 对象,标识错误应送达的位置。
  • action:一个绝对 URI,标识消息应触发的操作。
  • messageid:唯一标识消息的绝对 URI
  • relatesto:标识相关消息的 URI 对的数组。URI 对中的第一个 URI 标识关系类型;第二个 URI 指定相关消息的唯一 Message ID

WSA.Handler 向用于调用 Web 服务的 WS.Call 对象进行了注册后,WS.Call 对象就会在每次请求时调用该处理程序,向其传递对 SOAP.Envelope 对象的引用。处理程序将从 WSA.MessageContext 中提取信息,并向消息中插入恰当的 Header,如清单 5 中所示。

 

实现 WS-ResourceFramework 支持

Web 服务资源框架定义了一个使用 Web 服务标准访问和操作有状态资源的实例的约定。各个资源均使用 WS-Addressing EndpointReference 进行标识和引用。可以使用一些常见操作来检索或修改资源的属性。

wsrf.js JavaScript 库提供了支持 GetResourceProperty GetMultipleResourceProperties 操作的部分 Web 服务资源框架实现。该 API 是以 ws.js wsa.js API 为基础构建的,主要是为了演示这两个脚本的使用而设计的,而不是为了提供全面的 WS-ResourceFramework 实现。

WS-ResourceFramework 操作是定向到特定 Resource 实例的文档-文本 SOAP 请求。目标资源是用 WS-Addressing EndpointReference 标识的,如清单 4 所示。



清单 4. WSRF EndpointReference

<EndpointReference xmlns="http://www.w3.org/2005/08/addressing">

  <Address>http://localhost:9080/SoapAjax2/services/DeviceService</Address>

  <ReferenceParameters>

    <abc:DeviceID xmlns:abc="urn:deviceservice">ABC123</abc:DeviceID>

  </ReferenceParameters>

</EndpointReference>

 

当使用 wsa.js 中定义的机制在 SOAP 内进行表示时,WSRF EndpointReference 中的信息将以 SOAP 消息 Header 的形式出现,如清单 5 中所示。



清单 5. WSRF GetResourceProperty 请求

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">

 <Header>

  <To xmlns="http://www.w3.org/2005/08/addressing">

    http://localhost:9080/SoapAjax2/services/DeviceService</To>

  <abc:DeviceID xmlns="urn:deviceservice">ABC123</abc:DeviceID>

 </Header>

 <Body>

 <GetResourceProperty

  xmlns="http://docs.oasis-open.org/wsrf/rp-2"

  xmlns:ns="urn:foo">ns:bar</GetResourceProperty>

 </Body>

</Envelope>

 

wsrf.js 提供的 API 用于隐藏使用 SOAP Envelope 的所有细节以及允许与 WS-ResourceFramework Web 服务交互所必需的 WS-Addressing Header。不过,稍微注意一下此代码,您就会发现此代码的工作方式的许多重要方面。

清单 6 演示了 WSRF GetResourceProperty 操作的包装对象。此包装对象由 wsrf.js 库内部使用,其中包含了创建 SOAP 信封和构建操作所必需的 XML 的基本机制。请注意,该对象利用了 ws.js 提供的 SOAP.Element SOAP.Envelope API。在包装对象初始化阶段传入的“qname”参数是所请求的属性的 XML 限定名称。



清单 6. WSRF GetResourceProperty 请求包装对象

WSRF.Request.GetResourceProperty = Class.create();

WSRF.Request.GetResourceProperty.prototype = {

  initialize : function(qname) {

    this.envelope = new SOAP.Envelope();

    this.set_qname(qname);

  },

  set_qname : function(qname) {

    var body = this.envelope.create_body();

    var method = body.create_child(

      WSRF.Request.QNAME_GETRESOURCEPROPERTY);

    if (!qname.namespace) qname.namespace = '';

    if (!qname.prefix) qname.prefix = 'ns';

    method.declare_namespace(qname);

    method.set_value(qname.value_of());

  }

};

 

清单 7 中包含了来自 WSRF.Resource 对象的代码片段。您所看到的代码的作用在于:创建 WS.Call 对象,准备将用于设置恰当的 SOAP 消息 Header WSA.Handler 对象,创建 WSRF.Request.GetResourceProperty 包装对象以及调用各个 Web 服务操作。



清单 7. 调用 WSRF GetResourceProperty

get_resource_property : function(qname, callback) {

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

  var handler = new WSA.Handler();

  var wsactx = new WSA.MessageContext(this.epr);

  handler.set_context(wsactx);

  call.add_handler(handler);

  var req = new WSRF.Request.GetResourceProperty(qname);

  call.invoke(req.envelope, callback);

}

 

为了对 WS-ResourceFramework Web 调用 GetResourceProperty 操作,应用程序只需要提供目标 WS-Resource EndpointReference 和标识被检索的属性的 WS.QName 对象即可,如清单 8 中所示。



清单 8. 最终结果

var ADDRESS = 'http://localhost:9080/SoapAjax2/services/DeviceService'

 

function getDeviceName(deviceID, container) {

  var epr = new WSA.EndpointReference(ADDRESS);

  var epr_rp = epr.create_reference_parameters();

  epr_rp.create_child(

    new WS.QName(

      'DeviceID',

      'urn:deviceservice')).set_value(deviceID);

  var res = new WSRF.Resource(ADDRESS, epr);

  res.get_resource_property(

    new WS.QName('DeviceName','urn:deviceservice'),

    function(call,envelope) {

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

    }

  );

}

 

清单 8 将对 WS-Resource 的调用包装在可以从 HTML 页中的任何位置调用的适当函数中。清单 9 提供了一个按钮,该按钮可以从名为 id 的输入字段传入一个设备 ID,并在名为 result 的元素中显示响应 SOAP 信封。



清单 9. 调用 getDeviceName

<input

  value="Invoke the Web Service"

  type="button"

  onclick="getDeviceName($('id').value,$('result'))" />

后续部分

在这一部分中,您了解了在本系列第 1 部分中引入的 Ajax Web 服务客户机可以如何进行扩展,以支持更高级的 Web 服务标准(如 Web 服务寻址和 Web 服务资源框架)。在下一部分中,作者将讨论对 Web 服务描述语言 (Web Services Description Language) 的支持。