任何试过过Flickr、GMail、Google Suggest或者是Google Maps的人都会意识到一种新型的动态Web应用正在逐渐浮出水面。这些应用外观和表现都和传统的桌面应用程序很像,而他们不需要依赖于插件或者是特定于浏览器的功能。过去Web应用只是一系列HTML页面,他们任意一部份内容的更改都必须重新载入页面。像JavaScript编程语言和层叠样式表 (CSS)之类的技术已经成熟,可以有效地应用他们来创建高动态的Web应用,而且可以运行在所有的主流浏览器中。本文将会详细介绍你马上就可以使用的一些技术,让他们使你的Web应用像桌面应用更加丰富和更有交互性。
介绍异步JavaScript技术和XML(AJAX)使用JavaScript技术,一个HTML页面可以异步地对服务器(一般是载入页面的服务器)发送请求并获取XML文档。然后JavaScript可以使用XML文档来更新或改动HTML页面的文档对象模型(DOM)。最近形成了一个术语AJAX(Asynchronous JavaScript Technology and XML)来描述这种交互模型。 AJAX其实不是很新的东西。这些技术对于Windows平台上专注于Internet Explorer的开发人员来说,已经存在好几年了。直到最近,这个技术才被作为Web远程技术或者远程脚本技术被大家了解。Web开发人员也有一段时间曾经使用过插件、Java applet和隐藏框架来模拟这种交互模型。最近发生的变化是,对 基于AJAX的客户端之所以独特的原因是客户端包含了用JavaScript嵌入的特定于页面的控制逻辑。应用JavaScript技术的页面基于事件进行交互,如文档载入、鼠标点击、焦点改变甚至是定时器。AJAX交互使得表现层逻辑更加清晰地与数据分离。一个HTML页面也可以根据需要每次读入适当的数据,而不是每次需要显示一个更改时都重新载入整个页面。AJAX要求一种不同的服务器架构来支持它这种交互模型。以前,服务器端Web应用关注于对每个导致服务器调用的客户端事件都生成HTML文档。然后客户端对每个回应都要重新读入并重新渲染完整的HTML页面。富Web应用(Rich Web Application)关注于,让一个客户端获取一个HTML文档让它表现为一个模板或者是一个容器,可以基于事件并使用从服务器端组件中获取的XML 数据来对文档注入内容。 一些AJAX交互的应用如:
这个列表并未把所有的应用都列出来,但它已经显示了AJAX交互可以让Web应用比从前能做更多的事情。但尽管这些好处是值得关注的,这种方式也有一些缺点:
当开发人员在使用AJAX交互模型上获得更多的经验后,AJAX技术的框架和模式就会慢慢浮现出来。现在就关注于完全通用的AJAX交互框架,还为时过早。本文和相关的解决方案将关注于在现有的Java 2平台企业版(J2EE)上如何对AJAX进行支持,像servlet,JavaServer Page(JSP)软件、JavaServer Face应用和Java标准标签库(JSTL)。 AJAX交互剖析现在我们已经讨论了AJAX是什么以及一些高层次的问题。那现在让我们把所有的零件放在一起来展示一个具有AJAX的J2EE应用。 首先考虑一个例子。一个Web应用包括了一个静态HTML页面,或者是一个由JSP生成的HTML页面,这个JSP中还包括了一个HTML表单,它需要服务器端逻辑来对表单中的数据进行检验,而不用刷新页面。一个名为
以下条目代表了图1中出来AJAX交互的过程:
现在让我们逐个研究这个AJAX模型的每一步。 1.发生一个客户端事件。在一个事件发生时可以调用相应的JavaScript函数。在这里,
每次用户在表单域中按下一个键时,表单元素将都调用 2. 建立和配置一个
|
var req; function validate() { var idField = document.getElementById("idField"); var url = "validate?id=" + escape(idField.value); if (window.XMLHttpRequest) { req = new XMLHttpRequest(); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } req.open("GET", url, true); req.onreadystatechange = callback; req.send(null); } |
validate()
函数建立了一个XMLHttpRequest
对象并对象中的open函数。open函数需要两个参数:HTTP方法,可以是GET
或POST
; 和对象进行交互的服务器端组件的URL;一个布尔变量,表示是否要进行异步调用。API是XMLHttpRequest.open(String method, String URL, boolean asynchronous)
。如果一个交互被设置为异步, (true
) 那就必须指明一个回调函数。可以使用req.onreadystatechange = callback;
来设置这个交互的回调函数。详细内容见第六节。
XMLHttpRequest
对象进行调用当收到了语句req.send(null);
,就会进行一次调用。HTTPGET
的情况下,内容可以是null
或者留空。当调用XMLHttpRequest
的这个函数时,也会对已经配置了的URL进行调用。在下面这个例子中,要发送的数据(id
)将作为一个URL参数。
使用HTTPGET
,两个重复的请求将返回同样的结果。当使用HTTPGET
方法时,要注意URL的长度,包括已经转义的URL参数,可能会受到某些浏览器和服务器端的Web容器的限制。当发送的数据会影响到服务器端的应用程序的状态时,就应该使用HTTPPOST
方法。使用HTTPPOST
必须要对XMLHttpRequest
对象设置一个Content-Type
头,使用以下语句:
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); req.send("id=" + escape(idTextField.value)); |
当从JavaScript中发送表单值得时候,你应该考虑对字段值进行编码。JavaScript中有一个函数escape()
,应该用他来确保区域化的内容被正确编码,同时特殊字符也被正确转义。
ValidateServlet
对请求进行处理.一个映射到URI "validate" 的servlet将检验user ID是不是已经在数据库中存在了。
一个servlet处理一个XMLHttpRequest ,就像对待其它的HTTP请求一样。
下面的例子显示了服务器从请求中抽取出id
参数并检验是否被占用了。
public class ValidateServlet extends HttpServlet { private ServletContext context; private HashMap users = new HashMap(); public void init(ServletConfig config) throws ServletException { this.context = config.getServletContext(); users.put("greg","account data"); users.put("duke","account data"); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String targetId = request.getParameter("id"); if ((targetId != null) && !users.containsKey(targetId.trim())) { response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); response.getWriter().write("<message>valid</message>"); } else { response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); response.getWriter().write("<message>invalid</message>"); } } } |
在这个例子中,一个简单的HashMap用来存放存在的用户名。在这个例子中,我们假设用户的ID是duke
。
ValidateServlet
返回一个包含结果的XML文档用户ID "duke" 在users HashMap
的用户ID列表中出现了。将在应答中写一个包含值为invalid
的message
元素的XML文档。更复杂的用例将要求DOM、XSLT或其他API来生成这个应答。
response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); response.getWriter().write("<message>invalid</message>"); |
开发人员必须注意两个事情。第一,Content-Type
必须设为text/xml
。第二,Cache-Control
必须设为no-cache
。XMLHttpRequest
对象只会处理Content-Type
为text/xml
的应答,同时把将Cache-Control
设为no-cache
将确保浏览器不会从缓存相同的URL(包括参数)返回的应答。
XMLHttpRequest
对象调用callback()
函数并处理结果。XMLHttpRequest
对象已经配置为当有readyState
改变的时候就调用callback()
函数。让我们假设已经ValidateServlet
调用了而且ValidateServlet
是4
,表示XMLHttpRequest
的调用已经完成。HTTP状态代码200
表示一个成功的HTTP交互。
function callback() { if (req.readyState == 4) { if (req.status == 200) { // update the HTML DOM based on whether or not message is valid } } } |
浏览器维护了一个所显示的文档的对象形式(也就是所谓的Docuemt Object Model或DOM)。HTML页面中的JavaScript可以访问DOM,同时在页面载入完之后,可以使用API来修改DOM。
根据成功的请求,JavaScript代码可以修改HTML页面的DOM。从ValidateServlet
获得的对象形式的XML文档可以通过req.responseXML
在JavaScript中获得,req
是一个XMLHttpRequest
对象。DOM API给JavaScript提供了获取这个文档中的内容以及修改HTML页面的DOM的方法。所返回的字符串形式的XML文档可以通过req.responseText
获得。现在我们看看如何在JavaScript中使用DOM API,先看以下从ValidateServlet
返回的XML文档。
<message> valid </message> |
这个例子是一个简单的只包含了一个message
元素的XML片断,里面只有一个简单的字符串valid
或invalid
。一个更高级的例子可以包含多于一个的消息和可以给用户看的有效的名字:
function parseMessage() { var message = req.responseXML.getElementsByTagName("message")[0]; setMessage(message.childNodes[0].nodeValue); } |
parseMessages()
函数将处理一个从ValidateServlet
获取的XML文档。这个函数会调用setMessage()
with the,并给出message
作为参数来更新HTML DOM。
JavaScript技术可以使用很多API从HTML DOM中获得任何元素对象的引用。推荐的获得元素引用的方法是调用document.getElementById("userIdMessage")
, "userIdMessage"
是HTML文档中出现的一个元素的ID属性。有了这个元素的引用,就可以使用JavaScript来修改元素的属性、修改元素的样式、添加、删除或修改子元素。
一个常见的改变元素主体内容的方法是设置元素的innerHTML
属性,如下所示:
<script type="text/javascript"> function setMessage(message) { var userMessageElement = document.getElementById("userIdMessage"); userMessageElement.innerHTML = "<font color=\"red\">" + message + " </font>"; } </script> <body> <div id="userIdMessage"></div> </body> |
受到影响的那部分HTML页面会立刻根据innerHTML
的设置重新渲染。如果innerHTML
属性包含类似<image>
或者是<iframe>
之类的元素,那么由那些元素所指定的内容同样会被获取并渲染。
这种途径的主要缺点是HTML元素是作为字符串硬编码在JavaScript中的。JavaScript中硬编码的HTML标记不是一种好的实践,因为它使代码难于阅读、维护和修改。我们应该考虑在JavaScript中使用DOM API来创建和修改HTML元素。把显示和JavaScript代码的字符串混在一起只会让页面更难于阅读和编辑。
另一种修改HTML DOM的方法是动态地产生新的元素并把他们作为子元素追加到目标元素,如下面的例子所示:
<script type="text/javascript"> function setMessage(message) { var userMessageElement = document.getElementById("userIdMessage"); var userIdMessageFont = document.getElementById("userIdMessageFont"); var messageElement = document.createTextNode(message); if (userMessageElement.childNodes[0]) { // 更新元素 userIdMessageFont.replaceChild(messageElement, userIdMessageFont.childNodes[0]); } else { // 建立一个新的元素 var fontElement = document.createTextNode("font"); fontElement.setAtribute("id", "userIdMessageFont"); fontElement.setAtribute("color", "red"); userMessageElement.appendChild(fontElement); fontElement.appendChild(messageElement); } } </script> <body> <div id="userIdMessage"></div> </body> |
这个范例展示了JavaScript技术的DOM API可以用来更有目的地建立或改变一个元素。当然JavaScript的DOM AP在不同的浏览器上也可能有差别,所以你必须在开发应用程序时小心。
TheJava Blueprints Solutions Catalog是用来收集J2EE技术上AJAX的最佳实践的。每个解决方案包含一个问题和方法的描述、一个设计文档和可运行的源码。这些解决方案是为了让你根据需要在自己的应用程序中复用。以下是已经提供的AJAX交互:
自动补全提供了当用户在一个HTML表单中输入一个请求时对数据浏览的简化方式。当用户面对一大片数据时,可以在输入数据时把可能的完整形式显示给用户。然后选择其中一个完整形式可以保证用户输入的数据已经存在在服务器上。
考虑一个大公司的一个名字查找的Web应用。如图2所示,只要输入姓或名的开头几个字母就可以得到人的列表。用户可以然后就只要点击一下就可以浏览用户的详细信息。
|
在Web应用中,一个服务器端任务也可能要花一段时间去完成。这段时间很可能会超过HTTP交互的时间上限(超时)。当用户不知道这个任务什么时候才能完成时,用户很可能会重新提交一次表单或直接退出会话状态。一般来说,Web应用使用页面刷新来跟踪服务器端操作的状态,这种方式可能会让人厌烦而且也不准确。AJAX可以用来仅在一个HTML页面中跟踪服务器端操作的状态而无需刷新页面。用户可以以图形方式看到服务器端操作的进度,如图3。
|
向一个HTML页面提供最新的数据或服务器消息提醒在现在的Web世界中也是十分重要的,因为现在的Web世界中数据一直不停变化。尽管它不是一个实实在在的推送技术,但它可以通过使用AJAX交互不断进行查询来模拟。当数据需要更新或者要进行提醒,HTML页面将会动态地改变。图4显示了HTML页面中的一个服务器端计数器。这个计数器会在页面后台自动更新。
|
不是所有的表单域都可以单独用JavaScript技术在客户端完成。某些表单数据要求服务器端的验证逻辑。传统和Web应用曾使用页面刷新来完成这种验证,但这可能有些让人烦。
考虑一个需要一个唯一用户ID的Web应用。使用AJAX交互,用户可以在输入的时候就知道ID是否有效(图5)。
|
当一个用户输入了一个无效的用户ID,应用程序禁止了提交按钮并且向用户显示了一个信息(图6)。
|
用户马上就能知道用户ID是可用的也是有效的。
我们已经看到AJAX交互可以解决很多问题。配合HTTP处理、数据库、Web服务、XML处理和业务对象等API,J2EE技术已经提供了一个开发和部属基于AJAX应用的一个良好的基础。有了对于这个交互模型的更好的理解,今天的应用程序可以变得更加有交互性,给最终用户更好的体验。
使用AJAX要求你使用支持XMLHttpRequest
对象的最新浏览器版本。使用AJAX还要求大量对JavaScript技术和CSS的应用。作为一个应用程序架构师或是一个开发人员,你要会针对浏览器支持、架构复杂度和对开发人员的培训等方面来衡量开发一个富应用的需要。当AJAX编程模型不断地发展,现有的技术和框架会让这种转变更加容易。
很明显的是,突出的Web应用都越来越有交互性了。那么你的呢?
XMLHttpRequest
对象的不错的文档。
Greg Murray 是is a Sun Microsystems 的一名工程师,是servlet标准的领导人,BluePrint小组的前成员,在这个小组时他已经开始关注Web层次问题。他也是《Enterprise Applications With the Java 2 Platform,Enterprise Edition和Designing Web Services With the J2EE 1.4 Platform(Addison-Wesley)》一书的协助编撰者。
|