内容


在服务器端使用 E4X 和 Jaxer

使用 JavaScript 和 Jaxer 构建一个 Web 应用程序

Comments

简介

在本文中,我们将用 Jaxer 构建一个 Web 应用程序,它在客户端与服务器端都使用 JavaScript。我们假设您已经有了一些 Web 开发经验,特别是 JavaScript 开发的经验。由于我们将使用 JavaScript 特别是 E4X 来处理 XML,所以熟悉 XML 将会对您有帮助。这个应用程序是用 Aptana Studio™ 1.2.1 开发的,但不使用 Aptana Studio 开发 Jaxer 应用程序也是可行的。因为 Jaxer 1.0.2 是与 Aptana Studio 绑定在一起的,所以不必单独下载。参见 参考资料 获得下载链接。

Jaxer 概述

Jaxer 允许使用 JavaScript 作为服务器端编程语言。JavaScript 编程与 Java™、PHP、Python 或 Ruby™ 编程一样,都可以访问数据库、读取并写入文件,或与其他服务器通信。但 Jaxer 可以让您仅用 JavaScript 这一种编程语言开发出一个流行的 Web 应用程序。如果您创建过动态、交互的 Web 应用程序,相信您已经进行过很多 JavaScript 编程。如果您创建过大量 Ajax,那么可能花费了大量时间在 JavaScript 与服务器端使用的编程语言之间来回转换。现在,您可以通过使用 JavaScript 来简化开发工作。在我们开始使用 Jaxer 前,我们先看看 Jaxer 的架构,以便更好地理解 Jaxer 应用程序是如何工作的。

Jaxer 架构

Jaxer 在服务器端使用 JavaScript 很显然不太符合常规,但是它的架构是很标准的。Jaxer 在 Apache Web 服务器之上运行,并且它是一个 Apache 模块:mod_jaxer。这个架构与在 PHP、Python 或 Ruby on Rails(使用 Phusion Passenger™)内看到的无异,也与结合 Java Web 容器(比如 Tomcat)使用 Apache(使用 mod_jk)的架构没什么区别。图 1 显示了此架构的概要视图。

图 1. Jaxer 架构
Jaxer 架构
Jaxer 架构

这是一个非常典型的服务器端架构。Jaxer 还共享了服务器端技术(如 PHP)的其他特点。它采用了一种无共享方法(尽管它会自动地将会话数据存储到一个通用数据库)。

那么,Jaxer 是如何在服务器端实际运行 JavaScript 呢?Jaxer 使用了 Mozilla® 的 SpiderMonkey JavaScript Virtual Machine。这与 Firefox® 3.0 中使用的是同样的 JavaScript 引擎。这让 Jaxer 能够提供很好的性能,甚至可以媲美 PHP。有了 Jaxer,您可以使用 Firefox 中所支持的任何 JavaScript,而不限于 Firefox 和 Internet Explorer 共同支持的那一小部分 JavaScript。在后面您将会看到,它还包括一些函数编程结构,如 Array.mapArray.reduce,特别是用于解析和生成 XML 的 E4X。现在,您应该对 Jaxer 的工作原理有了一定的了解,接下来我们来看看在开始使用 Jaxer 前还需要安装些什么。

安装 Jaxer

Jaxer 可用于绝大多数平台,包括 Windows®、Mac OS® X、Linux® 和 Solaris™。因为 Jaxer 是与 Apache 绑定的,所以不必再下载其他东西。不过,一个使用 Jaxer 的更简单的方法是下载 Aptana Studio(参见 参考资料)。您可能已经这么做了,因为对 JavaScript 开发人员来说,它是一个很好的 IDE,但它还包括了一个嵌入的 Apache/Jaxer 服务器。借此,您就可以编写自己的 Web 应用程序并立刻在 Jaxer 上运行它。Aptana Studio 是一个 Eclipse 插件。您可以把它作为一个独立的应用程序下载,且捆绑了 Eclipse,如果您已经在使用 Eclipse ,也可以将其作为插件下载。

启动 Aptana Studio 后,不妨尝试一下 Jaxer shell。此 shell 让您能键入 JavaScript 并直接在 Jaxer 内执行它。它是直接在服务器上测试和调试代码的一种最佳方式。图 2 是使用 Jaxer shell 探索某些 Jaxer API 的一个例子。

图 2. Jaxer shell
Jaxer shell
Jaxer shell

