作者:Naveen Balani, 技术架构师, Webify Solutions Rajeev Hathi, 高级系统分析员, Satyam 计算机有限公司
异步 JavaScript 和 XML(Asynchronous JavaScript with XML,AJAX)支持动态、异步的 Web 体验,却不需要页面刷新。在本教程中,将跟随一个示例图书订购应用程序的构建,学习构建基于 AJAX 的 Web 应用程序 —— 拥有实时验证,却不用页面刷新。
开始之前
关于本教程
在本教程中,我解释了如何开发和设计基于异步 JavaScript 和 XML(或者叫做 AJAX)的 Web 应用程序。您将构建一个基于 Web 的、提供实时验证和页面刷新、为用户提供有效和平滑的用户交互的示例图书订购应用程序。
|
前提条件
我们将使用 Tomcat 来运行 AJAX 应用程序。Tomcat 是 Java Servlet 和 Java 服务器页面技术的参考实现使用的 servlet 容器。请从 Jakarta Site 下载 Download jakarta-tomcat-5.0.28.exe 并运行它把 Tomcat 安装到喜欢的位置 —— 例如 c:\tomcat5.0。
请下载本教程的源代码和 Web 应用程序(在 wa-ajax-Library.war 中)。
AJAX 介绍
AJAX 基础
AJAX 支持动态、异步的 Web 体验,不需要页面刷新。它集成了以下技术:
- XHTML 和 CSS 提供了基于标准的表示。
- 文档对象模型(DOM)提供了动态显示和交互。
- XML 和 XSLT 提供了数据交换和操纵。
-
XMLHttpRequest 提供了异步数据检索。
- JavaScript 把每一样东西绑定在一起。
AJAX 技术的核心是 JavaScript 对象:XMLHttpRequest 。这个对象是通过浏览器实现提供的 —— 先是通过 Internet Explorer 5.0 然后通过 Mozilla 兼容的浏览器。请进一步观察这个对象。
XMLHttpRequest
使用 XMLHttpRequest ,可以用 JavaScript 发出到服务器的请求,并在不阻塞用户的情况下处理响应。在创建 Web 站点并用 XMLHttpRequest 在客户机浏览器上无刷新地执行屏幕更新的同时,它还提供了更多灵活性和丰富的用户体验。
XMLHttpRequest 应用程序的示例包括 Google 的 Gmail 服务、Google 的 Suggest 动态查询界面以及 MapQuest 的动态地图界面。在下一节,在演示图书订购应用程序的设计和实现时,详细介绍了如何使用 XMLHttpRequest 对象。
应用程序设计
应用程序的元素
基于 Web 的示例图书订购应用程序包含以下用 AJAX 实现的客户端函数:
- 订购 ID 验证
- 一个 View Authors 列表
- 一个 View Publishers 列表
这里的目的是介绍 Web 页面中的实时验证 和页面刷新 如何让客户交互更平滑、更有效。
应用程序的结构
图 1 的图表说明了示例图书订购应用程序的设计架构:
图 1. AJAX 架构
应用程序是一个使用 Java 服务器页面(JSP)技术开发的单一 Web 页面。用户可以用 Web 浏览器(例如 Microsoft? Internet Explorer)调用 Web 页面并输入应用程序要实时验证的订购 ID。在 ID 异步验证的时候,用户可以输入更多信息。用户可以根据作者 或出版者 查看图书的书名。屏幕会根据用户的选择填充作者列表 或出版者列表。根据选择,书名列表 会被填充。所有这些列表都是实时填充的 —— 换句话说,页面没有刷新,但是数据仍然来自后台层。我们把这种现象叫做实时刷新。
从 图 1 可以看出,XMLHttpRequest JavaScript 对象帮助进行实时异步处理。该对象采用 XML 格式通过 HTTP 对位于 Web 容器内的 LibraryServlet 发出请求。然后 servlet 查询数据库、提取数据并发送回客户机,还是采用 XML 格式通过 HTTP 进行传送。请求和响应都是在没有刷新页面的情况下实时发生的。
就是这一点使得 AJAX 如此强大。用户不需要等候页面重新载入才能完成,因为这里没有页面重载。
在 下一节 中,我将演示如何根据这个设计来实现图书订购应用程序。我要介绍代码并做一些分析。(要得到本教程的示例代码,请 下载文件 x-ajax-library.war。)
实现应用程序
用 AJAX 实现应用程序
在这一节,我们研究示例图书订购应用程序的代码,并进一步查看每个基于 AJAX 的 Javascript 组件:
查看代码:验证订购 ID
先从验证订购 ID 的函数 <input type="text" name="subscriptionID" onblur="validate(this.form)"/> 开始。这个代码生成文本字段,用户可以在里面输入订购 ID。用户输入 ID 并移到表单的下一个字段时,触发 onBlur 事件。这个事件调用 JavaScript 函数 validate() :
var req;
function validate(formObj) {
init();
req.onreadystatechange = subscriptionValidator;
req.send("subscriptionID=" + formObj.subscriptionID.value);
}
|
validate() 函数接受 formObj 作为参数。它首先调用 init() 函数:
function init() {
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP");
}
var url = "/Library/LibraryServlet";
req.open("POST", url, true);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
}
|
查看代码: init()
现在看 init() 函数的工作(我们把代码分成几部分):
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP");
}
|
init() 函数首先创建 XMLHttpRequest 对象。这个请求对象是 AJAX 的核心。它以 XML 格式发送和接收请求。这段代码检查浏览器对 XMLHttpRequest 对象的支持(多数浏览器都支持它)。如果使用 Microsoft Internet Explorer 5.0 以上版本,那么就执行第二个条件。
req.open("POST", url, true);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
代码创建了 XMLHttpRequest 对象之后,需要设置某些请求属性。在前面的代码中,第一行设置请求方法、请求 URL 和请求的类型(是否异步)。它通过调用 XMLHttpRequest 对象上的 open() 方法做这件事。
这里我们要使用 POST 方法。理想情况下,当需要在服务器上修改状态时,请使用 POST 。我们的应用程序并不修改状态,但我们仍然倾向于使用 POST 。url 是要执行的 servlet 的 URL。true 表明我们要异步地执行请求。
对于 POST 方法,我们需要设置 Content-Type 这个请求头。对于 GET 方法来说不需要这个设置。
function validate(formObj) {
init();
req.onreadystatechange = subscriptionValidator;
req.send("subscriptionID=" + formObj.subscriptionID.value);
}
|
查看代码:回调句柄 1
继续查看验证方法,下面把 subscriptionValidator 回调句柄分配给 onreadystatechange ,请求状态的每个变化都会触发它。
这个回调句柄 都负责什么呢?因为正在异步地处理请求,所以需要一个回调句俩,从服务器返回完整响应的时候调用它 —— 回调句柄是对订购 ID 进行验证的地方(也就是编写实际的验证代码的地方)。
句柄充当侦听器。它一直等待响应完成。(稍后 将详细介绍句柄代码 。)为了发送请求,最后一行调用了 send() 方法。请求以名称=值 对的形式发送。对于 GET 方法,请求作为 URL 的一部分发送,所以 send() 方法被传递了一个空参数。
请求被发送到 servlet。servlet 处理请求并实时地发回响应。这就是 servlet 处理请求的方式。下一个代码段表示了 LibraryServlet 的 doPost() 方法。
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
String ID = null;
ID = req.getParameter("subscriptionID");
if (ID != null) {
String status = "<message>" + this.validID(ID) + "</message>";
this.writeResponse(resp, status);
}
}
|
查看代码:回调句柄 2
doPost() 方法从请求参数得到 subscriptionID 。为了验证 ID,它调用 validID() 方法。这个方法验证 ID,如果 ID 正确,则返回 true ,否则返回 false 。它用 XML 格式构造返回状态,并调用 writeResponse() 方法来写响应。现在来看 writeResponse() 方法。
public void writeResponse(HttpServletResponse resp, String output) throws IOException {
resp.setContentType("text/xml");
resp.setHeader("Cache-Control", "no-cache");
resp.getWriter().write(output);
}
|
响应用 XML 格式发送。第一行设置响应的内容类型为 text/xml 。第二行把头 Cache-Control 的值设为 no-cache 。这个值是必需的。AJAX 要求响应的输出不能被浏览器缓存。最后一行调用 getWriter().write() 方法来写响应。
查看代码:回调句柄 3
请求由 servlet 处理,响应被发送回客户机。请记住,所有这些都在后台发生,没有页面刷新。现在 前面 讨论过的回调句柄方法会处理并解析响应:
function subscriptionValidator() {
if (req.readystate == 4) {
if (req.status == 200) {
var messageObj = req.responseXML.getElementsByTagName("message")[0];
var message = messageObj.childNodes[0].nodeValue;
if (message == "true") {
msg.innerHTML = "Subscription is valid";
document.forms[0].order.disabled = false;
} else {
msg.innerHTML = "Subscription not valid";
document.forms[0].order.disabled = true;
}
}
}
}
|
查看代码:回到 XMLHttpRequest
如前所述,XMLHttpRequest 对象是构造和发送请求的核心对象。它也负责读取和解析从服务器返回的响应。请看下面几部分代码。
if (req.readystate == 4) {
if (req.status == 200) {
|
前面的代码检查请求的状态。如果请求处在就绪状态,就读取和解析响应。
就绪状态是什么意思呢?当请求对象的属性 readystate 的值是 4 时,就意味着客户机接收到了响应而且接收完成。下面我们检查请求的状态(响应是正常页面还是错误页面)。为了保证响应正常,要检查状态的值是否为 200 。如果 status 的值是 200 ,就会处理响应。
var messageObj = req.responseXML.getElementsByTagName("message")[0];
var message = messageObj.childNodes[0].nodeValue;
if (message == "true") {
msg.innerHTML = "Subscription is valid";
document.forms[0].order.disabled = false;
} else {
msg.innerHTML = "Subscription not valid";
document.forms[0].order.disabled = true;
} }
|
接下来,请求对象通过调用 responseXML 属性读取响应。请注意 servlet 用 XML 格式发送回响应,所以我们使用 responseXML 。如果响应是以文本格式发送的,那么可以使用 responseText 属性。
在这个示例中,我们处理 XML。servlet 把响应构建在一个 <message> 标记中。要解析这个 XML 标记,请在 XMLHttpRequest 对象的 responseXML 属性上调用 getElementsByTagName() 方法。它得到标记的名称以及标记的子值。根据解析到的值,格式化响应并用 HTML 改写。
现在就完成了对订购 ID 的验证,没有页面刷新。
查看代码:查看作者、出版者和书名
其他的功能 —— 查看作者、查看出版者 和查看书名 —— 工作的方式类似。只是需要为每个功能定义独立的句柄:
function displayList(field) {
init();
titles.innerHTML = " ";
req.onreadystatechange = listHandler;
req.send("select=" + escape(field));
}
function displayTitles(formObj) {
init();
var index = formObj.list.selectedIndex;
var val = formObj.list.options[index].value;
req.onreadystatechange = titlesHandler;
req.send("list=" + val);
}
|
请记住,示例应用程序允许用户根据作者和出版者查看书名。所以显示的或者是作者列表 或者是出版者列表。在这类场景中,应用程序只能根据用户的选择调用一个回调句柄 —— 换句话说,对于作者和出版者列表,只有一个 listHandler 回调句柄。
显示书名列表需要使用 titlesHandler 。其余的功能仍然一样:servlet 处理请求,用 XML 格式写回响应。然后读取、解析、格式化响应,用 HTML 改写。可以用 HTML 把列表呈现为 select......options 标记。这个示例代码段显示了 titlesHandler 方法。
var temp = "<select name=\"titles\" multiple\>";
for (var i=0; i<index; i++) {
var listObj = req.responseXML.getElementsByTagName("list")[i];
temp = temp + "<option value=" + i +">" + listObj.childNodes[0].nodeValue
+ "</option>";
}
temp = temp + "</select>";
titles.innerHTML = temp;
|
迄今为止,我们演示了如何实现实时验证和刷新。使用 AJAX,可以选择多种方式给 Web 站点的用户交互添加特色。下面运行应用程序。
运行和测试应用程序
运行应用程序
请下载示例代码 wa-ajax-Library.war 并把它拷贝到 Tomcat 的 Webapp 目录(例如,c:\Tomcat 5.0\Webapps)。要启动 Tomcat 服务器,请输入以下命令:
cd bin
C:\Tomcat 5.0\bin> catalina.bat start
|
Tomcat 现在启动了,AJAX Web 应用程序也部署在其中。
测试应用程序
要测试应用程序:
- 请打开 Web 浏览器,指向
http://localhost:tomcatport/Library/order.jsp ,其中变量 tompcatport 是 Tomcat 服务器运行所在的端口。
将看到订购屏幕。
- 在 Enter Subscription ID 字段中,输入用户 ID(“John” 除外)并离开字段。
向服务器异步发出的订购 ID 请求会被验证。可以看到 “Subscription not valid” 消息,如 图 2 所示: 图 2. “Subcription not valid” 屏幕
应用程序在不刷新浏览器的情况下,异步地验证用户并提供运行时验证。
- 输入用户 ID 值 John。
将看到消息 “Subscription is valid”。订购有效之后,应用程序就激活 Order 按钮。
- 请选择 By Author 或 By Publisher 按钮分别填充作者或出版者下拉列表。
- 从下拉列表选择一个作者或出版者。
书名区域被动态填充(如 图 3 所示)。 图 3. “Subcription is valid”屏幕
在选择作者或出版者时,应用程序请求服务器在运行时提供来自服务器的与选中作者或出版者对应的书名信息。在不刷新浏览器的情况下显示书名信息。
现在成功安装和测试了示例 AJAX 应用程序。
结束语
结束语
AJAX 从开始至今已经走了很长的路。我们相信 AJAX 不仅仅可以用作设计模式,不过 AJAX 仍然有一些问题:
- 浏览器对
XMLHttpRequest 对象的支持可能是个限制。大多数浏览器都支持 XMLHttpRequest 对象,但是有少数不支持(通常是较老版本的浏览器)。
- AJAX 最适合显示少量数据。如果要处理大量数据并在列表中实时显示,那么 AJAX 可能不是合适的解决方案。
- AJAX 非常依赖于 JavaScript。如果浏览器不支持 JavaScript 或者用户禁止了脚本选项,那么根本就不能利用 AJAX。
- AJAX 异步的性质不会保证多个请求的同步请求处理。如果需要对验证或刷新进行优先处理,那么要相应地设计应用程序。
即使有这些可能的问题,AJAX 仍然是提高 Web 页面和解决页面重载问题的最佳解决方案。
Naveen Balani, 技术架构师, Webify Solutions Rajeev Hathi, 高级系统分析员, Satyam 计算机有限公司
|