UML软件工程组织

使用 Ajax 操纵 Lotus Notes 文档
Joachim Dagerot (jd@dagerot.com), 系统架构师及技术领头人, Strand Interconnect

使用 Ajax,您可以轻而易举地即时向用户提供反馈,而不必再耗费大量时间重新载入用户所使用的页面。本文将带领您发现视图和代理如何帮助您构建 Ajax 驱动的应用程序,并教您如何定义一个 API,使客户机和 Domino 服务器协作。

每个人都曾经使用过某种类型的 To Do 列表系统。此类系统通常包含一些必须快速、易用的数据小段。To Do 列表也是演示 Asynchronous JavaScript and XML(Ajax)技术与 Lotus Domino 的完美范例,本文将为您介绍如何使用 Ajax 设计模式来构建一个名为 ToDo-list 的 Lotus Notes 应用程序,此应用程序为用户提供其任务的即时反馈和更新。

本文为您介绍整个 ToDo-List 应用程序的创建过程,还提供了示例应用程序的下载,以帮助您按文章逐步操作。应用程序使用 Domino 代理来处理 Web 浏览器发送的 Ajax 请求,如创建和更新 Notes 文档,还使用了一个 Domino 视图来生成应用程序中使用的 XML。本文的目标读者为具备 JavaScript 和 XML 知识、有一定经验的 Notes/Domino 应用程序开发人员。

如需了解 Ajax 的更多内容,请参见本文 “参考资料” 一节,获得介绍性文章。

ToDo-list 应用程序维护一份 To Do 项目的记录,各项包含 Subject 和 Status 字段。Subject 字段保存关于各项目是什么的信息(如图 1 所示),Status 字段包含一个 0 或 1,0 表示未完成的活动,1 表示已完成活动。

图 1. Ajax 驱动的 ToDo-list 应用程序

Ajax 驱动的应用程序的力量

Ajax 驱动的应用程序可增加任何基于 Web 的应用程序的实用性,由于其额外的安全性和分类化的视图,Lotus Domino 服务器为富客户机应用程序提供了出色的存储库。

Lotus Domino 提供的设计元素使得在 Domino 平台上开发 Ajax 解决方案变得极为简单。可轻松构建支持会话(出于安全性和个性化方面的考虑)的 Domino 代理来处理您的 Ajax 提交。此外,您可配置一个 Domino 视图来提供 HTML、XML 甚至 JavaScript Serialized Object Notation(JSON)。成功的 Ajax 实现所需的一切都有。

现代 Web 浏览器和成熟的 Document Object Model(DOM)使为用户创建功能丰富的应用程序比以往任何时候都要容易。例如,我们通常会在项目中包含提前键入或建议字段,用户键入时可在其中接收到建议,以使名称查找更为方便。在另外一个项目中,我们载入了一个带有小对话框的门户页面(即 <DIV>)。各 <DIV> 使用 Ajax 调用在后端进行填充。部分对话框每隔 30 秒自动更新,为管理者提供来自 Enterprise Resource Planning(ERP)系统的新信息。

第三个示例是另外一个门户项目,其中各对话框的标题栏中带有一个小小的 “Add to my page” 图标。当用户单击此图标时,一个请求将在后台发往服务器,其中带有用户希望订阅的对话框的相关信息。服务器以如下两条消息之一:“The box is now on your personal page” 或 “You have that box already!” 进行响应。

在所有这些应用程序中,我们都必须从指定 API 入手。指定不当的 API 会毁掉整个项目。

API

在您开始为 ToDo-List 应用程序编码之前,必须决定客户机和服务器间传输的信息格式。您的系统架构可选择其所需的任意交换格式,但近来这种格式通常是 XML、JSON 或简单文本。

XML、JSON 或简单参数

为使两台计算机能够彼此通信,您必须设置一个定义良好的规则,以便使发送方和接收方都能了解如何去处理每一段信息。这些规则可以复杂、详尽而灵活,也可以简明扼要且易于采纳。

如果可随意选择格式,我们建议您优先考虑可读性和完整性,其次才是维持小规模和低开销。可选格式如下(见表 1 给出的归纳):

