From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../web/api/fetch_api/basic_concepts/index.html | 66 ++++ .../fetch_api/cross-global_fetch_usage/index.html | 38 ++ files/zh-cn/web/api/fetch_api/index.html | 90 +++++ .../zh-cn/web/api/fetch_api/using_fetch/index.html | 391 +++++++++++++++++++++ 4 files changed, 585 insertions(+) create mode 100644 files/zh-cn/web/api/fetch_api/basic_concepts/index.html create mode 100644 files/zh-cn/web/api/fetch_api/cross-global_fetch_usage/index.html create mode 100644 files/zh-cn/web/api/fetch_api/index.html create mode 100644 files/zh-cn/web/api/fetch_api/using_fetch/index.html (limited to 'files/zh-cn/web/api/fetch_api') diff --git a/files/zh-cn/web/api/fetch_api/basic_concepts/index.html b/files/zh-cn/web/api/fetch_api/basic_concepts/index.html new file mode 100644 index 0000000000..fc87d2c5ef --- /dev/null +++ b/files/zh-cn/web/api/fetch_api/basic_concepts/index.html @@ -0,0 +1,66 @@ +--- +title: Fetch 基本概念 +slug: Web/API/Fetch_API/Basic_concepts +translation_of: Web/API/Fetch_API/Basic_concepts +--- +

{{DefaultAPISidebar("Fetch API")}}{{draft}}

+ +
+

Fetch 是一个现代的概念, 等同于 XMLHttpRequest。它提供了许多与XMLHttpRequest相同的功能,但被设计成更具可扩展性和高效性。本文介绍了 Fetch API的一些基本概念。

+
+ +
+

