XMLHttpRequest 简介
要实现Ajax 各种效果,必须非常熟悉一个 JavaScript 对象,即 XMLHttpRequest 。这个小小的对象实际上已经在几种浏览器中存在一段时间了,但真正为大家所熟悉是在Ajax 开始流行以后。下面给出用于该对象的很少的几个方法和属性。
- open() : 建立到服务器的新请求
- send() : 向服务器发送请求
- abort() : 退出当前请求
- readyState : 提供当前 HTML 的就绪状态
- responseText : 服务器返回的请求响应文本
要注意这些方法和属性都与发送请求及处理响应有关。事实上,如果看到 XMLHttpRequest 的所有方法和属性,就会发现它们都与非常简单的请求/ 响应模型有关,用好该对象可以彻底改变您的应用程序。
创建新的 XMLHttpRequest 对象
<script language="javascript" type="text/javascript"> //创建一个新变量 request 并赋值 false, //使用 false 作为判断条件,它表示还没有创建 XMLHttpRequest 对象 var request = false; //增加 try/catch 块 try { //尝试创建 XMLHttpRequest 对象 request = new XMLHttpRequest();//非 Microsoft 浏览器 } catch (trymicrosoft) { //Microsoft浏览器 try { //如果失败 //尝试使用较新版本的 Microsoft 浏览器创建 Microsoft 兼容的对象 request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (othermicrosoft) { try { //如果失败 //尝试使用较老版本的 Microsoft 浏览器创建 Microsoft 兼容的对象 request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (failed) { //如果失败 //则保证 request 的值仍然为 false request = false; } } } if (!request) //检查 request 是否仍然为 false //则使用 JavaScript 警告通知用户出现了问题 alert("Error initializing XMLHttpRequest!"); </script>
静态与动态
嵌套在 script 标记中的 JavaScript 代码称为静态 JavaScript 。就是说代码是在页面显示给用户之前的某个时候运行。这也是多数 Ajax 程序员创建 XMLHttpRequest 对象的一般方式。如将将代码放在一个方法中,则称之为 动态 JavaScript . 此方法惟一的问题是推迟了错误通知,这也是多数 Ajax 程序员不采用这一方法的原因。如下就是一个动态方法:
<script language="javascript" type="text/javascript"> var request; function createRequest() { try { request = new XMLHttpRequest(); } catch (trymicrosoft) { try { request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (othermicrosoft) { try { request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (failed) { request = false; } } } if (!request) alert("Error initializing XMLHttpRequest!"); } function getCustomerInfo() { createRequest(); // Do something with the request variable } </script>
用 XMLHttpRequest 发送请求
得到请求对象之后就可以进入请求/ 响应循环了。记住,XMLHttpRequest 惟一的目的是让您发送请求和接收响应。其他一切都是 JavaScript 、CSS 或页面中其他代码的工作:改变用户界面、切换图像、解释服务器返回的数据。准备好 XMLHttpRequest 之后,就可以向服务器发送请求了。
首先要确定连接的服务器的 URL 。这并不是 Ajax 的特殊要求,但仍然是建立连接所必需的,显然现在您应该知道如何构造 URL 了。多数应用程序中都会结合一些静态数据和用户处理的表单中的数据来构造该 URL 。有了要连接的 URL 后就可以配置请求了。可以用 XMLHttpRequest 对象的 open() 方法来完成。该方法有五个参数:
- request-type : 发送请求的类型。典型的值是 GET 或 POST ,但也可以发送 HEAD 请求
- url : 要连接的 URL
- asynch : 如果希望使用异步连接则为 true ,否则为 false 。该参数是可选的,默认为 true
- username :如果需要身份验证,则可以在此指定用户名。该可选参数没有默认值
- password :如果需要身份验证,则可以在此指定口令。该可选参数没有默认值
通常使用其中的前三个参数。事实上,即使需要异步连接,也应该指定第三个参数为 “true ”。这是默认值,但坚持明确指定请求是异步的还是同步的更容易理解。
一旦用 open() 配置好之后,就可以发送请求了。幸运的是,发送请求的方法的名称要比 open() 适当,它就是 send(), 它只有一个参数,就是要发送的内容。也能通过 URL 本身发送数据。事实上,GET 请求(在典型的 Ajax 应用中大约占 80% )中,用 URL 发送数据要容易得多。如果需要发送安全信息或 XML ,可能要考虑使用 send() 发送内容。如果不需要通过 send() 传递数据,则只要传递 null 作为该方法的参数即可。
function getCustomerInfo() { var phone = document.getElementById("phone").value; var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone); request.open("GET", url, true); request.send(null); }
指定回调方法
onreadystatechange 属性允许指定一个回调函数。回调允许服务器反向调用 Web 页面中的代码。它也给了服务器一定程度的控制权,当服务器完成请求之后,会查看 XMLHttpRequest 对象,特别是 onreadystatechange 属性。然后调用该属性指定的任何方法。之所以称为回调是因为服务器向网页发起调用,无论网页本身在做什么。比方说,可能在用户坐在椅子上手没有碰键盘的时候调用该方法,但是也可能在用户输入、移动鼠标、滚动屏幕或者点击按钮时调用该方法。它并不关心用户在做什么。
这就是称之为异步的原因:用户在一层上操作表单,而在另一层上服务器响应请求并触发 onreadystatechange 属性指定的回调方法。
function getCustomerInfo() { var phone = document.getElementById("phone").value; var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone); request.open("GET", url, true); request.onreadystatechange = updatePage; request.send(null); }
需要特别注意的是该属性在代码中设置的位置 —— 它是在调用 send() 之前 设置的。发送请求之前必须设置该属性,这样服务器在回答完成请求之后才能查看该属性。
处理服务器响应
现在已经看到如何告诉服务器完成后应该做什么:将 XMLHttpRequest 对象的 onreadystatechange 属性设置为要运行的函数名。这样,当服务器处理完请求后就会自动调用该函数。也不需要担心该函数的任何参数。事实上,每当 HTTP 就绪状态改变时它都会调用该方法。 HTTP 就绪状态表示请求的状态或情形。它用于确定该请求是否已经开始、是否得到了响应或者请求 / 响应模型是否已经完成。它还可以帮助确定读取服务器提供的响应文本或数据是否安全。在 Ajax 应用程序中需要了解五种就绪状态:
- 0 :请求没有发出(在调用 open() 之前)
- 1 :请求已经建立但还没有发出(调用 send() 之前)
- 2 :请求已经发出正在处理之中(这里通常可以从响应得到内容头部)
- 3 :请求已经处理,响应中通常有部分数据可用,但是服务器还没有完成响应
- 4 :响应已完成,可以访问服务器响应并使用它
与大多数跨浏览器问题一样,这些就绪状态的使用也不尽一致。您也许期望任务就绪状态从 0 到 1 、 2 、 3 再到 4 ,但实际上很少是这种情况。一些浏览器从不报告 0 或 1 而直接从 2 开始,然后是 3 和 4 。其他浏览器则报告所有的状态。还有一些则多次报告就绪状态 1 。对于 Ajax 编程,需要直接处理的惟一状态就是就绪状态 4 ,它表示服务器响应已经完成,可以安全地使用响应数据了。基于此,回调方法中的第一行应该如下所示。
function updatePage() { if (request.readyState == 4) alert("Server is done!"); }
修改后就可以保证服务器的处理已经完成。尝试运行新版本的 Ajax 代码,现在就会看到与预期的一样,只显示一次警告信息了。
HTTP 状态码
如果服务器响应请求并完成了处理但是报告了一个错误怎么办?要知道,服务器端代码应该明白它是由 Ajax 、 JSP 、普通 HTML 表单或其他类型的代码调用的,但只能使用传统的 Web 专用方法报告信息。而在 Web 世界中, HTTP 代码可以处理请求中可能发生的各种问题。
比方说,您肯定遇到过输入了错误的 URL 请求而得到 404 错误码的情形,它表示该页面不存在。这仅仅是 HTTP 请求能够收到的众多错误码中的一种(完整的状态码列表请参阅 参考资料 中的链接)。表示所访问数据受到保护或者禁止访问的 403 和 401 也很常见。无论哪种情况,这些错误码都是从完成的响应 得到的。换句话说,服务器履行了请求(即 HTTP 就绪状态是 4 )但是没有返回客户机预期的数据。
因此除了就绪状态外,还需要检查 HTTP 状态。我们期望的状态码是 200 ,它表示一切顺利。如果就绪状态是 4 而且状态码是 200 ,就可以处理服务器的数据了,而且这些数据应该就是要求的数据(而不是错误或者其他有问题的信息)。因此还要在回调方法中增加状态检查,如下所示 :
function updatePage() {
if (request.readyState == 4)
if (request.status == 200)
alert("Server is done!");
}
为了增加更健壮的错误处理并尽量避免过于复杂,可以增加一两个状态码检查 :
function updatePage() { if (request.readyState == 4) if (request.status == 200) alert("Server is done!"); else if (request.status == 404) alert("Request URL does not exist"); else alert("Error: status code is " + request.status); }
读取响应文本
现在可以确保请求已经处理完成(通过就绪状态),服务器给出了正常的响应(通过状态码),最后可以处理服务器返回的数据了。返回的数据保存在 XMLHttpRequest 对象的 responseText 属性中。
关于 responseText 中的文本内容,比如格式和长度,有意保持含糊。这样服务器就可以将文本设置成任何内容。比方说,一种脚本可能返回逗号分隔的值,另一种则使用管道符(即 | 字符)分隔的值,还有一种则返回长文本字符串。何去何从由服务器决定。
在本文使用的例子中,服务器返回客户的上一个订单和客户地址,中间用管道符分开。然后使用订单和地址设置表单中的元素值,下在面给出了更新显示内容的代码。
function updatePage() { if (request.readyState == 4) { if (request.status == 200) { var response = request.responseText.split("|"); document.getElementById("order").value = response[0]; document.getElementById("address").innerHTML = response[1].replace(/\n/g, ""); } else alert("status is " + request.status); } }
首先,得到 responseText 并使用 JavaScript split() 方法从管道符分开。得到的数组放到 response 中。数组中的第一个值 —— 上一个订单 —— 用 response[0] 访问,被设置为 ID 为 “ order ” 的字段的值。第二个值 response[1] ,即客户地址,则需要更多一点处理。因为地址中的行用一般的行分隔符(“ \n ”字符)分隔,代码中需要用 XHTML 风格的行分隔符 <br /> 来代替。替换过程使用 replace() 函数和正则表达式完成。最后,修改后的文本作为 HTML 表单 div 中的内部 HTML 。结果就是表单突然用客户信息更新了。