* XML。XML 格式极为适合结构化和定义良好的数据,在这种情况下,服务器和客户机都完全了解数据的形式。但 XML 略显冗长,较老的浏览器的 XML 实现还有一些古怪的规范。幸运的是,那些古怪的规范不会影响本文中介绍的现代 Ajax 驱动的示例。
* HTML。HTML 极为适合从服务器将数据发送到 Web 浏览器以便直接呈现,与 XML 比较时,其优势更为明显,使用 XML 时,客户机必须首先将内容转换为 HTML,然后才能呈现内容。在浏览器必须分析数据并对其作出反应时,不建议使用 HTML。
* JSON。JSON 对于人类来说可读性较好,同时规模小,开销低。对于 JavaScript 语言来说,JSON 是本地的,但需要在 Java 服务器端进行额外的编码工作。
* CSV。Comma-Separated Value(CSV)或许是最常用也经过最充分验证的两系统间数据交换方法。有些人甚至争辩说 CSV 适于人类阅读,但我们不支持这种观点。CSV 没有完整性检查,但也没有任何开销。
* Text。为信息交换使用纯文本是恰当的,例如,在 URL 这样的值对组合中。

表 1. 用于信息交换的不同格式概述

 

XML

JSON

HTML

CSV

Text

开销

非常轻

完整性检查

非常好

非常少

人类可读性

不可能

在本文的示例中,您将为发送到后端系统的请求使用文本格式,为响应使用 XML 格式。您也需要花费一点时间和精力来定义 XML。这并不像定义另外一个可放在纯 Lotus Notes 环境中的字段那样容易。

定义 XML 格式
使用 Lotus Domino 的内置 XML 生成器
在请求一个完整的文档或设计元素时,您还可使用 Lotus Domino 的本地 XML 格式 —— Domino XML(DXL)。这种方法的优点之一就是您可在服务器端编写很少的代码,而是使用内置的 DXL 生成器。

在本文稍后部分,您将定义客户机可向服务器发送的方法。但现在,您必须确定要使用那种 XML 方言来定义一个或多个 To Do 项目。ToDo-list 应用程序中的各 To Do 项目包含 3 个值:key、subject 和 status。key 是保存您的 To Do 项目的 Lotus Notes 文档的 Universal ID(UID)。subject 只是用户希望执行的任务或活动。status 为 0 或 1,0 表示未完成的任务,1 表示已完成的任务。

在本示例中,服务器通过一个更新的项目列表响应所有调用。在实际环境中,响应更多地连接到服务器执行的活动。本例的下一步不仅包括回发更新的项目列表,还包括提供状态信息,以使客户机了解服务器进行了怎样的操作,如以下代码清单所示。

<response>
<response status></response status>
<document>
<key></key>
<subject></subject>
<status></status>
</document>
<document>
<key></key>
<subject></subject>
<status></status>
</document>
</response>

现在,设置好 API、交换格式和 XML 标准后,即可开始开发服务器端功能了。此后,即可将精力集中在客户端代码。但在着手处理任一任务之前,您必须确定服务器将对您的文档执行怎样的操作。

创建客户机到服务器的调用

客户机上生成的各调用必须包含一个 action 参数,以使服务器了解执行怎样的活动。有时,还有必要发送额外的参数 —— 例如,若您希望来回切换一个项目的状态。在此类情况下,活动为 ToggleStatus,额外参数必须包含关于要为哪个文档来回切换状态的相关信息,以使 Domino 服务器可在正确的文档上执行正确的活动。

来自客户机的所有调用都是使用带有一些 URL 参数的 HTTP GET 方法完成的。下例是一个包含 method 和 key 参数名的完整 URL。

http://host/ibm/ajaxdemo.nsf/AjaxHandler?open&method=deleteDocument&key=88877766fefe5678

服务器应处理怎样的活动?

在您的 API 中,一个 Web 页面可能使用许多种不同的方法 —— 例如,createDocument、updateDocument、deleteDocument、getDocument 和 toggleStatus。各方法接受一个或多个参数。创建、更新、删除和切换状态活动均更改数据库中的信息,这样的更改必须反映在 Web 页面的 To Do 项目中。反映此更改的方法之一就是每次在数据库中进行更改时使客户机执行一个额外的请求,来获得项目列表。更好的解决方案就是设置代理,每次调用代理时即返回更新的 To Do 列表(本文稍后将介绍更多详细内容)。

服务器必须对表 2 所示定义好的活动作出反应。

表 2. 定义好的活动

方法 用途 参数 返回结果
CreateDocument 创建一个新的 Lotus Notes 文档,并使用所提供的数据填充其字段 字段列表,各字段参数包括字段名、类型和值 包含所有项目的 XML 对象
UpdateDocument 通过提供文档标识符、字段名和要更改的值请求更新 文档惟一的 ID 和一个字段参数。字段参数包括字段名和新字段值 包含所有项目的 XML 对象
DeleteDocument 删除一个文档 要删除的文档的惟一 ID 包含所有项目的 XML 对象
GetDocument 返回文档内容 要检索的文档的惟一 ID 包含所请求项目的 XML 对象
ToggleStatus
切换指定 To Do 项目的状态

