JavaScript编程
XMLHttpRequest 对象指南与参考
XMLHttpRequest 对象是 Ajax 编程的关键组件。本指南展示了其使用方法和常见问题的解决方案。
示例
以下是一个使用 XMLHttpRequest
的网页示例:
<!DOCTYPE html>
<html>
<!-- example.html : public domain -->
<script language="JavaScript" type="text/javascript">
function alertContents(httpRequest) {
if (httpRequest.readyState == 4) {
// 请求完成,响应已收到
if (httpRequest.status == 200 || httpRequest.status == 0) {
// 创建一个新的 DIV 节点
var htmlDoc = document.createElement('div');
// 将返回的 HTML 页面内容放入该节点
htmlDoc.innerHTML = httpRequest.responseText;
alert("The response was: " + httpRequest.status + "\n" + httpRequest.responseText);
} else {
alert('There was a problem with the request.\nStatus: ' + httpRequest.status + "\nResponse: " + httpRequest.responseText);
}
}
}
function send_with_ajax(the_url) {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() { alertContents(httpRequest); };
httpRequest.open("GET", the_url, true);
httpRequest.send(null);
}
</script>
<p onClick="send_with_ajax('example.html');">
Click me!
</p>
</html>
对象参考
方法
-
abort()
- 取消当前请求。
-
getAllResponseHeaders()
- 返回完整的 HTTP 响应头作为字符串。
-
getResponseHeader(headerName)
- 返回指定 HTTP 响应头的值。
-
open(method, URL)
open(method, URL, async)
open(method, URL, async, userName)
open(method, URL, async, userName, password)
- 配置请求的 HTTP 方法、URL 及其他可选参数。
method
: HTTP 方法,如GET
、POST
、HEAD
、PUT
、DELETE
,或其他 W3C 规范中列出的方法。URL
: 请求的相对或完整 URL。async
: 是否异步处理请求。true
表示异步,脚本在send()
方法后继续执行;false
表示同步,脚本等待响应后再继续。
-
send(content)
- 发送请求。
content
: 可以是字符串或文档引用。
- 发送请求。
-
setRequestHeader(label, value)
- 添加 HTTP 请求头的键值对。
示例说明
主要功能
-
动态加载内容
- 使用
XMLHttpRequest
发送异步请求并接收服务器响应。 - 在
alertContents
中,根据readyState
和status
判断请求完成和状态。
- 使用
-
事件绑定
onreadystatechange
事件用于监听请求状态变化。
-
异步处理
- 在
open
方法中设置async
参数为true
,实现异步处理。
- 在
属性
onreadystatechange
- 指定一个事件处理程序,用于在对象状态发生变化时触发事件。
readyState
- 返回对象的状态值:
- 0 = 未初始化:尚未调用
open()
方法。 - 1 = 已打开:尚未调用
send()
方法。 - 2 = 已发送:
send()
方法已被调用,头信息和状态可用。 - 3 = 接收中:正在下载,
responseText
包含部分数据。 - 4 = 完成:操作已完成。
- 0 = 未初始化:尚未调用
responseText
- 以字符串形式返回响应内容。
responseXML
- 以 XML 文档对象形式返回响应内容,可以使用 W3C DOM 方法和属性解析。
responseBody
- 返回以二进制编码的字符串形式的响应内容。此属性不是原生
XMLHttpRequest
的一部分,在使用 ActiveX 对象时可用。示例:if (typeof ActiveXObject !== "undefined") { var xmlhttp = new ActiveXObject("MSXML2.XMLHTTP"); xmlhttp.open("GET", "#", false); xmlhttp.send(null); alert(xmlhttp.responseBody); } else { alert("此浏览器不支持 ActiveX 对象。"); }
status
- 返回 HTTP 状态码,例如:
200
表示 "OK";404
表示 "Not Found";- 某些网络相关状态码(如
408
表示 "Request Timeout")可能在某些浏览器中引发错误。
statusText
- 返回状态的描述字符串,例如 "OK" 或 "Not Found"。
错误和不一致问题
处理 XMLHttpRequest 实现中的错误和不一致
缓存问题
大多数 XMLHttpRequest
实现都支持 HTTP 缓存机制,但不同浏览器在缓存验证的时机和方式上有所不同:
-
Firefox:
- 每次刷新页面时会重新验证缓存响应。
- 发送一个
If-Modified-Since
请求头,其值设置为缓存响应的Last-Modified
值。
-
Internet Explorer:
- 只有当缓存的响应过期(即收到的
Expires
头日期已过)时才重新验证。 - 存在一个 bug,即 IE 中缓存的响应可能永远不会被刷新。
- 只有当缓存的响应过期(即收到的
这种不一致可能导致问题。以下脚本展示了一个方法,统一客户端缓存行为:
var request = new XMLHttpRequest();
request.open("GET", url, false);
request.send(null);
// 检查缓存的 Date 头是否为空
if (!request.getResponseHeader("Date")) {
var cached = request;
request = new XMLHttpRequest();
// 设置 If-Modified-Since 头,优先使用 Last-Modified 值,否则使用一个很早的日期
var ifModifiedSince = cached.getResponseHeader("Last-Modified") || new Date(0); // 1970年1月1日
request.open("GET", url, false);
request.setRequestHeader("If-Modified-Since", ifModifiedSince);
request.send("");
// 如果服务器返回 304 (Not Modified),则返回缓存的响应
if (request.status == 304) {
request = cached;
}
}
原理解析
-
检查 Date 响应头:
- 如果
Date
响应头为空,说明缓存响应没有重新验证。
- 如果
-
发送验证请求:
- 如果缓存响应未重新验证,发送带
If-Modified-Since
的新请求。 - 使用缓存响应的
Last-Modified
值作为参考时间。
- 如果缓存响应未重新验证,发送带
-
处理响应:
- 如果服务器返回
304 (Not Modified)
状态,则继续使用缓存的响应。
- 如果服务器返回
注意事项
-
假设条件:
- 此脚本假设服务器始终发送
Date
响应头。
- 此脚本假设服务器始终发送
-
同步通信:
- 该脚本使用同步通信方式。如果需要异步通信,则需要在回调函数中执行缓存验证。
-
避免不必要的请求:
- 对于无副作用的操作(如获取文档数据),可以使用上述验证方法优化缓存。
- 对于有副作用的操作(如提交评论、标记消息为已读),应使用 POST 方法以确保请求到达服务器。
在 Internet Explorer 中,如果缓存的响应未重新验证,则响应的 Date
头为空字符串。解决方法是检查 Date
响应头,并在需要时发出另一个请求。如果需要第二次请求,实际的 HTTP 请求不会重复进行,因为第一次调用不会产生实际的 HTTP 请求。
对缓存请求的引用会被保留,因为如果第二次调用的响应状态码为 "304 Not Modified",响应体将变为空字符串 ""
,此时需要返回到缓存对象。为节省内存和避免重复创建对象,可以仅保留所需的响应数据并重复使用 XMLHttpRequest
对象。
上述脚本假设服务器始终发送 Date
响应头,这在大多数服务器配置中应该是正确的。此外,它展示了服务器与客户端之间的同步通信方式。如果是异步通信,则需要在回调中进行缓存验证。
解决方案建议
防止缓存的技术
此问题通常通过防止缓存的技术来解决,但不加选择地使用这些技术可能导致性能下降并浪费网络带宽。
副作用操作
如果脚本执行的操作具有副作用(例如添加评论、标记消息为已读),并且请求需要始终到达服务器,则应使用 POST 方法代替 GET 方法。
解决方法
1. 使用时间戳或随机数
Internet Explorer 对动态页面进行缓存的问题在于 URL 可能保持不变,而内容却会发生变化(例如新闻订阅)。一种解决方法是向 URL 添加唯一时间戳或随机数,或者两者结合使用。通常使用 Date
对象和/或 Math.random()
。
var url = "example.com/data?ts=" + new Date().getTime();
2. 修改查询字符串
对于简单的文档请求,可以使用查询字符串分隔符 '?'
。对于已有查询的 URL,可以在最后添加一个子查询:
var url = "example.com/data?param=value&ts=" + Math.random();
注意: 这种方法的缺点是,每次请求都会填满缓存,存储无用(从不复用)的内容,从而可能导致更有用的数据被清除以腾出缓存空间。
3. 添加 Meta 标签防止缓存
一种更好的解决方法是为动态页面添加 Meta 标签,使其不可缓存:
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
这些方法可以有效防止缓存问题,确保动态内容的及时更新,同时减少不必要的重复请求带来的开销。