SIGN IN SIGN UP

现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。

0 0 0 HTML
2019-06-11 20:53:47 +08:00
# Fetch API
到目前为止,我们已经对 `fetch` 相当了解了。
2019-06-11 20:53:47 +08:00
现在让我们来看看 `fetch` 的剩余 API来了解它的全部本领吧。
2019-06-11 20:53:47 +08:00
2019-11-29 15:52:31 +08:00
```smart
请注意:这些选项 (option) 大多都很少使用。即使跳过本章,你也可以很好地使用 `fetch`。
2019-11-29 15:52:31 +08:00
但是,知道 `fetch` 可以做什么还是很好的,所以如果需要,你可以来看看这些细节内容。
2019-11-29 15:52:31 +08:00
```
这是所有可能的 `fetch` 选项及其默认值(注释中标注了可选值)的完整列表:
2019-06-11 20:53:47 +08:00
```js
let promise = fetch(url, {
method: "GET", // POSTPUTDELETE等。
2019-06-11 20:53:47 +08:00
headers: {
// 内容类型 header 值通常是自动设置的
// 取决于 request body
2019-12-14 14:38:18 +08:00
"Content-Type": "text/plain;charset=UTF-8"
2019-06-11 20:53:47 +08:00
},
body: undefined // stringFormDataBlobBufferSource或 URLSearchParams
referrer: "about:client", // 或 "" 以不发送 Referer header
// 或者是当前源的 url
referrerPolicy: "no-referrer-when-downgrade", // no-referreroriginsame-origin...
mode: "cors", // same-originno-cors
credentials: "same-origin", // omitinclude
cache: "default", // no-storereloadno-cacheforce-cache或 only-if-cached
redirect: "follow", // manualerror
integrity: "", // 一个 hash像 "sha256-abcdef1234567890"
2019-06-11 20:53:47 +08:00
keepalive: false, // true
signal: undefined, // AbortController 来中止请求
2019-06-11 20:53:47 +08:00
window: window // null
});
```
一个令人印象深刻的列表,对吧?
2019-06-11 20:53:47 +08:00
我们已经在 <info:fetch> 一章中详细介绍了 `method``headers``body`
2019-06-11 20:53:47 +08:00
在 <info:fetch-abort> 一章中介绍了 `signal` 选项。
2019-06-11 20:53:47 +08:00
2022-04-21 09:36:57 +08:00
现在让我们一起探索其余的功能。
2019-06-11 20:53:47 +08:00
## referrerreferrerPolicy
2019-06-11 20:53:47 +08:00
这些选项决定了 `fetch` 如何设置 HTTP 的 `Referer` header。
2019-06-11 20:53:47 +08:00
通常来说,这个 header 是被自动设置的,并包含了发出请求的页面的 url。在大多数情况下它一点也不重要但有时出于安全考虑删除或缩短它是有意义的。
2019-06-11 20:53:47 +08:00
**`referrer` 选项允许设置任何 `Referer`(在当前域的),或者移除它。**
2019-06-11 20:53:47 +08:00
如果不想发送 referrer可以将 `referrer` 设置为空字符串:
2019-06-11 20:53:47 +08:00
```js
fetch('/page', {
*!*
referrer: "" // 没有 Referer header
2019-06-11 20:53:47 +08:00
*/!*
});
```
设置在当前域内的另一个 url
2019-06-11 20:53:47 +08:00
```js
fetch('/page', {
2019-11-29 15:52:31 +08:00
// 假设我们在 https://javascript.info
// 我们可以设置任何 Referer header但必须是在当前域内的
2019-06-11 20:53:47 +08:00
*!*
referrer: "https://javascript.info/anotherpage"
*/!*
});
```
**`referrerPolicy` 选项为 `Referer` 设置一般的规则。**
请求分为 3 种类型:
2019-11-29 15:52:31 +08:00
1. 同源请求。
2020-08-02 14:25:56 +08:00
2. 跨源请求。
3. 从 HTTPS 到 HTTP 的请求 (从安全协议到不安全协议)。
2019-11-29 15:52:31 +08:00
`referrer` 选项允许设置确切的 `Referer` 值不同,`referrerPolicy` 告诉浏览器针对各个请求类型的一般的规则。
2019-11-29 15:52:31 +08:00
可能的值在 [Referrer Policy 规范](https://w3c.github.io/webappsec-referrer-policy/)中有详细描述:
2019-11-29 15:52:31 +08:00
- **`"no-referrer-when-downgrade"`** —— 默认值:除非我们从 HTTPS 发送请求到 HTTP到安全性较低的协议否则始终会发送完整的 `Referer`
- **`"no-referrer"`** —— 从不发送 `Referer`
- **`"origin"`** —— 只发送在 `Referer` 中的域,而不是完整的页面 URL例如只发送 `http://site.com` 而不是 `http://site.com/path`
- **`"origin-when-cross-origin"`** —— 发送完整的 `Referer` 到相同的源,但对于跨源请求,只发送域部分(同上)。
- **`"same-origin"`** —— 发送完整的 `Referer` 到相同的源,但对于跨源请求,不发送 `Referer`
- **`"strict-origin"`** —— 只发送域,对于 HTTPS→HTTP 请求,则不发送 `Referer`
- **`"strict-origin-when-cross-origin"`** —— 对于同源情况下则发送完整的 `Referer`,对于跨源情况下,则只发送域,如果是 HTTPS→HTTP 请求,则什么都不发送。
- **`"unsafe-url"`** —— 在 `Referer` 中始终发送完整的 url即使是 HTTPS→HTTP 请求。
2019-11-29 15:52:31 +08:00
这是一个包含所有组合的表格:
2019-11-29 15:52:31 +08:00
| 值 | 同源 | 跨源 | HTTPS→HTTP |
2019-11-29 15:52:31 +08:00
|-------|----------------|-------------------|------------|
| `"no-referrer"` | - | - | - |
| `"no-referrer-when-downgrade"``""`(默认) | 完整的 url | 完整的 url | - |
| `"origin"` | 仅域 | 仅域 | 仅域 |
| `"origin-when-cross-origin"` | 完整的 url | 仅域 | 仅域 |
2019-11-29 15:52:31 +08:00
| `"same-origin"` | 完整的 url | - | - |
| `"strict-origin"` | 仅域 | 仅域 | - |
| `"strict-origin-when-cross-origin"` | 完整的 url | 仅域 | - |
2019-11-29 15:52:31 +08:00
| `"unsafe-url"` | 完整的 url | 完整的 url | 完整的 url |
2019-06-11 20:53:47 +08:00
假如我们有一个带有 URL 结构的管理区域admin zone它不应该被从网站外看到。
2019-06-11 20:53:47 +08:00
如果我们发送了一个 `fetch`,则默认情况下,它总是发送带有页面完整 url 的 `Referer` header我们从 HTTPS 向 HTTP 发送请求的情况除外,这种情况下没有 `Referer`)。
2019-06-11 20:53:47 +08:00
例如 `Referer: https://javascript.info/admin/secret/paths`
2019-06-11 20:53:47 +08:00
如果我们想让其他网站只知道域的部分,而不是 URL 路径,我们可以这样设置选项:
2019-06-11 20:53:47 +08:00
2019-11-29 15:52:31 +08:00
```js
fetch('https://another.com/page', {
// ...
referrerPolicy: "origin-when-cross-origin" // Referer: https://javascript.info
});
```
我们可以将其置于所有 `fetch` 调用中,也可以将其集成到我们项目的执行所有请求并在内部使用 `fetch` 的 JavaScript 库中。
2019-11-29 15:52:31 +08:00
与默认行为相比,它的唯一区别在于,对于跨源请求,`fetch` 只发送 URL 域的部分(例如 `https://javascript.info`,没有路径)。对于同源请求,我们仍然可以获得完整的 `Referer`(可能对于调试目的是有用的)。
2019-11-29 15:52:31 +08:00
```smart header="Referrer policy 不仅适用于 `fetch`"
在 [规范](https://w3c.github.io/webappsec-referrer-policy/) 中描述的 referrer policy不仅适用于 `fetch`,它还具有全局性。
2019-11-29 15:52:31 +08:00
特别是,可以使用 `Referrer-Policy` HTTP header或者为每个链接设置 `<a rel="noreferrer">`来为整个页面设置默认策略policy
2019-12-14 14:38:18 +08:00
```
2019-06-11 20:53:47 +08:00
## mode
`mode` 选项是一种安全措施,可以防止偶发的跨源请求:
2019-06-11 20:53:47 +08:00
- **`"cors"`** —— 默认值,允许跨源请求,如 <info:fetch-crossorigin> 一章所述,
- **`"same-origin"`** —— 禁止跨源请求,
2022-04-21 09:36:57 +08:00
- **`"no-cors"`** —— 只允许安全的跨源请求。
2019-06-11 20:53:47 +08:00
当 `fetch` 的 URL 来自于第三方,并且我们想要一个“断电开关”来限制跨源能力时,此选项可能很有用。
2019-06-11 20:53:47 +08:00
## credentials
`credentials` 选项指定 `fetch` 是否应该随请求发送 cookie 和 HTTP-Authorization header。
2019-06-11 20:53:47 +08:00
- **`"same-origin"`** —— 默认值,对于跨源请求不发送,
2022-04-21 09:36:57 +08:00
- **`"include"`** —— 总是发送,需要来自跨源服务器的 `Access-Control-Allow-Credentials`,才能使 JavaScript 能够访问响应,详细内容在 <info:fetch-crossorigin> 一章有详细介绍,
- **`"omit"`** —— 不发送,即使对于同源请求。
2019-06-11 20:53:47 +08:00
## cache
默认情况下,`fetch` 请求使用标准的 HTTP 缓存。就是说,它遵从 `Expires``Cache-Control` header发送 `If-Modified-Since`,等。就像常规的 HTTP 请求那样。
2019-06-11 20:53:47 +08:00
使用 `cache` 选项可以忽略 HTTP 缓存或者对其用法进行微调:
2019-06-11 20:53:47 +08:00
- **`"default"`** —— `fetch` 使用标准的 HTTP 缓存规则和 header
- **`"no-store"`** —— 完全忽略 HTTP 缓存,如果我们设置 header `If-Modified-Since``If-None-Match``If-Unmodified-Since``If-Match`,或 `If-Range`,则此模式会成为默认模式,
2022-04-21 09:36:57 +08:00
- **`"reload"`** —— 不从 HTTP 缓存中获取结果(如果有),而是使用响应填充缓存(如果 response header 允许此操作),
- **`"no-cache"`** —— 如果有一个已缓存的响应,则创建一个有条件的请求,否则创建一个普通的请求。使用响应填充 HTTP 缓存,
- **`"force-cache"`** —— 使用来自 HTTP 缓存的响应即使该响应已过时stale。如果 HTTP 缓存中没有响应,则创建一个常规的 HTTP 请求,行为像正常那样,
- **`"only-if-cached"`** —— 使用来自 HTTP 缓存的响应即使该响应已过时stale。如果 HTTP 缓存中没有响应,则报错。只有当 `mode` 为 `same-origin` 时生效。
2019-06-11 20:53:47 +08:00
## redirect
通常来说,`fetch` 透明地遵循 HTTP 重定向,例如 301302 等。
2019-06-11 20:53:47 +08:00
`redirect` 选项允许对此进行更改:
2019-06-11 20:53:47 +08:00
- **`"follow"`** —— 默认值,遵循 HTTP 重定向,
- **`"error"`** —— HTTP 重定向时报错,
2022-04-21 09:36:57 +08:00
- **`"manual"`** —— 允许手动处理 HTTP 重定向。在重定向的情况下,我们将获得一个特殊的响应对象,其中包含 `response.type="opaqueredirect"` 和归零/空状态以及大多数其他属性。
2019-06-11 20:53:47 +08:00
## integrity
`integrity` 选项允许检查响应是否与已知的预先校验和相匹配。
2019-06-11 20:53:47 +08:00
正如 [规范](https://w3c.github.io/webappsec-subresource-integrity/) 所描述的,支持的哈希函数有 SHA-256SHA-384和 SHA-512可能还有其他的这取决于浏览器。
2019-06-11 20:53:47 +08:00
例如,我们下载一个文件,并且我们知道它的 SHA-256 校验和为 "abcdef"(当然,实际校验和会更长)。
2019-06-11 20:53:47 +08:00
我们可以将其放在 `integrity` 选项中,就像这样:
2019-06-11 20:53:47 +08:00
```js
fetch('http://site.com/file', {
2019-11-29 15:52:31 +08:00
integrity: 'sha256-abcdef'
2019-06-11 20:53:47 +08:00
});
```
然后 `fetch` 将自行计算 SHA-256 并将其与我们的字符串进行比较。如果不匹配,则会触发错误。
2019-06-11 20:53:47 +08:00
## keepalive
`keepalive` 选项表示该请求可能会在网页关闭后继续存在。
2019-06-11 20:53:47 +08:00
例如,我们收集有关当前访问者是如何使用我们的页面(鼠标点击,他查看的页面片段)的统计信息,以分析和改善用户体验。
2019-06-11 20:53:47 +08:00
当访问者离开我们的网页时 —— 我们希望能够将数据保存到我们的服务器上。
2019-06-11 20:53:47 +08:00
我们可以使用 `window.onunload` 事件来实现:
2019-06-11 20:53:47 +08:00
```js run
window.onunload = function() {
fetch('/analytics', {
method: 'POST',
body: "statistics",
*!*
keepalive: true
*/!*
});
};
```
通常当一个文档被卸载时unloaded所有相关的网络请求都会被中止。但是`keepalive` 选项告诉浏览器,即使在离开页面后,也要在后台执行请求。所以,此选项对于我们的请求成功至关重要。
2019-06-11 20:53:47 +08:00
2019-11-29 15:52:31 +08:00
它有一些限制:
2019-12-14 14:38:18 +08:00
2022-04-21 09:36:57 +08:00
- 我们无法发送兆字节的数据:`keepalive` 请求的 body 限制为 64KB。
2020-11-14 13:11:13 +08:00
- 如果我们需要收集有关访问的大量统计信息,我们则应该将其定期以数据包的形式发送出去,这样就不会留下太多数据给最后的 `onunload` 请求了。
- 此限制是被应用于当前所有 `keepalive` 请求的总和的。换句话说,我们可以并行执行多个 `keepalive` 请求,但它们的 body 长度之和不得超过 64KB。
- 如果文档document已卸载unloaded我们就无法处理服务器响应。因此在我们的示例中因为 `keepalive`,所以 `fetch` 会成功,但是后续的函数将无法正常工作。
- 在大多数情况下,例如发送统计信息,这不是问题,因为服务器只接收数据,并通常向此类请求发送空的响应。