要为其切换状态的文档的惟一 ID
包含所有项目的 XML 对象

这 5 种活动就是您创建 Domino 服务器和 Web 浏览器间的完整 API 时所需的一切活动。接下来让我们看看服务器端代理代码。

服务器端

Domino 服务器确保所有 Ajax 请求都以恰当的方式得到处理。操纵数据的调用必须由服务器端的某种智能作出应答 —— 在本例中为代理。仅请求信息的调用可轻松依赖于 Lotus Notes 视图,后者是一种出色的 XML 源。

清单的视图

在本例中,您使用一个配置来显示 XML 的 Lotus Notes 视图。为避免 Lotus Domino 自行添加 HTML 代码,将视图配置为以 HTML 格式显示其内容。

在 ToDo-List 应用程序中,您只需要此视图中的一列,即可为系统中的各 To Do 项目生成完整的 XML 代码。第一列列出主题,此列是隐藏的,仅用于设置排序规则(参见图 2)。

图 2. 显示 XML 视图的 Lotus Domino Designer

除此视图外,您还需要一个与视图相关的 $$viewtemplate 窗体。窗体中包含图 3 所示的 XML 打包代码。

图 3. $$viewtemplate 窗体的一部分

显示现有信息非常重要,图 3 所示的解决方案可完美地满足您的需求。现在,您必须创建对您的请求做出应答的 Lotus Notes 代理。

用于后端命令的 Domino 代理
W3C 建议
切记,World Wide Web Consortium(W3C)建议,只要数据在后端发生更改,就使用 POST 方法,而为不更改服务器上任何信息的所有请求使用 GET 方法。

您知道了如何以一种最适合 Ajax 模式的方法从 Domino 后端系统中获取数据,那么您该如何使用 Domino 代理来接收 Ajax 调用呢?

首先,您必须确定应以 HTTP GET 方法还是 POST 方法调用代理。由于一个 To Do 项目只包含很少的一点信息,因此您可以使用 GET 方法,这也就意味着服务器的所有参数都是在 URL 字符串中发送的,如下所示:

/database.nsf/ajaxHandlerAgent?openagent&method=UpdateDocument&key=FFF&status=0

代理解析 URL 字符串中的参数,并根据方法参数中的值作出反应。此代码清单展示了 ajaxHandlerAgent 是如何根据方法参数采取必要的行动的。

UrlArguments args = new UrlArguments(); (1)
args.fromSession(session);

String method = args.getString("method");
if("ToggleStatus".equals(method)) {
try {
Document doc = thisDB.getDocumentByUNID(args.getString("key")); (2)
String newStatus="1";
if(doc.hasItem("status")) {
String currStatus = doc.getItemValueString("status");
if("1".equals(currStatus)) {
newStatus=="0";}
}
doc.replaceItemValue("status",newStatus);
pw.println("[" + "/" + thisDB.getFilePath() + "/XmlView?open]");
doc.save(true,false);
} catch(Exception e) {
pw.println(e);(3)
}
}

注意:此处的代理代码片段仅展示了 ToggleStatus 活动。如需获得此代理的完整代码及客户机的完整代码,请从 “下载” 一节下载。

请特别注意清单中的以下代码行: (1):UrlArguments 类是一个小型实用工具类,它将 URL 参数解析为散列表对象。 (2):在这一行中开始使用各 Lotus Notes 文档的 universalID 参数。 (3):本行中的错误处理在生产环境中是不足的,应在必要时加以修订。

您能否看出代理为将视图中的数据回发到客户机而执行的重定向操作?

使 Domino 代理服务于视图

使用 Ajax 的一个很大的优势就是您可以为用户提供来自服务器的即时、实时的信息。因此,在您的 ToDo-List 应用程序中,您将一个新的项目列表作为对所有服务器请求的响应回发。这是在代码的下一行中完成的,即让代理执行到您的 XML 视图的服务器端重定向操作。Web 浏览器随后获取 XML 视图的内容,将其作为对代理的所有调用的响应。

pw.println("[" + "/" + thisDB.getFilePath() + "/XmlView?open]");

客户端

