使用 XMLHttpRequest
在本指南中,我们将了解如何使用 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 工作器之外使用同步请求。
注意:构造函数 XMLHttpRequest
不仅限于 XML 文档。它以 “XML” 开头,因为在创建它时,最初用于异步数据交换的主要格式是 XML。
处理响应
为 XMLHttpRequest()
构造函数定义了几种类型的 响应属性。它们告诉发出 XMLHttpRequest
的客户端有关响应状态的重要信息。在以下部分概述了一些处理非文本响应类型可能涉及一些操作和分析的情况。
分析和操作 responseXML 属性
如果使用 XMLHttpRequest
获取远程 XML 文档的内容,则 responseXML
属性将是一个包含已解析 XML 文档的 DOM 对象。这可能难以操作和分析。有四种主要方法可以分析此 XML 文档
- 使用 XPath 来寻址(或指向)其中的部分。
- 手动 解析和序列化 XML 为字符串或对象。
- 使用
XMLSerializer
将 DOM 树序列化为字符串或文件。 RegExp
可用于在事先始终知道 XML 文档内容的情况下。如果使用RegExp
扫描换行符,则可能需要删除换行符。但是,此方法是“最后的手段”,因为如果 XML 代码稍有更改,则该方法可能会失败。
注意:XMLHttpRequest
现在可以使用 responseXML
属性为您解释 HTML。阅读有关 XMLHttpRequest 中的 HTML 的文章以了解如何执行此操作。
处理包含 HTML 文档的 responseText 属性
如果使用 XMLHttpRequest
获取远程 HTML 网页的内容,则 responseText
属性是一个包含原始 HTML 的字符串。这可能难以操作和分析。有三种主要方法可以分析和解析此原始 HTML 字符串
- 使用
XMLHttpRequest.responseXML
属性,如 XMLHttpRequest 中的 HTML 文章中所述。 - 通过
fragment.body.innerHTML
将内容注入到 文档片段 的主体中,并遍历片段的 DOM。 RegExp
可用于在事先始终知道 HTMLresponseText
内容的情况下。如果使用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
属性现在支持许多其他内容类型,因此可以使用更现代的技术,这使得发送和接收二进制数据变得更加容易。
例如,请考虑以下代码片段,它使用 "arraybuffer
" 的 responseType
将远程内容获取到 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:
协议不可用进度事件。
每个接收到的数据块都会出现进度事件,包括在最后数据包接收并关闭连接之前接收最后一个数据包的情况。在这种情况下,当该数据包的加载事件发生时,会自动触发进度事件。这使您可以通过仅观察“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
"yourpage.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 "yourpage.html"
ifHasChanged("yourpage.html", function (modified, visit) {
console.log(
`The page '${this.filepath}' has been changed on ${new Date(
nModified,
).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 标头。
XMLHttpRequests 被停止
如果您在使用 XMLHttpRequest 接收时得到 status=0
和 statusText=null
,这意味着请求未被允许执行。它处于 UNSENT
状态。一个可能的原因是,当随后调用 open()
时,XMLHttpRequest
的来源(在创建 XMLHttpRequest 时)发生了变化。例如,这种情况可能发生在:当一个 XMLHttpRequest 在窗口的 onunload 事件中触发时,预期中的 XMLHttpRequest 在要关闭的窗口仍然存在时被创建,最后在该窗口失去焦点且另一个窗口获得焦点时发送请求(换句话说,调用 open()
)。避免此问题的最有效方法是在新窗口的 DOMActivate
事件上设置一个监听器,该事件在终止窗口触发其 unload
事件后设置。
规范
规范 |
---|
XMLHttpRequest 标准 # 接口-xmlhttprequest |
浏览器兼容性
BCD 表格仅在浏览器中加载