Fetch API
Fetch API 能够执行 XMLHttpRequest 对象的所有任务,但更容易使用,接口也更现代化,能够在Web 工作线程等现代 Web 工具中使用。XMLHttpRequest 可以选择异步,而 Fetch API 则必 须是异步。Fetch API 是 WHATWG 的一个“活标准”(living standard),用规范原文说,就是“Fetch 标准定义请求、响应,以及绑定二者的流程:获取(fetch)”。
Fetch API 本身是使用 JavaScript 请求资源的优秀工具,同时这个 API 也能够应用在服务线程(service worker)中,提供拦截、重定向和修改通过 fetch()
生成的请求接口。
基本用法
fetch()
方法是暴露在全局作用域中的,包括主页面执行线程、模块和工作线程。调用这个方法,
浏览器就会向给定 URL 发送请求。
分派请求
fetch()
只有一个必需的参数 input。多数情况下,这个参数是要获取资源的 URL。URL 的格式(相对路径、绝对路径等)的解释与 XHR 对象一样。
请求完成、资源可用时,期约会解决为一个 Response 对象。这个对象是 API 的封装,可以通过它取得相应资源。获取资源要使用这个对象的属性和方法,掌握响应的情况并将负载转换为有用的形式,如下所示:
fetch('http://127.0.0.1:8080/data').then(response => {
console.log(response); // Response { type: "basic", url: ... }
});
读取响应
读取响应内容的最简单方式是取得纯文本格式的内容,这要用到 text()方法。这个方法返回一个期约,会解决为取得资源的完整内容:
fetch('http://127.0.0.1:8080/data')
.then(response => response.text())
.then(value => {
// 获取服务器端响应的文本数据
console.log(value);
});
使用 json()
方法,可以使返回的数据是 JSON 格式。
fetch('http://127.0.0.1:8080/data')
.then(response => response.json())
.then(value => {
// 获取服务器端响应的JSON格式数据
console.log(value);
});
处理状态码和请求失败
Fetch API 支持通过 Response 的 status
(状态码)和 statusText
(状态文本)属性检查响应状态。成功获取响应的请求通常会产生值为 200 的状态码,如下所示:
fetch('http://127.0.0.1:8080/data').then(response => {
console.log(response.status); // 200
console.log(response.statusText); // OK
});
// 请求不存在的资源通常会产生值为404的状态码:
fetch('http://127.0.0.1:8080/not-found').then(response => {
console.log(response.status); // 404
console.log(response.statusText); // Not Found
});
// 请求的 URL 如果抛出服务器错误会产生值为 500 的状态码:
fetch('http://127.0.0.1:8080/throw-server-error').then(response => {
console.log(response.status); // 500
console.log(response.statusText); // Internal Server Error
});
在前面这几个例子中,虽然请求可能失败(如状态码为 404、500),但都只执行了期约的解决处理函数。事实上,只要服务器返回了响应,fetch()
期约都会解决。这个行为是合理的:系统级网络协议已经成功完成消息的一次往返传输。
至于真正的“成功”请求,则需要在处理响应时再定义。通常状态码为 200 时就会被认为成功了,其他情况可以被认为未成功。
因为服务器没有响应而导致浏览器超时,这样真正的 fetch()
失败会导致期约被拒绝:
自定义选项
只使用 URL 时,fetch()
会发送 GET 请求,只包含最低限度的请求头。要进一步配置如何发送请求,需要传入可选的第二个参数 init 对象。init 对象有如下属性:
body
指定使用请求体时请求体的内容必须是 Blob
、BufferSource
、FormData
、URLSearchParams
、ReadableStream
或 String
的实例
cache
用于控制浏览器与 HTTP缓存的交互。要跟踪缓存的重定向,请求的 redirect 属性值必须是"follow",而且必须符合同源策略限制。必须是下列值之一
-
default
fetch()
返回命中的有效缓存。不发送请求- 命中无效(stale)缓存会发送条件式请求。如果响应已经改变,则更新缓存的值。然后
fetch()
返回缓存的值 - 未命中缓存会发送请求,并缓存响应。然后
fetch()
返回响应
-
no-store
- 浏览器不检查缓存,直接发送请求
- 不缓存响应,直接通过 fetch()返回
-
reload
- 浏览器不检查缓存,直接发送请求
- 缓存响应,再通过 fetch()返回
-
no-cache
- 无论命中有效缓存还是无效缓存都会发送条件式请求。如果响应已经改变,则更新缓存的值。然后 fetch()返回缓存的值
-
force-cache
- 无论命中有效缓存还是无效缓存都通过 fetch()返回。不发送请求
-
only-if-cached
- 只在请求模式为 same-origin 时使用缓存
credentials
用于指定在外发请求中如何包含 cookie。与 XMLHttpRequest 的 withCredentials 标签类似 必须是下列字符串值之一
-
omit
:不发送 -
same-origin
(默认值):只在请求 URL 与发送 fetch()请求的页面同源时发送 cookie -
include
:无论同源还是跨源都包含 cookie
headers
用于指定请求头部,必须是 Headers 对象实例或包含字符串格式键/值对的常规对象
默认值为不包含键/值对的 Headers 对象。这不意味着请求不包含任何头部,浏览器仍然会随请求发送一些头部。虽然这些头部对 JavaScript 不可见,但浏览器的网络检查器可以观察到
integrity
用于强制子资源完整性
必须是包含子资源完整性标识符的字符串,默认为空字符串。
keepalive
用于指示浏览器允许请求存在时间超出页面生命周期。适合报告事件或分析,比如页面在 fetch()
请求后很快卸载。设置 keepalive
标志的 fetch()
请求可用于替代 Navigator.sendBeacon()
必须是布尔值,默认为 false.
method
用于指定 HTTP 请求方法, 有如下:
-
GET
(默认值) -
POST
-
PUT
-
PATCH
-
DELETE
-
HEAD
: 与 GET 类似,但服务器只返回头部信息,不返回实际数据。通常用于检查资源是否已更改,以减少数据传输。 -
OPTIONS
: 获取有关资源的信息,或者关于服务器的功能的信息。通常用于 CORS(跨域资源共享)预检请求。 -
CONNECT
: 用于将服务器作为代理连接,通常用于安全隧道(例如,在通过 SSL/TLS 连接时建立代理)。 -
TRACE
: 用于在目的服务器上执行一个请求诊断。然而,由于安全性问题,大多数现代浏览器都禁用了 TRACE 方法
mode
用于指定请求模式。这个模式决定来自跨源请求的响应是否有效,以及客户端可以读取多少响应。违反这里指定模式的请求会抛出错误,必须是下列字符串值之一:
-
cors
(默认值):允许遵守 CORS 协议的跨源请求。 -
no-cors
: 允许不需要发送预检请求的跨源请求。 -
same-origin
: 任何跨源请求都不允许发送 -
navigate
:用于支持 HTML 导航,只在文档间导航时使用。
redirect
用 于指定如何处理重定向响应(状态码为 301、302、303、307 或 308),必须是下列字符串值之一:
-
follow
(默认值):跟踪重定向请求,以最终非重定向 URL 的响应作为最终响应 -
error
:重定向请求会抛出错误 -
manual
:不跟踪重定向请求,而是返回 opaqueredirect 类型的响应,同时仍然暴露期望的重定向 URL。允许以手动方式跟踪重定向
referrer
用于指定 HTTP 的 Referer 头部的内容
referrerPolicy
用于指定 HTTP 的 Referer 头部
signal
用于支持通过 AbortController 中断进行中的 fetch()请求,必须是 AbortSignal 的实例,默认为未关联控制器的 AbortSignal 实例
自定义选项不完整,带补充
常见 Fetch 请求模式
与 XMLHttpRequest 一样,fetch()
既可以发送数据也可以接收数据。使用 init 对象参数,可以配置 fetch()
在请求体中发送各种序列化的数据。
发送 JSON 数据
let payload = JSON.stringify({
foo: 'bar',
});
let jsonHeaders = new Headers({
'Content-Type': 'application/json',
});
fetch('/send-me-json', {
method: 'POST',
body: payload,
headers: jsonHeaders,
});
在请求体中发送参数
let payload = 'foo=bar&baz=qux';
let paramHeaders = new Headers({
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
});
fetch('/send-me-params', {
method: 'POST',
body: payload,
headers: paramHeaders,
});
发送文件
因为请求体支持 FormData 实现,所以 fetch()
也可以序列化并发送文件字段中的文件:
let imageFormData = new FormData();
let imageInput = document.querySelector("input[type='file']");
imageFormData.append('image', imageInput.files[0]);
fetch('/img-upload', {
method: 'POST',
body: imageFormData,
});
let imageFormData = new FormData();
let imageInput = document.querySelector("input[type='file'][multiple]");
for (let i = 0; i < imageInput.files.length; ++i) {
imageFormData.append('image', imageInput.files[i]);
}
fetch('/img-upload', {
method: 'POST',
body: imageFormData,
});
加载 Blob 文件
Fetch API也能提供 Blob 类型的响应,而 Blob 又可以兼容多种浏览器 API。一种常见的做法是明确将图片文件加载到内存,然后将其添加到 HTML图片元素。为此,可以使用响应对象上暴露的 blob()
方法。这个方法返回一个期约,解决为一个 Blob 的实例。然后,可以将这个实例传给 URL.createObjectUrl()
以生成可以添加给图片元素 src 属性的值:
const imageElement = document.querySelector('img');
fetch('my-image.png')
.then(response => response.blob())
.then(blob => {
imageElement.src = URL.createObjectURL(blob);
});