要创建使用 Jaxer 的应用程序,可以创建一个新项目并选择 Aptana Projects > Default Web Projects。在此向导内,会看到一个有关添加 Jaxer 支持的屏幕。一旦创建了项目,就可以右键单击任何一个 HTML 文件并选择 Run As > JavaScript Web Application。这将把文件部署到 Jaxer 并在默认的 Web 浏览器中打开此页面。如果使用的是 Firefox,Jaxer 有一个插件,能使用流行的 Firebug 插件来调试 JavaScript。这个 Jaxer 版本允许在 Aptana Studio 内调试真正的代码。了解并开始使用了 Aptana Studio 和 Jaxer 之后,我们就可以构建一个示例应用程序来展示 Jaxer 对服务器端 E4X 的支持。

示例应用程序:用 JavaScript 查找 Flickr

现在,就可以创建这个示例应用程序了。为了简便起见,我们将创建一个能从流行的照片共享站点 Flickr 搜索照片的应用程序。此应用程序将使用一个 Flickr Web 服务来搜索具有给定标记的所有照片。Flickr 将会以 XML 给出这些数据,而在服务器上使用 E4X 处理此 XML。我们先来看看此 XML,然后再研究如何使用 E4X 处理它。

用 E4X 解析 XML

Flickr 的 Web 服务能够以多种格式提供数据,比如 XML、JSON,甚至序列化的 PHP。默认的格式是 XML。清单 1 给出了来自 Flickr 的 Web 服务的示例输出。

清单 1. 来自 Flickr 的示例 XML
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<photos page="1" pages="20626" perpage="16" total="330003">
    <photo id="3284086892" owner="49356365@N00" secret="a072efd762" 
server="3294" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3283258525" owner="49356365@N00" secret="30c9b39aa1" 
server="3409" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3284079858" owner="49356365@N00" secret="798d6ca888" 
server="3517" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3284077350" owner="49356365@N00" secret="c27902242e" 
server="3413" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3283262179" owner="49356365@N00" secret="2b2e36cafd" 
server="3149" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3284084794" owner="49356365@N00" secret="57afa6f5f8"
server="3294" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3284081008" owner="49356365@N00" secret="5cd1b5a932" 
server="3435" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3284085910" owner="49356365@N00" secret="0a51105dcd" 
server="3487" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3283263823" owner="49356365@N00" secret="944da852f9"
server="3407" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3284082986" owner="49356365@N00" secret="2d0f3c3755" 
server="3282" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3283236151" owner="49356365@N00" secret="2299cca4e1" 
server="3146" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3283249899" owner="49356365@N00" secret="e23d563cef" 
server="3623" farm="4" title="Snow Day" ispublic="1" isfriend="0" isfamily="0" />
</photos>
</rsp>

如果必须要像这样用服务器端 Java 代码甚至是客户端 JavaScript 代码解析数据,您可能会使用 DOM API。这虽然很直观,却会导致出现非常冗长的代码。有了 E4X,这变得更为简单。清单 2 所示的代码可用来解析来自 Flickr 的 XML。

清单 2. 用 E4X 解析 Flickr XML
/*
 * Parses XML in the following format:
 *     <photo id="3283249899" owner="49356365@N00" secret="e23d563cef" 
 *   server="3623" farm="4" title="Snow Day" ispublic="1" isfriend="0" 
 *   isfamily="0" />
 */
function FlickrPhoto(xml){
    this.id = xml.@id.toXMLString();
    this.owner = xml.@owner.toXMLString();
    this.secret = xml.@secret.toXMLString();
    this.server = xml.@server.toXMLString();
    this.farm = xml.@farm.toXMLString();
    this.caption = xml.@title.toXMLString();
    this.url = "http://farm"+ this.farm + ".static.flickr.com/"+
    this.server +"/"+ this.id +"_"+ this.secret +".jpg";
    this.toHtml = function(){
        var html = <img src={this.url} alt={this.caption} 
title={this.caption}></img>;
        return html.toXMLString();
    }    
}

开始部分是注释,然后才是代码。它展示了作为输入参数的 XML 的类型。它是来自 清单 1 中的示例 XML 的一个照片元素。对于此照片元素的每个属性,比如 id、owner 等,此值都会映射给称为 FlickrPhoto 的 JavaScript 类的一个具有相似命名的属性。所以,如果要访问此 XML 的服务器属性,可以使用 xml.@server.toXMLString()toXMLString 方法返回此数据的一个字符串表示。

