Fetch

1 概念

Fetch 基于 Primise 提供了获取资源的功能(包括跨域请求),与 XMLHttpRequest 的很多功能相似,但使用更方便,且更具有可扩展性和高效性。

Fetch 的核心在于为 Request、Response、Body、CORS、HTTP 等提供了一种通用的定义方法,取代了它们之前分离的定义方式。

2 Fetch 与 jquery.ajax 的不同

  • 当接收到错误的 HTTP 状态码时,从 Fetch 返回的 Promise 会被标记为 resolve (但会将 resolve 返回值的 ok 属性设置为 false),当且仅当网络故障或请求被终止时,才会才会被标记为 reject。

  • 默认 Fetch 不会给服务器发送或接收服务器的 cookie,除非设置 credentials 选项。(自 2017 年 8 月 25 日以后,默认的 credentials 政策变更为 same-origin。Firefox 也在 61.0b13 版本中进行了修改)

3 语法

1
Promise<Response> fetch(input[, init]);

3.1 参数

?input

定义要获取的资源。这可能是:

  • 一个 USVString 字符串,包含要获取资源的 URL。一些浏览器会接受 blob:data: 作为 schemes.
  • 一个 Request 对象。

init 可选

一个配置项对象,包括所有对请求的设置。可选的参数有:

  • method: 请求使用的方法,如 GETPOST
  • headers: 请求的头信息,形式为 Headers 的对象或包含 ByteString 值的对象字面量。
  • body: 请求的 body 信息:可能是一个 BlobBufferSourceFormDataURLSearchParams 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。
  • mode: 请求的模式,如 cors、 no-cors 或者 same-origin。
  • credentials: 请求的 credentials,如 omit、same-origin 或者 include。为了在当前域名内自动发送 cookie , 必须设置该选项为 same-origin, 从 Chrome 50 开始, 这个属性也可以接受 FederatedCredential 实例或是一个 PasswordCredential 实例。
  • cache: 请求的 cache 模式: default no-store 、 reload 、 no-cache 、 force-cache 或者 only-if-cached 。
  • redirect: 可用的 redirect 模式: follow (自动重定向), error (如果产生重定向将自动终止并且抛出一个错误), 或者 manual (手动处理重定向). 在Chrome中,Chrome 47之前的默认值是 follow,从 Chrome 47开始是 manual。
  • referrer: 一个 USVString 可以是 no-referrer、``client或一个 URL。默认是 client。
  • referrerPolicy: 指定了HTTP头部referer字段的值。可能为以下值之一: no-referrer、 no-referrer-when-downgrade、 origin、 origin-when-cross-origin、 unsafe-url 。
  • integrity: 包括请求的 subresource integrity 值 ( 例如: sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=)。

3.2 返回值

一个 Promise,resolve 时会传 Response 对象。

4 示例

4.1 判断请求是否成功

当response.ok 为 true 时,请求才成功

1
2
3
4
5
6
7
8
9
10
11
fetch('flowers.jpg').then(function(response) {
if(response.ok) {//当response.ok 为 true 时,请求才成功
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);
});

4.2 发送带凭证的请求

通过 credentials 选项配置凭证

1
2
3
4
5
6
fetch('https://example.com', {
//include 表示携带凭证,即使是跨域请求
//same-origin 表示同源时才携带凭证
//omit 表示不携带凭证
credentials: 'include'
})

4.3 上传 JSON 数据

1
2
3
4
5
6
7
8
9
10
11
12
13
var url = 'https://example.com/profile';
var data = {username: 'example'};

fetch(url, {
method: 'POST', // or 'PUT'
//通过 body 字段发送 json 数据
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));

4.4 上传文件

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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));

4.5 上传多个文件

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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));

4.6 自定义请求对象

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 对象来创造一个拷贝:

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

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

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

1