本文中,我们将对 e4x 做更深层次的介绍。e4x(ecmascript for xml) 是 javascript 的简单扩展,它使得在 javascript 中使用 xml 的脚本更简单易用。我们将用 e4x 构建该交互过程中的服务器端,我们也会介绍如何用 javascript 来实现简单的 web 服务。
本文(由两部分组成的文章中的第二部分)中,我们将对 e4x 做更深层次的介绍。e4x(ecmascript for xml) 是 javascript 的简单扩展,它使得在 javascript 中使用 xml 的脚本更简单易用。在本文章第一部分中,我们展示了一个名为 ajax(异步 javascript 及 xml,asynchronous javascript and xml)的 web 编程模型,并且也展示了一些新的 javascript 的 xml 扩展是如何使其变得非常简单的。在本文章的第二部分中,我们将用 e4x 构建该交互过程中的服务器端,我们也会介绍如何用 javascript 来实现简单的 web 服务。
本文将介绍在 axis 中使用一个 e4xprovider 情况下,e4x 脚本是怎样用来实现 web 服务的。
用 e4x 提供 web 服务
我们已经成功的使用 e4x 调用 web 服务,现在我们要用 e4x 提供 web 服务。很多 web 服务工具包使用这样的一种方法――就是把 xml 映射成本机语言的结构或者相反。例如 jax-rpc(针对基于 xml 的 rpc 的 java api)定义了如何将已存在的 java 方法映射成可访问 soap 的服务。 鉴于这篇文章的写作目的,我们将使用一种截然不同的方法。我们通过提供接受 xml 文档作为参数的方法来提供 web 服务。在这个模型中,web 服务就是一个简单的接收 xml 文档并返回 xml 文档的函数。
根据对 soap 解析程度的需要,我们有两种不同的签名。第一种签名方式与之前编写的客户端程序非常相像,我们只要编写以下程序:
function service(soapenvelope) {
// do things
return newsoapenvelope;
}
这个模型依赖于可以完整解析和管理整个 soapenvelope 的编码器。而更一般的情况是,我们希望使用一种稍微不同的模型,就是把对 soapenvelope 的处理一分为二。在该模型中,一系列的“headler”用于处理 soap 消息头部,而“业务逻辑”处理消息主体。两种模型都有自身的优缺点,由于在 e4x 中拥有强大而易用的 xml 处理能力,因此我们可以轻松地使用这两种模型。
只含有消息体的签名看起来很类似:
function service(soapbody) {
// do things
return newsoapbody;
}
我们将如何部署符合这个模型的服务?为了实现这一目的,我们在这篇文章中的例子里 提供了一种“宿主”技术――apache axis 提供者。提供者是这样一种方式:axis 允许可插拔地插入新的服务提供者程序,而 e4x 提供者允许用户使用 e4x 脚本实现服务。
事实上,我们还有另外两种选择没有提到。第一种是在 j2ee 应用服务器中将 rhino 和 e4x 插入到使用 jax-rpc 和企业 web 服务(ews,也称作 jsr109)技术中。然而,这实际上有一定的困难,因为这些标准对于请求者与提供者采用标准的方法去访问整个 soap 包时并不容易。
总的来说,一个 web 服务的容器(正如 axis 或者一个实现 ews 的 j2ee 服务器)为我们做了两件事。首先,这些容器具有工具与运行时环境支持使得在网络中传递的 xml 消息被映射成运行时系统中程序员所使用的对象。其次,他们通常为附加的 web 服务标准提供支持,比如 web 服务寻址(ws-addressing)、web 服务安全(ws-security)和在消息中加入新的报头并且转换的 ws 事务(ws-transactions),比如通过加密 xml 消息。我们这里所采用的方法将去除了在第一个方面的需要,因为这时我们已经将 xml 作为一个直接使用的本机对象,而不用再次的映射操作。
当然,可能的情况下在 e4x 中使用 web 服务安全,可能不是那么有趣(我们还没有试过!)。所以将 e4x 嵌入到一个 web 服务容器中非常有利。另一方面,如果您在实现一个非常简单的 web 服务(比如没有 web 服务 安全的考虑),您根本不需要一个 web 服务容器。所以第三种选择就是将 rhino/e4x 部署为一个 servlet 容器中的 servlet。如果您只是做简单的报头支持,比如说 web 服务寻址,您可以在 e4x 中处理,实际上这个实现起来非常简单。
尽管在这篇文章中包括了 e4xservlet 和一个可以在下载中找到的样本压缩包的例子,但是我们着重介绍使用 axis。
axis 提供了一个非常简单的方法:通过将 jar 文件加入到类路径环境变量中就可以加入新的提供者。为了实现这个方法,您需要 axis 1.2 或者更高的版本。您可以从 apache axis web 服务引擎中下载。由于 axis 之前发布的版本在保存脚本方面存在问题,所以我们推荐使用上面提到的版本。(如果遇到了类似服务器重新启动之后脚本不能工作的情况,可能是您使用的版本有这个缺陷,请使用新版本的 axis 再次重新部署。)
e4x 提供者要求您使用 e4xprovider.jar(下面会用到)来更新 axis 类路径,除此之外还要包含 rhino.jar 和 xbean.jar。
一旦更新了 axis 类路径,您需要创建一个部署脚本并将其部署到 axis 中。与使用一个 java 实现的服务不同,在这个模型中我们不需要将任何代码部署到类路径中。相反,我们将代码放到部署描述符中,如本文中的一个例子 e4xstock.wsdd 所示。
清单 1. e4xstock.wsdd
<deployment name="test" xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="e4xstock" provider="java:e4x">
<parameter name="type" value="body"/>
<documentation>
<![cdata[
function service(x) {
var value = (x..*::symbol=="ibm")?983:52.5;
var n = new namespace("urn:quoteservice");
default xml namespace = n;
response =
<getquotereturn>
<result>{value}</result>
</getquotereturn>;
return response;
}
]]>
</documentation>
</service>
</deployment>
它首先定义了一套标准的命名空间(namespace)。 parameter 标记使您可以配置提供者的参数。这里 e4x 提供者支持的唯一参数是 type,它的值可以是 body 或是 envelope。在这个例子中,“body”意味着脚本仅仅需要处理消息体元素。 e4x 提供者使用 documentation 元素来存储脚本。这是个极其简便的实现方法――明智的方法往往不是偶然的!字符串 允许我们在其中填写任意字符,包括脚本中的 "" 与 <>。 脚本本身定义了一个独立的函数,它以一个 xml 参数作为输入同时输出一个 xml。使用语法 x..*::symbol 来摘取出符号。同时,这也是从 soap 主体中任意深度的任意命名空间来提取标记 symbol。如果与等于“ibm”,我们设置值为 983。(好,对于我们的股票程序,我们已经有一点点希望了)否则,我们返回 52.5。 我们使用在第 1 篇文章中描述的 {} 语法来创建响应: response = 为了测试它,首先要象上面描述的那样正确设置类路径,以使 axis 运行起来。我们假定 axis 运行在本地 8080 端口。在本例中使用 simpleaxisserver 已经足够了。现在键入: > java org.apache.axis.client.adminclient 如果前面的工作已正确完成,您将看到响应: processing file e4xstock.wsdd 现在,我们可以使用之前尝试的 mozilla 客户端。首先,浏览 axis 主页。您将看到如下所示的一些响应: 图 1. axis 服务主页 您可以点击 e4xstock 的(wsdl)链接,您将看到一个基本的 wsdl。您可以复制这个 wsdl 的 url 地址并将它粘贴到第 1 部分中的 mozilla 客户端。 如果您点击 update url 按钮,那么将获得服务的 url。 图 2. 使用 stockclient.html 尝试我们新的服务 现在,您可以点击 send。如果一切正常,您可以在浏览器里看到响应的 soap 消息结果:983。 请注意:通常,您必须安全地运行 axis。尽管这样还是有一定的风险,因为 e4x 提供者可以轻易的部署新脚本(甚至编写包含攻击型的代码)。记住基于 rhino 的脚本可以轻易的调用 java 代码。在实际生活中,您必须安全运行或是禁止 adminservice 来阻止它。 如果您想试试“信封”模型,那也是十分简单的。将下面的行: <parameter name="type" value="body"/> 改为: <parameter name="type" value="envelope"/> 下面一个脚本的片断将展示如何来创建信封: var s = new namespace("s", e.s::body.appendchild(response); 使用 e4x 编写简单编排服务 在这最终的章节里,我们将服务请求者与服务提供者放在一起来创建新的编排服务。这些脚本被表示为服务,但是也允许您用来调用其他服务。有效的方式是将一些请求者逻辑放置在一个提供者中。 这里有一个简单的例子。我们将调整股票报价例子,使之允许我们可以根据货币符号来得到不同的股票价格。为了实现这个,我们将调用两个服务――一个是股票报价服务,以及一个是货币服务――并在返回给 mozilla 客户端前综合它们的结果。幸运地,xmethods 也有一个货币服务。 我们的脚本包含下列完整的结构: function service(x) { /*body*/ 图 3. 简单的编排服务 每次查找 wsdl 是不现实的,但是它阻止了这篇文章中对端点 url 的修改,这里也展示了这种实现逻辑。更多的智能脚本可以缓存结果。但是,为了将这个用在 rhino 中,我们可能要编写一个 java 的 singleton 类。 首先要做的就是专门编写一个从 wsdl 中提取 url 的函数,因为从 wsdl 中提取 url 需要执行两次。 function getandparsewsdl(wsdlurl) { 您将注意到两件事情。首先,因为这些都将在服务器里运行,所以我们希望能够同步地执行所有这些操作,因为异步操作需要大量的”智能机制“,例如持久性保存与 web 服务寻址(ws-addressing)的支持。 其次,因为我们想使代码具有一些未来的实验,我们已经清除了 location 用来抽取第一个location。如果我们想做的更彻底一点,我们需要遵循 wsdl 结构来指定有我们期望的绑定的位置信息。 这里是获取货币符号的代码: function getcurrencyvalue(country, url) { 大多数情况下,xml 文档将从由测试客户端发出的 soap 消息中获取。仅有的改变是使用 {country} 来嵌入国家代码参数。 我们已经获取了股票值的代码了。完整的脚本在文件 currencystock.wsdd 中。主方法如下所示: function service(x) { 如果您已经有了上面的 axis 例子,您可以使用下面的命令进行简单的部署: < java org.apache.axis.client.adminclient -l "http://localhost:8080/axis/services/adminservice" currencystock.wsdd 客户端 currencystock.html 是 stockclient.html 的一个改进版本,它包含了国家代码。一旦您部署了该服务,那么就可以浏览已经部署的服务列表: http://localhost:8080/axis 复制 wsdl 的 url 到客户端浏览器中并点击 update url。现在您可以发出请求。客户端仍旧是异步的,但是服务器是同步的,您所请求的线程将阻塞直至它完成了 4 个独立的 http 请求(两个 wsdl 查找和两个服务请求)。 结束语 在这两篇关于 e4x 与 web 服务的文章中,我们讲述了大量的基础知识。首先,我们学习了如何使用 e4x 及一些简单的 xml 操作。然后我们讲述了使用 e4x 构建一个简单的浏览器接口,在 mozilla 中的 xmlhttprequest 支持用来发送 web 服务请求及解析结果。我们使用这一模型来创建一个 ajax 方式的客户端,它通过您输入的符号更新股票价格。 我们还讲述了服务器端,简单的 axis 提供者,开始使用 e4x 创建一个新的 web 服务。最后,我们将这些服务组合到一起来创建一个新的服务,它将货币与股票信息综合到一个服务中。 这篇文章中,我们自始至终关注于不同模型的 web 服务。我们使用简单自然的 soap xml 或主体元素, 而不是使用 wsdl 和工具创建 stubs 或者服务的 skeletons。虽然这种方式并不象使用语言工具那样强大,例如 java 和 c#,但是 e4x 是一个简单可行的选择。 当提到创建中间或编排服务时,从一个请求中提取信息或者 xml 元素,并将之放到另一个中。正如上面提到的代货币符号的股票例子,这一模型就变得非常的强大了。
<getquotereturn>
<result>{value}</result>
</getquotereturn>;
-l "http://localhost:8080/axis/services/adminservice" e4xstock.wsdd
(所有这些必须输入在同一行)
<admin>>done processing</admin>
"http://schemas.xmlsoap.org/soap/envelope/");
var e = <s:envelope xmlns:s={s}/>;
//e.s::header="";
e.s::body="";
// create response element here
return e;
0. extract stock symbol from request body
1. extract currency symbol from request body
2. get wsdl for stock service and extract location
3. get wsdl for currency service and extract location
4. call stock service
5. call currency service
6. multiply
7. create response body
8. return
}
var xh = new xmlhttprequest();
xh.open("get", wsdlurl ,false);
xh.send(null);
var resp = getasxml( xh.responsetext)
return resp..*::address.@location[0];
}
var env = <s:envelope
xmlns:c="urn:xmethods-currencyexchange"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:body>
<c:getrate>
<country1>us</country1>
<country2>{country}</country2>
</c:getrate>
</s:body>
</s:envelope>
response = execservice(url, env);
return response..result;
}
var symbol = x..*::symbol[0].tostring();
var country = x..*::country[0].tostring();
var currurl = getandparsewsdl(
"http://www.xmethods.net/sd/2001/currencyexchangeservice.wsdl");
var stockurl = getandparsewsdl(
"http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl");
var rate = getcurrencyvalue(country, currurl);
var ticker = getstockquote(symbol, stockurl);
var response =
<n:getquoteresponse xmlns:n="urn:xmethods-delayed-quotes">
<result>{rate*ticker}</result>
</n:getquoteresponse>
return response;
}
阅读关于 ajax web服务 xml 的全部文章