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>

对象参考

方法

  1. abort()

    • 取消当前请求。
  2. getAllResponseHeaders()

    • 返回完整的 HTTP 响应头作为字符串。
  3. getResponseHeader(headerName)

    • 返回指定 HTTP 响应头的值。
  4. open(method, URL)

    • open(method, URL, async)
    • open(method, URL, async, userName)
    • open(method, URL, async, userName, password)
    • 配置请求的 HTTP 方法、URL 及其他可选参数。
      • method: HTTP 方法,如 GETPOSTHEADPUTDELETE,或其他 W3C 规范中列出的方法。
      • URL: 请求的相对或完整 URL。
      • async: 是否异步处理请求。true 表示异步,脚本在 send() 方法后继续执行;false 表示同步,脚本等待响应后再继续。
  5. send(content)

    • 发送请求。
      • content: 可以是字符串或文档引用。
  6. setRequestHeader(label, value)

    • 添加 HTTP 请求头的键值对。

示例说明

主要功能

  1. 动态加载内容

    • 使用 XMLHttpRequest 发送异步请求并接收服务器响应。
    • alertContents 中,根据 readyStatestatus 判断请求完成和状态。
  2. 事件绑定

    • onreadystatechange 事件用于监听请求状态变化。
  3. 异步处理

    • open 方法中设置 async 参数为 true,实现异步处理。

属性

onreadystatechange

  • 指定一个事件处理程序,用于在对象状态发生变化时触发事件。

readyState

  • 返回对象的状态值:
    1. 0 = 未初始化:尚未调用 open() 方法。
    2. 1 = 已打开:尚未调用 send() 方法。
    3. 2 = 已发送send() 方法已被调用,头信息和状态可用。
    4. 3 = 接收中:正在下载,responseText 包含部分数据。
    5. 4 = 完成:操作已完成。

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;
  }
}

原理解析

  1. 检查 Date 响应头

    • 如果 Date 响应头为空,说明缓存响应没有重新验证。
  2. 发送验证请求

    • 如果缓存响应未重新验证,发送带 If-Modified-Since 的新请求。
    • 使用缓存响应的 Last-Modified 值作为参考时间。
  3. 处理响应

    • 如果服务器返回 304 (Not Modified) 状态,则继续使用缓存的响应。

注意事项

  1. 假设条件

    • 此脚本假设服务器始终发送 Date 响应头。
  2. 同步通信

    • 该脚本使用同步通信方式。如果需要异步通信,则需要在回调函数中执行缓存验证。
  3. 避免不必要的请求

    • 对于无副作用的操作(如获取文档数据),可以使用上述验证方法优化缓存。
    • 对于有副作用的操作(如提交评论、标记消息为已读),应使用 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" />

这些方法可以有效防止缓存问题,确保动态内容的及时更新,同时减少不必要的重复请求带来的开销。


参考链接

最后修改: 2025年01月13日 星期一 15:14