2006 年 5 月发表
异步 JavaScript 和 XML (Ajax) 是一种组合了 JavaScript、文档对象模型 (DOM) 和 XMLHttpRequest 技术的 Web 开发技术,用于在客户端和服务器之间提供动态交互。
例如,假设用户填写一份表单以将数据添加到数据库表。如果不使用 Ajax,则在提交表单之前将不检查该表单中的数据有效性。有了 Ajax,可以在使用服务器应用程序中的业务逻辑向表单域中添加数据时对添加到表单的数据进行动态验证。因此,如果表单中的数据有效,则不必将完整的表单发送给服务器进行检查。
在本文中,您将学习如何在 Oracle JDeveloper 10g (10.1.3) 中使用 Ajax 创建一个 Web 应用程序。但首先,我们将介绍一些背景知识。
作为 J2EE 开发人员,您可能对 DOM 和 JavaScript 比较熟悉,而对 XMLHttpRequest 则并不一定很熟悉。在 Ajax 中,XMLHttpRequest 对象用于在 Web 应用程序与基础服务器和业务服务之间提供异步通信。使用 XMLHttpRequest 对象,客户端可以检索 XML 数据并将其直接提交给 Web 服务器,而不必重新加载该页。XML 数据使用 DOM 和可扩展样式表转换 (XSLT) 在客户端上转换为可呈现的 HTML。
XMLHttpRequest 最初以 ActiveX 组件的形式由 Microsoft 推出,现在已受到大多数现代浏览器的支持。但在 2006 年 4 月之前,XMLHttpRequest 在浏览器之间以不同的方式实现。例如,Internet Explorer 6 将 XMLHttpRequest 实施为 ActiveX 对象。XMLHttpRequest 对象的实例在 IE 6 中通过以下代码创建:
var req = new ActiveXObject("Microsoft.XMLHTTP");
而在 Internet Explorer 7 中,XMLHttpRequest 以窗口对象属性的形式提供。在 IE7 和更高版本中,XMLHttpRequest 对象的实例通过以下代码创建:
var req = new XMLHttpRequest();
2006 年 4 月,推出了 XMLHttpRequest 对象的 W3C 工作草稿。随着 XMLHttpRequest 对象成为正式的 W3C 标准,XMLHttpRequest 对象的实施将很有可能得到更进一步的标准化。XMLHttpRequest 包含各种可以提供 HTTP 客户端功能的属性和方法。表 1 中介绍了 XMLHttpRequest 属性。
表 1:XMLHttpRequest 属性
属性 |
说明 |
onreadystatechange |
设置异步请求的事件处理程序 |
readyState |
检索请求的当前状态。readyState 的不同值包括: 0 — 已创建但未初始化 XMLHttpRequest 对象。未调用 open() 方法。 1 — 已创建并初始化对象,但尚未调用 send() 方法。 2 — 已调用 send() 方法,但状态和标题不可用。 3 — 已检索到某些数据,但由于响应标题和状态并不完全可用,因此调用 responseXML 将生成空值。调用 responseText 生成部分数据。 4 — 已收到所有数据。 |
responseText |
从服务器中检索文本响应。 |
responseXML |
从服务器中检索 XML DOM 对象形式的响应。 |
responseBody |
检索响应主体。responseBody 属性是在 IE7 和更高版本的窗口对象中定义的,而非在 W3C XMLHttpRequest 规范中定义。 |
status |
检索请求的 HTTP 状态代码。状态代码的示例包括 404(表示“Not Found”)和 200(表示“OK”)。 |
statusText |
检索 HTTP 状态的文本。 |
表 2 中介绍了 XMLHttpRequest 对象方法。
表 2:XMLHttpRequest 方法
方法 |
说明 |
Abort() |
取消当前的 HTTP 请求。 |
getAllResponseHeaders() |
返回所有响应标题。如果 readyState 的值不是 3 和 4,则返回空值。 |
getResponseHeader(string header) |
返回指定的响应标题。如果 readyState 的值不是 3 和 4,则返回空值。 |
open(string method, string url, boolean asynch, string username, string password) |
打开 HTTP 请求,但不发送该请求。调用 open() 方法会将 readyState 属性设置为 1。responseText、responseXML、status 和 statusText 属性设置为其初始值。需要在 open() 方法中指定 HTTP 方法和服务器 URL(相对 URL 或绝对 URL)。asynch 的布尔值指定 HTTP 请求是否是异步的;不需要指定布尔值参数,并且默认值为 true。用户名和密码是针对服务器端验证指定的,并且可选。 |
send(data) |
向服务器发送 HTTP 请求并接收响应。使用 send() 方法发送的数据可以是字符串、无符号字节数组或 XML DOM 对象。使用 send() 方法发送的数据是可选的,并且可能为空。根据 open() 方法中的 asynch 参数值,send() 方法为同步或异步。如果为同步,则在检索整个响应之前该方法将不返回。如果为异步,则该方法将立即返回。调用 send() 方法之后,readyState 属性将设置为 2。当请求完成加载时,readyState 属性将设置为 4。 |
setRequestHeader(string headerName, string headerValue) |
设置请求的 HTTP 标题 |
首先,您将需要下载和安装 JDeveloper 10.1.3。然后,依次单击 File>New>General> Application 创建一个 JDeveloper 应用程序和项目。在该项目中,依次单击 File>New>Web Tier>JSP>JSP 添加一个 JSP 文件 input.jsp 以创建 Ajax Web 应用程序。在 JSP File 框架中,指定文件名并单击 Next。在 Error Page Options 框架中,选择默认设置并单击 Next。在 Tag libraries 框架中,选择默认设置并单击 Next。在 HTML Options 框架中,选择默认设置并单击 Next。JSP 提供了 Ajax 应用程序的客户端。同样,添加 JSP catalog.jsp 和 error.jsp,以根据 Web 应用程序是否生成错误重新定向客户端应用程序。对于服务器端处理,依次单击 File>New>Web Tier> Servlets>HTTP Servlet 添加一个 HTTP Servlet。
图 1:创建 Servlet
在 Mapping Information 框架中,指定 Servlet 名称为 FormValidationServlet,指定一个 servlet 映射 URL(例如,validateForm),然后单击 Next。
图 2:指定 Servlet 映射
在 Servlet Parameters 框架中,指定任何 servlet 参数(如果需要),然后单击 Finish。图 3 中显示了生成的 Ajax 应用程序结构。
图 3:Ajax 应用程序目录结构
然后,依次单击 Tools>Project Properties>Libraries>Add Library 向 Ajax 项目中添加一些库。添加 JSP Runtime、Servlet Runtime 和 Oracle JDBC 库。
图 4:添加项目库
本文中的 Ajax 应用程序将检索并更新 Oracle 数据库 10g 快捷版(Oracle 的免费开发人员版)表中的数据。下载并安装 Oracle 数据库 XE(包括示例模式),然后创建一个数据库实例。使用下面的 SQL 脚本创建一个数据库表 Catalog。
CREATE TABLE OE.Catalog(CatalogId VARCHAR(25) PRIMARY KEY, Journal VARCHAR(25), Publisher Varchar(25), Edition VARCHAR(25), Title Varchar(45), Author Varchar(25)); INSERT INTO OE.Catalog VALUES('catalog1', 'Oracle Magazine', 'Oracle Publishing', 'Nov-Dec 2004', 'Database Resource Manager', 'Kimberly Floss'); INSERT INTO OE.Catalog VALUES('catalog2', 'Oracle Magazine', 'Oracle Publishing', 'Nov-Dec 2004', 'From ADF UIX to JSF', 'Jonas Jacobi'); INSERT INTO OE.Catalog VALUES('catalog3', 'Oracle Magazine', 'Oracle Publishing', 'March-April 2005', 'Starting with Oracle ADF ', 'Steve Muench');
然后,在 Connections-Navigator 中定义一个与 Oracle 数据库之间的 JDBC 连接。要创建 JDBC 连接,右键单击 Connections-Navigator 中的 Database 节点,然后选择 New Database Connection。这将启动 Create Database Connection 向导。在 Type 框架中,指定连接名并选择默认 Connection Type Oracle (JDBC)。单击 Next。在 Authentication 框架中,指定 Username 和 Password,然后单击 Next。在 Connection 框架中,选择瘦驱动程序,将 Host Name 指定为 localhost,将 JDBC Port 指定为 1521。指定 SID 并单击 Next。在 Test 框架中,单击 Test Connection 按钮。随即将建立 JDBC 连接,并将连接节点添加到 Connections-Navigator。
Connections-Navigator 中的 DBConnection1 以数据源形式提供,且资源名称为 jdbc/DBConnection1DS。向 web.xml 配置文件中添加一个 <resource-def/> 元素。
<resource-ref> <res-ref-name>jdbc/DBConnection1DS</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
在本文中,您将创建一个 Ajax 应用程序来验证 HTML 表单中的数据输入。添加到 HTML 表单的数据异步发送到服务器。在服务器端,HTTP servlet 处理表单中的输入并以 XML DOM 对象形式返回响应。在客户端应用程序中,将处理来自服务器的响应并输出一个有关所添加数据的有效性的消息。在本示例应用程序中,输入表单用于创建一个发送到服务器并在服务器端 Servlet 的数据库中更新的目录条目。
您可能要确定目录数据库中是否采用了输入表单中指定的目录 Id。由于客户端指定了目录 Id 字段,因此将根据输入域中的每次更改向服务器发送一个 XMLHttpRequest。服务器发出的 XML DOM 对象形式的响应向客户端提供了有关指定的目录 Id 值的有效性信息。如果不使用 Ajax,则在从服务器收到响应后,必须向服务器和重新加载的客户端 JSP 发送完整的表单。发送 XMLHttpRequest 请求的过程如下所示。
从包含输入域(用于创建目录条目)的 HTML 表单中启动 XMLHttpRequest。XMLHttpRequest 从需要验证的目录 Id 字段启动。使用 onkeyup 事件调用 JavaScript 函数 validateCatalogId()。
<form name="validationForm" action="validateForm" method="post"> <table> <tr><td>Catalog Id:</td><td><input type="text" size="20" id="catalogId" name="catalogId" autocomplete="off" onkeyup="validateCatalogId()"></td> <td><div id="validationMessage"></div></td> </tr> .... .... </table></form>
在 JavaScript 函数 validateCatalogId() 中,创建一个新的 XMLHttpRequest 对象。
<script type="text/javascript"> function validateCatalogId(){ var xmlHttpRequest=init(); function init(){ if (window.XMLHttpRequest) { return new XMLHttpRequest(); } else if (window.ActiveXObject) { return new ActiveXObject("Microsoft.XMLHTTP"); } } </script>
然后,构造将 XMLHttpRequest 发送到的 URL。当 FormValidationServlet 映射到 servlet URL validateForm 时,URL 将为具有 catalogId 参数的 validateForm。catalogId 值将通过 encodeURIComponent(string) JavaScript 函数编码到 url 中。
var catalogId=document.getElementById("catalogId"); xmlHttpRequest.open("GET", "validateForm?catalogId="+ encodeURIComponent(catalogId.value), true);
使用 XMLHttpRequest 对象(使用 onreadystatechange 属性)注册一个回调事件处理程序。在此示例应用程序中,回调方法为 processRequest() JavaScript 函数。
xmlHttpRequest.onreadystatechange=processRequest;
使用 send(data) 方法将 XMLHttpRequest 发送到服务器。由于 HTTP 方法为 GET,因此使用 send 方法发送的数据将设置为空。
xmlHttpRequest.send(null);
由于回调方法为 processRequest,因此当 readyState 属性的值更改时将调用 processRequest 函数。在 processRequest 函数中,检索 readyState 属性值以确定请求是否已完全加载并且 HTTP 状态是否为“OK”。readyState 值 4 表示请求已经完全加载。状态值 200 表示 HTTP 状态为“OK”。
function processRequest(){ if(xmlHttpRequest.readyState==4){ if(xmlHttpRequest.status==200){ processResponse(); } } }
XMLHttpRequest 发送到 url validateForm?catalogId=<catalogId>。
变量 <catalogId> 是参数 catalogId 的值。当 FormValidationServlet 映射到 url validateForm 时,将调用 servlet。由于 XMLHttpRequest 方法为 GET,因此将调用 servlet 的 doGet() 方法。在 doGet() 方法中,检索 catalogId 参数的值。
String catalogId = request.getParameter("catalogId");
要从数据库中获取数据,请从数据源中创建一个 JDBC 连接。使用 InitialContext 对象查找创建一个 DataSource 对象,然后从 DataSource 对象中获取一个 Connection 对象。
InitialContext initialContext = new InitialContext(); javax.sql.DataSource ds = (javax.sql.DataSource) initialContext.lookup("java:comp/env/jdbc/DBConnection1DS"); java.sql.Connection conn = ds.getConnection();
创建一个 Statement 对象,指定一个 SQL 查询以从数据库数据中检索在输入表单中指定的 catalogId 值,并使用 Statement 对象的 executeQuery(String query) 方法获取一个 ResultSet 对象。
Statement stmt = conn.createStatement(); String query = "SELECT * from OE.Catalog WHERE catalogId=" + "'" + catalogId + "'"; ResultSet rs = stmt.executeQuery(query);
将 HttpServletResponse 的内容类型设置为 text/xml,并将 cache-control 标题设置为 no-cache。
response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache");
servlet 中的响应采用 XML DOM 对象格式。构造一个包含有关 catalogId 字段值有效性说明的 XML DOM 对象。如果 ResultSet 对象为空,则表示未在数据库表 Catalog 中定义 catalogId 字段值,因此 catalogId 字段值有效。如果 ResultSet 对象包含数据,则表示已经在数据库中定义了 catalogId 值,因此 catalogId 字段值无效。如果已经为 catalogId 值定义了目录条目,则将输入表单中不同字段的字段值包含在 XML DOM 对象中。XML DOM 对象必需包含一个根元素。在此示例应用程序中,XML DOM 对象包含一个 <valid></valid> 元素,用于指定 Catalog Id 字段值的有效性。
if (rs.next()) { out.println("<catalog>" + "<valid>false</valid>" + "<journal>" + rs.getString(2) + "</journal>" + "<publisher>" + rs.getString(3) + "</publisher>" + "<edition>" + rs.getString(4) + "</edition>" + "<title>" + rs.getString(5) + "</title>" + "<author>" + rs.getString(6) + "</author>" + "</catalog>"); } else { out.println("<valid>true</valid>"); }
如果未在数据库中定义 catalogId 字段值,则可以通过获取数据库的 JDBC 连接并使用 INSERT 语句添加目录条目为 Catalogfield 值添加一个新的目录条目。单击此处查看 FormValidationServlet。
如果 readyState 属性值为 4(对应于已完成的 XMLHttpRequest),并且状态属性值为 200(对应于 HTTP 状态“OK”),则将调用 processResponse() 函数。在 processResponse 函数中,获取 responseXML 属性的值。
var xmlMessage=xmlHttpRequest.responseXML;
作为处理 XML 响应的替换方法,如果只收到“true”,则可以使用 xmlHttpRequest.responseText。
responseXML 对象是 XML DOM 对象。使用 getElementsByTagName(String) 方法获取 <valid/> 元素的值。
var valid=xmlMessage.getElementsByTagName("valid")[0].firstChild.nodeValue;
如果 <valid/> 元素值为 true,则将 Catalog Id 字段行中 validationMessage div 元素的 HTML 设置为“Catalog Id is Valid”。启用输入表单中的提交按钮。
if(valid=="true"){ var validationMessage=document.getElementById("validationMessage"); validationMessage.innerHTML = "Catalog Id is Valid"; document.getElementById("submitForm").disabled = false; }
如果 <valid/> 元素值为 false,则将 Catalog ID 字段行中 validationMessage div 元素的 HTML 设置为“Catalog Id is not Valid”。禁用提交按钮,并设置其他输入字段的值。
if(valid=="false"){ var validationMessage=document.getElementById("validationMessage"); validationMessage.innerHTML = "Catalog Id is not Valid"; document.getElementById("submitForm").disabled = true; }
单击此处查看 JSP input.jsp。
接下来,在 JDeveloper 10.1.3 中运行 Ajax 应用程序。在 Applications Navigator 中右键单击 input.jsp 文件并选择 Run。
图 5:运行 Ajax 应用程序
将显示输入表单。开始向 Catalog Id 字段中添加数据。向服务器中发送 XMLHttpRequest,以验证所添加的数据的有效性。
如果 Catalog Id 字段值有效,将显示“Catalog Id is Valid”消息。
图 6:具有动态验证的输入表单
每次修改 Catalog Id 输入字段时都将发送 XMLHttpRequest。
图 7:修改输入字段值
如果添加了一个已经在数据库中定义的值,则将显示“Catalog Id is not Valid”消息,并禁用 Submit 按钮。
图 8:无效的输入字段值
指定一个有效的 Catalog Id 字段值,然后单击 Create Catalog 按钮添加一个目录条目。
图 9:添加目录条目
由于在 <form/> 元素中指定的方法为 POST,因此使用 POST 方法向服务器发送表单。指定字段值的目录条目添加到数据库。如果随后重新指定了 Catalog Id 值为 Catalog4,则向服务器发送 XMLHttpRequest,并且响应将 <valid/> 元素设置为 false。
图 10:为无效输入字段禁用的表单
恭喜,您已经创建了一个“Ajaxian”Web 应用程序!
您已经在本文了解到,Ajax 技术使用 XMLHttpRequest 对象对添加到输入表单的数据进行动态验证。此外,您还可以使用 JavaScript、DOM 和 Servlet 以外的组合。例如,服务器端应用程序可以为 PHP 脚本而非 servlet。
在 Ajax 编程过程中获得快乐!