上述 url 属性集可以构造指向来自 Web 服务所提供数据的图像。更为有趣的是 toHtml 方法。注意,这里使用的是一个 XML 常值(literal)。E4X 不仅非常适合访问 XML 格式的数据,而且也适合创建 XML。当然,HTML,更准确些是 XHTML,也是 XML。所以可以使用 E4X 创建一个 HTML 表达式。在本例中,它是一个图像标记。我们不妨将其作为字符串进行构造,但是 E4X 更为优雅。请注意我们是如何将此 url 和 caption 属性直接嵌入到 XML 表达式内的。那么,如何才能从 Flickr 获得 XML 并放入到这些对象内呢?为此,需要使用一个代理(proxy)。

使用代理

在浏览器内,可以用 XMLHttpRequest 进行远程调用,但只有当它来自于与您的 Web 页面相同的域时,才可以使用。这就排除了对 Flickr 的直接调用,需要通过一个代理来从服务器调用它。Jaxer 可以使这个过程变得很容易。只需像往常一样在一个脚本元素中声明 JavaScript,但需要添加一个额外属性以让 Jaxer 知道您想把它用作一个代理。参见 清单 3

清单 3. 采用代理的 Flickr 搜索页
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <title>Flickr Search</title>
    <script type="text/javascript" runat="server-proxy">            
// server side code goes here        
    </script>
    <script type="text/javascript">
        function getData(){
            var tag = document.getElementById("tag").value;
            var response = getFlickr(tag);
            document.getElementById("out").innerHTML = response;
        }
    </script>
    </head>
    <body>
        <div>
            <span>Search:</span>
        <input type="text" id="tag"></input>
            <input type="button" value="Show Photos" onclick="getData()"/>
      </div>
    <div id="out">Output will go here</div>
    </body>
</html>

请注意第一个脚本标记。它将 runat 设置为 server-proxy。这会告诉 Jaxer 在此块内的所有代码均将在服务器上运行,但在客户端也是可调用的。Jaxer 会在客户机上创建代理对象。

在本例中,我们得到的是一个简单的 HTML 表单,此表单具有一个文本输入字段和一个按钮。当用户单击按钮时,就会调用 getData 函数。此函数接受在文本字段内所输入的数据并将其传递给 getFlickr 函数。但该函数又在何处呢?它在服务器上定义。Jaxer 库为其创建一个代理以便它能被 清单 3 所示的客户端脚本调用。此函数将访问 Flickr Web 服务。现在,我们来看看这是如何工作的。

调用 Web 服务

在服务器上,同样可以使用在客户机上使用的任何东西。比如,可以使用 XMLHttpRequest 调用一个远程 Web 服务。不过,服务器具有较少的限制,所以 Jaxer 包含了一些 API 以便简化操作。清单 4 给出了相关代码。

清单 4. 在服务器上调用 Flickr
function getFlickr(tag){
    const API_KEY = "Your Key Goes here";
    var url = "http://api.flickr.com/services/rest/?method=" +
        "flickr.photos.search&per_page=16&api_key="+ API_KEY + 
        "&tags="+tag;
    var raw = Jaxer.Web.get(url);
    Jaxer.Log.debug("Got data from Flickr=" + raw);
    var rsp = new XML(raw.replace(/<\?(.*?)\?>/,''));
    var photos = [rsp.photos.photo[i] for (i in rsp.photos.photo)];
    photos = photos.map(function(x) new FlickrPhoto(x));
    var html = photos.reduce(function (previous, current){
        return previous + current.toHtml();
    }, "");                
    return html;
}

清单 4 所示代码十分简明,但却功能丰富。首先,创建指向此 Flickr Web 服务的 URL。此外,还需要一个 API 键来调用 Flickr。请注意,API_KEY 变量被声明为 const。这是 JavaScript 1.5 的一部分,但 Internet Explorer 并不支持。从字面上不难看出,它将此变量声明为只读常量。

接下来,调用 Flickr Web 服务,我们使用 Jaxer.Web.get API。这是 Jaxer 库的一部分,允许对任何的远程服务器进行同步调用,无任何限制。它比 XMLHttpRequest 更容易使用。可以接受其 结果并使用另一个 Jaxer API Jaxer.Log.debug 来记录来自 Flickr 的数据。可以直接从 Aptana Studio 访问这些 Jaxer 日志记录。而且可能还会需要调整日志记录的级别,因为在默认情况下,它只记录 ERROR 级别的消息。

