使用 XMLHttpRequest
Baseline 广泛可用 *
在本指南中,我们将探讨如何使用 XMLHttpRequest 发出 HTTP 请求,以便在网站和服务器之间交换数据。
其中包含 XMLHttpRequest 的常见和不太常见的用例示例。
发送 HTTP 请求
- 创建
XMLHttpRequest对象 - 打开一个 URL
- 发送请求。
交易完成后,XMLHttpRequest 对象将包含有用的信息,例如响应正文和结果的 HTTP 状态。
function reqListener() {
console.log(this.responseText);
}
const req = new XMLHttpRequest();
req.addEventListener("load", reqListener);
req.open("GET", "http://www.example.org/example.txt");
req.send();
请求类型
通过 XMLHttpRequest 发出的请求可以通过两种方式获取数据:异步或同步。请求的类型由 XMLHttpRequest.open() 方法上的可选 async 参数(第三个参数)决定。如果此参数为 true 或未指定,则 XMLHttpRequest 将异步处理;否则,将同步处理。有关这两种请求类型的详细讨论和演示,请参阅 同步和异步请求 页面。您不能在 Web Worker 之外使用同步请求,因为它会冻结主界面。
注意: 构造函数 XMLHttpRequest 不仅限于 XML 文档。它以 "XML" 开头,因为在创建它时,最初用于异步数据交换的主要格式是 XML。
处理响应
XMLHttpRequest() 构造函数定义了几种 响应属性。这些属性告诉发出 XMLHttpRequest 的客户端有关响应状态的重要信息。以下部分概述了一些处理非文本响应类型时可能涉及的操作和分析的场景。
分析和操作 responseXML 属性
如果您使用 XMLHttpRequest 获取远程 XML 文档的内容,则 responseXML 属性将是一个包含已解析 XML 文档的 DOM 对象。这可能会难以操作和分析。有四种主要方法可以分析此 XML 文档:
- 使用 XPath 来定位(或指向)其中的部分。
- 手动 解析和序列化 XML 为字符串或对象。
- 使用
XMLSerializer将 **DOM 树序列化为字符串或文件**。 - 如果您始终事先知道 XML 文档的内容,则可以使用
RegExp。如果您使用RegExp来扫描换行符,您可能希望删除换行符。但是,这种方法是“最后的手段”,因为如果 XML 代码稍有更改,该方法很可能会失败。
注意: XMLHttpRequest 现在可以使用 responseXML 属性为您解释 HTML。请阅读有关 XMLHttpRequest 中的 HTML 的文章,了解如何执行此操作。
处理包含 HTML 文档的 responseText 属性
如果您使用 XMLHttpRequest 获取远程 HTML 网页的内容,则 responseText 属性是一个包含原始 HTML 的字符串。这可能会难以操作和分析。有三种主要方法可以分析和解析此原始 HTML 字符串:
- 使用
XMLHttpRequest.responseXML属性,如 XMLHttpRequest 中的 HTML 文章中所述。 - 通过
fragment.body.innerHTML将内容注入 文档片段 的 body 中,并遍历片段的 DOM。 - 如果您始终事先知道 HTML
responseText的内容,则可以使用RegExp。如果您使用RegExp来扫描换行符,您可能希望删除换行符。但是,这种方法是“最后的手段”,因为如果 HTML 代码稍有更改,该方法很可能会失败。
处理二进制数据
尽管 XMLHttpRequest 最常用于发送和接收文本数据,但它也可以用于发送和接收二进制内容。有几种经过充分测试的方法可以将 XMLHttpRequest 的响应强制转换为发送二进制数据。这些方法包括利用 XMLHttpRequest 对象上的 overrideMimeType() 方法,这是一种可行的解决方案。
const req = new XMLHttpRequest();
req.open("GET", url);
// retrieve data unprocessed as a binary string
req.overrideMimeType("text/plain; charset=x-user-defined");
/* … */
但是,现在有更现代的技术,因为 responseType 属性现在支持多种额外的内容类型,这使得发送和接收二进制数据更加容易。
例如,考虑这段代码片段,它使用 responseType 为 "arraybuffer" 来将远程内容获取到 ArrayBuffer 对象中,该对象存储原始二进制数据。
const req = new XMLHttpRequest();
req.onload = (e) => {
const arraybuffer = req.response; // not responseText
/* … */
};
req.open("GET", url);
req.responseType = "arraybuffer";
req.send();
有关更多示例,请参阅 发送和接收二进制数据 页面。
监控进度
XMLHttpRequest 提供了在请求处理过程中侦听各种事件的能力。这包括定期的进度通知、错误通知等。
对 XMLHttpRequest 传输的 DOM progress 事件监控的支持遵循 进度事件规范:这些事件实现了 ProgressEvent 接口。您可以监控以确定正在进行的传输状态的实际事件是:
const req = new XMLHttpRequest();
req.addEventListener("progress", updateProgress);
req.addEventListener("load", transferComplete);
req.addEventListener("error", transferFailed);
req.addEventListener("abort", transferCanceled);
req.open();
// …
// progress on transfers from the server to the client (downloads)
function updateProgress(event) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
// …
} else {
// Unable to compute progress information since the total size is unknown
}
}
function transferComplete(evt) {
console.log("The transfer is complete.");
}
function transferFailed(evt) {
console.log("An error occurred while transferring the file.");
}
function transferCanceled(evt) {
console.log("The transfer has been canceled by the user.");
}
我们添加事件监听器来处理使用 XMLHttpRequest 进行数据传输时发送的各种事件。
注意: 您需要在对请求调用 open() 之前添加事件监听器。否则,progress 事件将不会触发。
进度事件处理程序(在此示例中由 updateProgress() 函数指定)接收要传输的总字节数以及事件的 total 和 loaded 字段中已传输的字节数。但是,如果 lengthComputable 字段为 false,则总长度未知且将为零。
下载和上传传输都存在进度事件。下载事件在 XMLHttpRequest 对象本身上触发,如上面的示例所示。上传事件在 XMLHttpRequest.upload 对象上触发,如下所示:
const req = new XMLHttpRequest();
req.upload.addEventListener("progress", updateProgress);
req.upload.addEventListener("load", transferComplete);
req.upload.addEventListener("error", transferFailed);
req.upload.addEventListener("abort", transferCanceled);
req.open();
注意: file: 协议不支持进度事件。
对于收到的每个数据块,都会触发进度事件,包括在最后一个数据包已接收且连接在进度事件触发之前关闭的情况下的最后一个数据块。在这种情况下,当该数据包的 load 事件发生时,进度事件会自动触发。这样,您就可以只通过监视“progress”事件来可靠地监控进度。
还可以使用 loadend 事件检测所有三个加载结束条件(abort、load 或 error)。
req.addEventListener("loadend", loadEnd);
function loadEnd(e) {
console.log(
"The transfer finished (although we don't know if it succeeded or not).",
);
}
请注意,根据 loadend 事件接收到的信息,无法确定是哪种条件导致操作终止;但是,您可以使用此事件来处理需要在所有传输结束场景中执行的任务。
获取最后修改日期
function getHeaderTime() {
console.log(this.getResponseHeader("Last-Modified")); // A valid GMTString date or null
}
const req = new XMLHttpRequest();
req.open(
"HEAD", // use HEAD when you only need the headers
"your-page.html",
);
req.onload = getHeaderTime;
req.send();
当最后修改日期更改时执行某些操作
让我们创建两个函数。
function getHeaderTime() {
const lastVisit = parseFloat(
window.localStorage.getItem(`lm_${this.filepath}`),
);
const lastModified = Date.parse(this.getResponseHeader("Last-Modified"));
if (isNaN(lastVisit) || lastModified > lastVisit) {
window.localStorage.setItem(`lm_${this.filepath}`, Date.now());
isFinite(lastVisit) && this.callback(lastModified, lastVisit);
}
}
function ifHasChanged(URL, callback) {
const req = new XMLHttpRequest();
req.open("HEAD" /* use HEAD - we only need the headers! */, URL);
req.callback = callback;
req.filepath = URL;
req.onload = getHeaderTime;
req.send();
}
并进行测试。
// Let's test the file "your-page.html"
ifHasChanged("your-page.html", function (modified, visit) {
console.log(
`The page '${this.filepath}' has been changed on ${new Date(
modified,
).toLocaleString()}!`,
);
});
如果您想知道当前页面是否已更改,请参阅有关 document.lastModified 的文章。
跨站 XMLHttpRequest
现代浏览器通过实现 跨域资源共享 (CORS) 标准来支持跨站请求。只要服务器配置为允许来自您的 Web 应用程序源的请求,XMLHttpRequest 就可以正常工作。否则,将抛出 INVALID_ACCESS_ERR 异常。
绕过缓存
一种跨浏览器兼容的绕过缓存的方法是在 URL 后面附加一个时间戳,确保根据需要包含 "?" 或 "&"。例如:
http://example.com/bar.html -> http://example.com/bar.html?12345 http://example.com/bar.html?foobar=baz -> http://example.com/bar.html?foobar=baz&12345
由于本地缓存是按 URL 索引的,这会导致每个请求都唯一,从而绕过缓存。
您可以使用以下代码自动调整 URL:
const req = new XMLHttpRequest();
req.open("GET", url + (/\?/.test(url) ? "&" : "?") + new Date().getTime());
req.send(null);
安全
启用跨站脚本的推荐方法是在 XMLHttpRequest 的响应中使用 Access-Control-Allow-Origin HTTP 标头。
XMLHttpRequest 的停止
如果您最终使用一个 XMLHttpRequest 接收到 status=0 和 statusText=null,这意味着该请求不允许执行。它处于 UNSENT 状态。这可能的原因是 XMLHttpRequest 的源(在创建 XMLHttpRequest 时)在后续 open() 时发生了变化。这种情况可能发生在,例如,当一个 XMLHttpRequest 在窗口的 onunload 事件上触发时,预期的 XMLHttpRequest 在要关闭的窗口仍然存在时创建,最后在窗口失去焦点并且另一个窗口获得焦点时发送请求(即,open())。避免此问题的最有效方法是在新窗口的 DOMActivate 事件上设置一个监听器,当已终止窗口的 unload 事件触发后,该监听器将被设置。
规范
| 规范 |
|---|
| XMLHttpRequest # interface-xmlhttprequest |
浏览器兼容性
加载中…