在浏览器上,您需要一个函数来处理 Ajax 请求、一个响应信息的占位符、一个或多个用户可交互的热点。交互点可以是任意能触发事件的对象。最常见的类型就是输入类型按钮和锚定链接(见图 4)。如果您的用户熟悉分类视图,那么您就可以了解交互点应采取的形式。Ajax 能进一步优化此实现:在用户展开视图时仅载入一个类别,从而可使您节省大量开销,并使您的用户受益。

图 4. ToDo-list 应用程序中的交互点

在图 4 中,所有可用热点均以粉色高亮显示。不同热点的 HTML 有所不同。REFRESH! 和 Create new todo! 链接是静态代码,而状态符号(红色的 X 及绿色的对号)链接及项目主题必须动态呈现。在此应用程序中,您设置了一个 helper 函数,此例程依赖于 Domino 服务器提供的图标。无论为用户生成可视化响应需要哪些 HTML 和 JavaScript 代码,向服务器发送的请求都是使用 XMLHttpRequest 对象以相同的方式设置的。

设置和处理 Ajax 请求

创建实际的 XMLHttpRequest 对象相当轻松。在更为复杂的解决方案中,某些类型的队列处理程序应是关注的中心。但现在您可使用以下代码清单中显示的代码。

var xmlHttp;
function createXMLHttpRequest() {
if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}
}

创建、发送和检索

由于可读性方面的原因,将 API 中的各方法映射到客户端的一个方法和服务器端的同名方法更为容易。每当用户单击状态图标(绿色的对号或红色的 X)时,就会调用下一段代码清单中显示的 JavaScript 函数。首先声明一个函数(02),每当 xmlHttp 对象接收到状态更改时(12),即执行此函数。

在第(10)行,填充了全局变量 Http;第(11)行构建添加到 URL 中的参数。第(13)行中,Ajax 对象被初始化,以便在第(16)行中发送。

01 function toggleStatus(key) {
02 this.handleStateChange = function() {
03 if(xmlHttp.readyState == 4) {
04 if(xmlHttp.status == 200) {
05 refreshTodoList(xmlHttp.responseXML);
06 }
07 }
08 }
09
10 createXMLHttpRequest(); // build update string
11 var args='&method=ToggleStatus&key=' + key;
12 xmlHttp.onreadystatechange = this.handleStateChange;
13 xmlHttp.open("GET"
14 ,"./HandleAjaxRequests?open" + args
15 ,true);
16 SMLHTTP.send(null);
17 }

在第(05)行中将响应进一步发送到 XML 解析器之前,有两次检查。

跨服务器实现的解决方案

如今的 XMLHttpRequest(也称为 XHR)实现均带有安全性约束,阻止不同域处理请求,因此在涉及多台服务器时,使用 Ajax 解决方案更为困难。我们不会详细探讨如何重写这一约束或是否该重写这一约束,而是给出一些替代解决方案。尽管需要编写更多代码,但至少可使用户获得与使用 XMLHttpRequest 实现时相同的用户体验:

* iframe 方法是在 XMLHttpRequest 对象得到普遍实现之前获得 Ajax 功能性的常见方法。通过将内容载入一个隐藏的 iframe 对象并在后台加以处理,即可获得与使用 XHR 解决方案时几乎相同的可能性。
* 当今,在 XHR 调用非原始服务器时,代理方法是最好或许也是最常用的一种解决方案。使用这种方法时,您必须使代理位于一台提供 Ajax 页面的服务器上。随后代理即可检索所有 Ajax 调用,并将其转发到正确的目的地。
* 代理设计模式也可添加缓存和一些内容验证,因此在所有内容均加载自同一服务器时,这是一种不错的解决方案。

结束语

Ajax 设计模式极为适合用户,因为他们执行每个活动时,都会接收到直接响应。在出现通信错误或类似情况时,这种模式也非常适合保持服务器端数据完整。但在全面部署 Ajax 解决方案之前,Domino 开发人员或架构师应考虑一些事项。

首先,XMLHttpRequest 对象并非标准 —— 即使说存在通过此对象创建标准的 W3C 项目。另外一个值得考虑的问题就是服务器性能。Ajax 解决方案可能不会提高带宽需求,但会在事务数量方面给 Domino 服务器造成压力。第三,您可能不想构建不支持 Web 浏览器的 Back 按钮功能的解决方案。

考虑到这些缺点以及设计良好的 Ajax 驱动的解决方案所提供的用户体验,我们认为,只要 Ajax 能够提高可用性,您就可以选择 Ajax。本文还为您展示了给您的用户提供一些额外的元素、使其 Web 体验比使用典型面向页面的 Web 站点更为便捷有多么轻松。

 

版权所有:UML软件工程组织