来自 Flickr 的数据被解析成了一个 XML 对象。请注意,首先在其上运行了一个正则表达式。此正则表达式删除了 XML 声明(<?xml version="1.0" encoding="utf-8" ?>),因为它不是 E4X 所预期的。有了 XML 对象,就能通过表达式 rsp.photos.photo 得到照片元素的列表。请注意这是如何与 清单 1 中的元素结构相匹配的。

下一个表达式(以 var photos 开始)即便是一个有经验的 JavaScript 开发人员也未必熟悉。它是一个数组推导式(array comprehension),而且是 JavaScript 1.7 的一部分。同样,由于跨浏览器的要求,在 JavaScript 内并不会经常看到这类函数编程的风格。如果对 Python 十分熟悉,不难发现它非常类似于 Python 内的列表推导式(listing comprehension)。在本例中,我们对 rsp.photos.photo 对象进行迭代。这是一个 XMLList 对象,即一个列表,其中的每个元素都对应于来自 清单 1 所示的 XML 的一个 photo 节点。这个推导式创建一个数组,数组内的每个元素都是来自 XMLList 的一个 XML 元素。换言之,就是将 XMLList 转变为一个数组。为何这么做呢?这是因为此 Array 类具有比 XMLList 更好用的 API。请继续阅读。

在照片 XML 对象处于数组内时,就可以对此数组调用 map 函数。这是另一个函数编程结构。它接受单个参数:一个函数,并将此函数应用于数组内的每个元素。结果是一个新数组,其中的第 i 个元素就是将此函数应用到原始数组的第 i 个元素后的结果。实际上,可以将数组推导式和映射的这两行代码结合在一起,但是这里将二者分开是为了充分展示利用服务器端 JavaScript 可以实现的强大功能。

最后,使用了一个更为先进的 JavaScript 特性。它就是数组上的 reduce 方法。这是在 JavaScript 1.8 内引入的,与数组推导式和映射一样,它非常类似于 Python 内的一个结构(在很多其他的函数语言中,它被称为 fold)。它接受两个参数:一个函数和一个初始值。该函数具有两个参数:previouscurrent。previous 参数有一个初始值,在本例中,是一个空白字符串。current 参数的初始值是数组内的第一个元素。而 previous 参数之后会被此函数的结果替代,并且这个过程还会针对数组的下一个元素重复,直到所有元素都用完为止。在本例中,起始时是一个空白字符串,然后将其连接(concatenate)到第一个元素的 toHtml 方法的调用结果。之后再将该结果连接到第二个元素的 toHtml 方法的调用结果,以此类推。最后,就将应用到数组的每个元素的 toHtml 的结果连接起来了。 表 1 演示了这一过程:

表 1. toHtml 应用到数组内的每个元素后的连接结果
IterationPreviousCurrentReturn value
0 """"
1""photos[0]photos[0].toHtml()
2photos[0].toHtml()photos[1]photos[0].toHtml()
+
photos[1].toHtml()
3photos[0].toHtml()
+
photos[1].toHtml()
photos[2]photos[0].toHtml()
+
photos[1].toHtml()
+
photos[2].toHtml()

最后,映射表达式会接受每个元素,应用它的 toHtml 方法并将这些值连接起来。如果回到客户机代码,就能看到所有这些结果均被传递回至客户机,并被用来在那里更新 DOM。这样一来,它就能够显示来自 Flickr 的所有图像了。

结束语

至此,您看到了 Jaxer 如何让开发人员仅利用 JavaScript 就能创建功能强大的 Web 应用程序。在服务器端运行 JavaScript 并不是什么怪事。它是一种非常现代、功能又强大的编程语言。尤其是它的 E4X 特性让您能够随意处理 XML 文档和创建 XML 表达式。服务器上的 JavaScript 具有很多其他的特性。强大的语言及在服务器和客户机中都能使用的能力,这二者的组合十分诱人。Jaxer 使之成为了可能,所以现在,您尽可以开始以新的方式使用您的 JavaScript 技能。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML, Web development, Open source
ArticleID=381382
ArticleTitle=在服务器端使用 E4X 和 Jaxer
publish-date=04132009