Note: 这篇文章可能还需要修改。如果你觉得有的概念可以解释的更好,让人们在MDN论坛上知道,或 Mozilla IRC (#mdn room)。

+
+ +

简而言之

+ +

Fetch 的核心在于对 HTTP 接口的抽象,包括 {{domxref("Request")}},{{domxref("Response")}},{{domxref("Headers")}},{{domxref("Body")}},以及用于初始化异步请求的 {{domxref("GlobalFetch.fetch","global fetch")}}。得益于 JavaScript 实现的这些抽象好的 HTTP 模块,其他接口能够很方便的使用这些功能。

+ +

Service Workers 是大量使用Fetch的API的一个示例。

+ +

除此之外,Fetch 还利用到了请求的异步特性——它是基于 {{jsxref("Promise-based","Promise")}} 的。

+ +

Guard

+ +

Guard 是 {{domxref("Headers")}} 对象的特性,基于不同的情况,它可以有以下取值:immutable、requestrequest-no-corsresponse 或 none。

+ +

当使用 {{domxref("Headers.Headers","Headers()")}} {{glossary("constructor")}} 创建一个新的 {{domxref("Headers")}} 对象的时候,它的 guard 被设置成 none(默认值)。当创建 {{domxref("Request")}} 或 {{domxref("Response")}} 对象的时候,它将拥有一个按照以下规则实现的与之关联的 {{domxref("Headers")}} 对象:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
新对象的类型创建时的构造函数关联的 {{domxref("Headers")}} 对象的 guard
{{domxref("Request")}}{{domxref("Request.Request","Request()")}}request
{{domxref("Request.Request","Request()")}},{{domxref("Request.mode","mode")}} 设置成 no-corsrequest-no-cors
{{domxref("Response")}}{{domxref("Response.Response","Response()")}}response
{{domxref("Response.error","error()")}} 或 {{domxref("Response.redirect","redirect()")}} 方法immutable
+ +

头信息的 guard 会影响 {{domxref("Headers.set","set()")}}、{{domxref("Headers.delete","delete()")}} 和 {{domxref("Headers.append","append()")}} 方法。如果你试图修改 guard 是 immutable 的 {{domxref("Headers")}} 对象,那么会抛出一个 TypeError。以下情况则不会抛出错误:

+ + diff --git a/files/zh-cn/web/api/fetch_api/cross-global_fetch_usage/index.html b/files/zh-cn/web/api/fetch_api/cross-global_fetch_usage/index.html new file mode 100644 index 0000000000..7ca474a67a --- /dev/null +++ b/files/zh-cn/web/api/fetch_api/cross-global_fetch_usage/index.html @@ -0,0 +1,38 @@ +--- +title: Cross-global fetch usage +slug: Web/API/Fetch_API/Cross-global_fetch_usage +tags: + - Fetch + - 相对 URL + - 跨源 + - 边缘情况 +translation_of: Web/API/Fetch_API/Cross-global_fetch_usage +--- +

本文解释了在fetch时发生的边缘情况(以及潜在的其他APIs展示相同类型的资源检索行为)。当从“iframe”发起包含相对url的跨源fetch时,相对url用于针对当前全局位置而不是iframe的位置进行解析。

+ +

边缘情况

+ +

大多数网站几乎不会遇到这种边缘情况。如下:

+ + + +

遇到的问题

+ +

以前,我们从当前全局 URL 中解析相对 URL,例如:

+ +
let absolute = new URL(relative, window.location.href)
+ +

这样做不是什么大问题,只是表现出这种行为的不同 API 与规范中定义的行为的不一致可能导致问题的进一步发展。

+ +

解决方案

+ +

在 Firefox 60 及以后版本中,Mozilla 对相对 URL 的解析是相对于拥有fetch()函数的全局的。(见 {{bug(1432272)}})。因此在上述情形中,URL 是相对于 iframe 的地址进行解析的:

+ +
let absolute = new URL(relative, frame.contentWindow.location.href)
+ +

关于使新规范与此行为变化保持一致,以缓解未来可能出现的问题,大量讨论正在进行中。

diff --git a/files/zh-cn/web/api/fetch_api/index.html b/files/zh-cn/web/api/fetch_api/index.html new file mode 100644 index 0000000000..4aa036332c --- /dev/null +++ b/files/zh-cn/web/api/fetch_api/index.html @@ -0,0 +1,90 @@ +--- +title: Fetch API +slug: Web/API/Fetch_API +tags: + - Fetch + - Fetch API + - Web + - XMLHttpRequest + - 参考 +translation_of: Web/API/Fetch_API +--- +

{{DefaultAPISidebar("Fetch API")}}

+ +

Fetch API 提供了一个获取资源的接口(包括跨域请求)。任何使用过 {{domxref("XMLHttpRequest")}} 的人都能轻松上手,而且新的 API 提供了更强大和灵活的功能集。

+ +

概念和用法

+ +

Fetch 提供了对 {{domxref("Request")}} 和 {{domxref("Response")}} (以及其他与网络请求有关的)对象的通用定义。使之今后可以被使用到更多地应用场景中:无论是 service worker、Cache API、又或者是其他处理请求和响应的方式,甚至是任何一种需要你自己在程序中生成响应的方式。

+ +

它同时还为有关联性的概念,例如CORS和HTTP原生头信息,提供一种新的定义,取代它们原来那种分离的定义。

+ +

发送请求或者获取资源,需要使用 {{domxref("WindowOrWorkerGlobalScope.fetch()")}} 方法。它在很多接口中都被实现了,更具体地说,是在 {{domxref("Window")}} 和 {{domxref("WorkerGlobalScope")}} 接口上。因此在几乎所有环境中都可以用这个方法获取到资源。

+ +

 fetch() 必须接受一个参数——资源的路径。无论请求成功与否,它都返回一个 Promise 对象,resolve 对应请求的 {{domxref("Response")}}。你也可以传一个可选的第二个参数 init(参见 {{domxref("Request")}})。

+ +

一旦 {{domxref("Response")}} 被返回,就可以使用一些方法来定义内容的形式,以及应当如何处理内容(参见 {{domxref("Body")}})。

+ +

你也可以通过 {{domxref("Request.Request","Request()")}} 和 {{domxref("Response.Response","Response()")}} 的构造函数直接创建请求和响应,但是我们不建议这么做。他们应该被用于创建其他 API 的结果(比如,service workers 中的 {{domxref("FetchEvent.respondWith")}})。

+ +
+

注意:更多关于 Fetch API 的用法,参考 Using Fetch,以及一些概念 Fetch basic concepts

+
+ +

中止 fetch

+ +

浏览器已经开始为 {{domxref("AbortController")}} 和 {{domxref("AbortSignal")}} 接口(也就是Abort API)添加实验性支持,允许像 Fetch 和 XHR 这样的操作在还未完成时被中止 。请参阅接口页面了解更多详情。

+ +

Fetch 接口

+ +
+
{{domxref("WindowOrWorkerGlobalScope.fetch()")}}
+
包含了fetch() 方法,用于获取资源。
+
{{domxref("Headers")}}
+
相当于 response/request 的头信息,可以使你查询到这些头信息,或者针对不同的结果做不同的操作。
+
{{domxref("Request")}}
+
相当于一个资源请求。
+
{{domxref("Response")}}
+
相当于请求的响应
+
+ +

Fetch mixin

+ +
+
{{domxref("Body")}}
+
提供了与 response/request 中的 body 有关的方法,可以定义它的内容形式以及处理方式。
+
+ +

规范

+ + + + + + + + + + + + + + +
规范状态说明
{{SpecName('Fetch')}}{{Spec2('Fetch')}}Initial definition
+ +

浏览器兼容

+ + + +

{{Compat("api.WindowOrWorkerGlobalScope.fetch")}}

+ +

相关链接

+ + diff --git a/files/zh-cn/web/api/fetch_api/using_fetch/index.html b/files/zh-cn/web/api/fetch_api/using_fetch/index.html new file mode 100644 index 0000000000..eb8f12659b --- /dev/null +++ b/files/zh-cn/web/api/fetch_api/using_fetch/index.html @@ -0,0 +1,391 @@ +--- +title: 使用 Fetch +slug: Web/API/Fetch_API/Using_Fetch +tags: + - API + - BODY + - Fetch + - HTTP + - Promise + - Response + - request + - 指南 +translation_of: Web/API/Fetch_API/Using_Fetch +--- +

Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。它还提供了一个全局 {{domxref("GlobalFetch.fetch","fetch()")}} 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

+ +

这种功能以前是使用 {{domxref("XMLHttpRequest")}} 实现的。Fetch 提供了一个更理想的替代方案,可以很容易地被其他技术使用,例如  {{domxref("ServiceWorker_API", "Service Workers")}}。Fetch 还提供了专门的逻辑空间来定义其他与 HTTP 相关的概念,例如 CORS 和 HTTP 的扩展。

+ +

请注意,fetch 规范与 jQuery.ajax() 主要有三种方式的不同:

+ + + +

一个基本的 fetch 请求设置起来很简单。看看下面的代码:

+ +
fetch('http://example.com/movies.json')
+  .then(function(response) {
+    return response.json();
+  })
+  .then(function(myJson) {
+    console.log(myJson);
+  });
+ +

这里我们通过网络获取一个 JSON 文件并将其打印到控制台。最简单的用法是只提供一个参数用来指明想 fetch() 到的资源路径,然后返回一个包含响应结果的promise(一个 {{domxref("Response")}} 对象)。

+ +

当然它只是一个 HTTP 响应,而不是真的JSON。为了获取JSON的内容,我们需要使用 {{domxref("Body.json","json()")}} 方法(在 {{domxref("Body")}} mixin 中定义,被 {{domxref("Request")}} 和 {{domxref("Response")}} 对象实现)。

+ +
+

注意:Body mixin 还有其他相似的方法,用于获取其他类型的内容。参考 {{anch("Body")}}。

+
+ +

最好使用符合内容安全策略 (CSP)的链接而不是使用直接指向资源地址的方式来进行Fetch的请求。

+ +

支持的请求参数

+ +

fetch() 接受第二个可选参数,一个可以控制不同配置的 init 对象:

+ +

参考 {{domxref("GlobalFetch.fetch","fetch()")}},查看所有可选的配置和更多描述。

+ +
// Example POST method implementation:
+
+postData('http://example.com/answer', {answer: 42})
+  .then(data => console.log(data)) // JSON from `response.json()` call
+  .catch(error => console.error(error))
+
+function postData(url, data) {
+  // Default options are marked with *
+  return fetch(url, {
+    body: JSON.stringify(data), // must match 'Content-Type' header
+    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
+    credentials: 'same-origin', // include, same-origin, *omit
+    headers: {
+      'user-agent': 'Mozilla/4.0 MDN Example',
+      'content-type': 'application/json'
+    },
+    method: 'POST', // *GET, POST, PUT, DELETE, etc.
+    mode: 'cors', // no-cors, cors, *same-origin
+    redirect: 'follow', // manual, *follow, error
+    referrer: 'no-referrer', // *client, no-referrer
+  })
+  .then(response => response.json()) // parses response to JSON
+}
+
+ +

发送带凭据的请求

+ +

为了让浏览器发送包含凭据的请求(即使是跨域源),要将credentials: 'include'添加到传递给 fetch()方法的init对象。

+ +
fetch('https://example.com', {
+  credentials: 'include'
+})
+
+ +

如果你只想在请求URL与调用脚本位于同一起源处时发送凭据,请添加 credentials: 'same-origin'

+ +
// The calling script is on the origin 'https://example.com'
+
+fetch('https://example.com', {
+  credentials: 'same-origin'
+})
+ +

要改为确保浏览器不在请求中包含凭据,请使用 credentials: 'omit'

+ +
fetch('https://example.com', {
+  credentials: 'omit'
+})
+ +

上传 JSON 数据

+ +

使用 {{domxref("GlobalFetch.fetch","fetch()")}} POST JSON数据

+ +
var url = 'https://example.com/profile';
+var data = {username: 'example'};
+
+fetch(url, {
+  method: 'POST', // or 'PUT'
+  body: JSON.stringify(data), // data can be `string` or {object}!
+  headers: new Headers({
+    'Content-Type': 'application/json'
+  })
+}).then(res => res.json())
+.catch(error => console.error('Error:', error))
+.then(response => console.log('Success:', response));
+ +

上传文件

+ +

可以通过 HTML <input type="file" /> 元素,{{domxref("FormData.FormData","FormData()")}} 和 {{domxref("GlobalFetch.fetch","fetch()")}} 上传文件。

+ +
var formData = new FormData();
+var fileField = document.querySelector("input[type='file']");
+
+formData.append('username', 'abc123');
+formData.append('avatar', fileField.files[0]);
+
+fetch('https://example.com/profile/avatar', {
+  method: 'PUT',
+  body: formData
+})
+.then(response => response.json())
+.catch(error => console.error('Error:', error))
+.then(response => console.log('Success:', response));
+ +

上传多个文件

+ +

可以通过HTML <input type="file" mutiple/> 元素,{{domxref("FormData.FormData","FormData()")}} 和 {{domxref("GlobalFetch.fetch","fetch()")}} 上传文件。

+ +
var formData = new FormData();
+var photos = document.querySelector("input[type='file'][multiple]");
+
+formData.append('title', 'My Vegas Vacation');
+// formData 只接受文件、Blob 或字符串,不能直接传递数组,所以必须循环嵌入
+for (let i = 0; i < photos.files.length; i++) {
+    formData.append('photo', photos.files[i]);
+}
+
+fetch('https://example.com/posts', {
+  method: 'POST',
+  body: formData
+})
+.then(response => response.json())
+.then(response => console.log('Success:', JSON.stringify(response)))
+.catch(error => console.error('Error:', error));
+
+ +

检测请求是否成功

+ +

如果遇到网络故障,{{domxref("GlobalFetch.fetch","fetch()")}} promise 将会 reject,带上一个 {{jsxref("TypeError")}} 对象。虽然这个情况经常是遇到了权限问题或类似问题——比如 404 不是一个网络故障。想要精确的判断 fetch() 是否成功,需要包含 promise resolved 的情况,此时再判断 {{domxref("Response.ok")}} 是不是为 true。类似以下代码:

+ +
fetch('flowers.jpg').then(function(response) {
+  if(response.ok) {
+    return response.blob();
+  }
+  throw new Error('Network response was not ok.');
+}).then(function(myBlob) {
+  var objectURL = URL.createObjectURL(myBlob);
+  myImage.src = objectURL;
+}).catch(function(error) {
+  console.log('There has been a problem with your fetch operation: ', error.message);
+});
+ +

自定义请求对象

+ +

除了传给 fetch() 一个资源的地址,你还可以通过使用 {{domxref("Request.Request","Request()")}} 构造函数来创建一个 request 对象,然后再作为参数传给 fetch()

+ +
var myHeaders = new Headers();
+
+var myInit = { method: 'GET',
+               headers: myHeaders,
+               mode: 'cors',
+               cache: 'default' };
+
+var myRequest = new Request('flowers.jpg', myInit);
+
+fetch(myRequest).then(function(response) {
+  return response.blob();
+}).then(function(myBlob) {
+  var objectURL = URL.createObjectURL(myBlob);
+  myImage.src = objectURL;
+});
+
+ +

Request()fetch() 接受同样的参数。你甚至可以传入一个已存在的 request 对象来创造一个拷贝:

+ +
var anotherRequest = new Request(myRequest,myInit);
+ +

这个很有用,因为 request 和 response bodies 只能被使用一次(译者注:这里的意思是因为设计成了 stream 的方式,所以它们只能被读取一次)。创建一个拷贝就可以再次使用 request/response 了,当然也可以使用不同的 init 参数。

+ +
+

注意:{{domxref("Request.clone","clone()")}} 方法也可以用于创建一个拷贝。它和上述方法一样,如果 request 或 response 的 body 已经被读取过,那么将执行失败。区别在于, clone() 出的 body 被读取不会导致原 body 被标记为已读取。

+
+ +

Headers

+ +

使用 {{domxref("Headers")}} 的接口,你可以通过 {{domxref("Headers.Headers","Headers()")}} 构造函数来创建一个你自己的 headers 对象。一个 headers 对象是一个简单的多名值对:

+ +
var content = "Hello World";
+var myHeaders = new Headers();
+myHeaders.append("Content-Type", "text/plain");
+myHeaders.append("Content-Length", content.length.toString());
+myHeaders.append("X-Custom-Header", "ProcessThisImmediately");
+ +

也可以传一个多维数组或者对象字面量:

+ +
myHeaders = new Headers({
+  "Content-Type": "text/plain",
+  "Content-Length": content.length.toString(),
+  "X-Custom-Header": "ProcessThisImmediately",
+});
+ +

它的内容可以被获取:

+ +
console.log(myHeaders.has("Content-Type")); // true
+console.log(myHeaders.has("Set-Cookie")); // false
+myHeaders.set("Content-Type", "text/html");
+myHeaders.append("X-Custom-Header", "AnotherValue");
+
+console.log(myHeaders.get("Content-Length")); // 11
+console.log(myHeaders.getAll("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]
+
+myHeaders.delete("X-Custom-Header");
+console.log(myHeaders.getAll("X-Custom-Header")); // [ ]
+ +

虽然一些操作只能在 {{domxref("ServiceWorker_API","ServiceWorkers")}} 中使用,但是它提供了更方便的操作 Headers 的 API。

+ +

如果使用了一个不合法的HTTP Header属性名,那么Headers的方法通常都抛出 TypeError 异常。如果不小心写入了一个不可写的属性,也会抛出一个 TypeError 异常。除此以外的情况,失败了并不抛出异常。例如:

+ +
var myResponse = Response.error();
+try {
+  myResponse.headers.set("Origin", "http://mybank.com");
+} catch(e) {
+  console.log("Cannot pretend to be a bank!");
+}
+ +

最好在在使用之前检查内容类型 content-type 是否正确,比如:

+ +
fetch(myRequest).then(function(response) {
+  if(response.headers.get("content-type") === "application/json") {
+    return response.json().then(function(json) {
+      // process your JSON further
+    });
+  } else {
+    console.log("Oops, we haven't got JSON!");
+  }
+});
+ +

Guard

+ +

由于 Headers 可以在 request 请求中被发送或者在 response 请求中被接收,并且规定了哪些参数是可写的,Headers 对象有一个特殊的 guard 属性。这个属性没有暴露给 Web,但是它影响到哪些内容可以在 Headers 对象中被操作。

+ +

可能的值如下:

+ + + +
+

注意:你不可以添加或者修改一个 guard 属性是 request 的 Request Header 的 Content-Length 属性。同样地,插入 Set-Cookie 属性到一个 response header 是不允许的,因此,Service Worker 中,不能给合成的 Response 的 header 添加一些 cookie。

+
+ +

Response 对象

+ +

如上所述,{{domxref("Response")}} 实例是在 fetch() 处理完 promise 之后返回的。

+ +

你会用到的最常见的 response 属性有:

+ + + +

它的实例也可用通过 JavaScript 来创建,但只有在 {{domxref("ServiceWorker_API", "ServiceWorkers")}} 中才真正有用,当使用 {{domxref("FetchEvent.respondWith","respondWith()")}} 方法并提供了一个自定义的 response 来接受 request 时:

+ +
var myBody = new Blob();
+
+addEventListener('fetch', function(event) {
+  event.respondWith(new Response(myBody, {
+    headers: { "Content-Type" : "text/plain" }
+  });
+});
+ +

{{domxref("Response.Response","Response()")}} 构造方法接受两个可选参数—— response 的数据体和一个初始化对象(与{{domxref("Request.Request","Request()")}} 所接受的 init 参数类似。)

+ + + +
+

注意: 静态方法 {{domxref("Response.error","error()")}} 只是返回了错误的response。与此类似地,{{domxref("Response.redirect","redirect()")}} 只是返回了一个可以重定向至某 URL 的 response。这些也只与 Service Worker 有关。

+
+ +

Body

+ +

不管是请求还是响应都能够包含 body 对象。body 也可以是以下任意类型的实例。

+ + + +

{{domxref("Body")}} 类定义了以下方法(这些方法都被 {{domxref("Request")}} 和{{domxref("Response")}}所实现)以获取 body 内容。这些方法都会返回一个被解析后的{{domxref("Promise")}}对象和数据。

+ + + +

比起XHR来,这些方法让非文本化的数据使用起来更加简单。

+ +

请求体可以由传入 body 参数来进行设置:

+ +
var form = new FormData(document.getElementById('login-form'));
+fetch("/login", {
+  method: "POST",
+  body: form
+})
+
+ +

request和response(包括 fetch() 方法)都会试着自动设置 Content-Type。如果没有设置 Content-Type 值,发送的请求也会自动设值。

+ +

特性检测

+ +

Fetch API 的支持情况,可以通过检测{{domxref("Headers")}}, {{domxref("Request")}}, {{domxref("Response")}} 或 {{domxref("GlobalFetch.fetch","fetch()")}}是否在{{domxref("Window")}} 或 {{domxref("Worker")}} 域中。例如:

+ +
if(self.fetch) {
+    // run my fetch request here
+} else {
+    // do something with XMLHttpRequest?
+}
+ +

Polyfill

+ +

如果要在不支持的浏览器中使用 Fetch,可以使用 Fetch Polyfill

+ +

规范

+ + + + + + + + + + + + + + +
详细说明状态注释
{{SpecName('Fetch')}}{{Spec2('Fetch')}}Initial definition
+ +

浏览器兼容性

+ + + +

{{Compat("api.WindowOrWorkerGlobalScope.fetch")}}

+ +

参见

+ + + +
{{DefaultAPISidebar("Fetch API")}}
-- cgit v1.2.3-54-g00ecf