Fetch API快速入门及参数详解
写在前面: Fetch API是一种基于Promise、由Web原生提供的、更现代的实现Ajax的技术。本文对Fetch API的使用进行了简单介绍,同时更注重于与Fethc API相关的接口的参数解析。
从一个简单的fetch开始
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => {
console.log('response', response);
return response.json();
})
.then((json) => {
console.log('JSON', json);
});
简单看出fetch有两个特点:
- fetch的返回值是一个
Promise
,请求的Response
在这个Promise
里 - 需要经过两个
Promise
才能拿到真正的服务器返回的数据
fetch()
方法
fetch()
方法接受两个参数:
resource
- 经常是一个URL字符串,也可以是一个Request
对象init options
- 如果第一个参数是URL字符串,可以借助该参数配置一些请求参数
fetch()
方法除了可以直接传参数调用,也可以直接传一个Request
对象。二者是等价的。
init options参数
const res = fetch('url', init);
const init = {
method: 'POST', // *GET, POST, PUT, DELETE...
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded'
// 'Content-Type': 'text/plain'
// ...
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', /* no-referrer,
no-referrer-when-downgrade,
origin,
origin-when-cross-origin,
same-origin,
strict-origin,
*strict-origin-when-cross-origin,
unsafe-url */
body: JSON.stringify(data), // body data type must match "Content-Type" header
signal: signal, // AbortSignal object instance
};
method
: HTTP请求方法,默认使用GET
mode
: 指定该请求是- 跨域(
cors
),默认值 - 仅允许同源(
same-origin
),否则抛出TypeError
no-cors
:- 仅允许
HEAD
,GET
和POST
方法 - 仅可以设置simple headers
- 可以发出请求但是不能拿到
Response
- 仅允许
- 跨域(
cache
: 指定该请求的HTTP缓存规则,详情解释可以参考MDN Request.cache,但是还是推荐通过HTTP header设置Cache-Control
来控制客户端使用缓存规则。default
: 默认行为,fresh使用cache,stale与服务器通信检查资源变化(协商缓存)no-cache
: 无论fresh还是stale,都与服务器通信检查资源变化(协商缓存)force-cache
: 无论fresh还是stale,都使用cache
credentials
: 指定该请求是否携带cookie或其他credentialsinclude
: 总是携带cookie,无论同源还是跨域same-origin
: 默认值,仅在同源请求时携带cookieomit
: 从不携带cookie
headers
: 接受一个string object或者Headers对象referrer
: 标识请求的“来源”,一般不建议手动设置- same-origin URL
about:client
- empty string
“”
referrerpolicy
: 指定设置 Referer HTTP header的规则- 默认值为
strict-origin-when-cross-origin
,具体行为表现为:- 同源请求,
Referer
header中写入完整的URL - 跨域请求,
Referer
header中仅写入当前请求URL的origin - 请求non-potentially trustworthy URL,无
Referer
header
- 同源请求,
- 其他参数的具体行为参考W3C Spec Referrer Policies
- 默认值为
redirect
: 指定遇到重定向的行为follow
: 默认值,跟随重定向URLerror
: 如果遇到重定向,中断请求抛出Errormanual
body
: 请求体,数据类型必须和Content-Type
header 相匹配signal
: 一个AbortSignal
对象实例,用于和AbortController
通信中断请求
返回值 - Promise<Response>
Promise
-
fetch()
的返回值是一个Promise
-
不会reject HTTP 异常状态(HTTP code ≥ 300)
-
异常状态的
Response.ok
为false,并且Response.status
会给出具体的状态码。可以依据以上两个字段去做异常处理。 -
因此HTTP 异常状态不会被catch到,需要手动抛出Error才会被catch到
fetch('https://jsonplaceholder.typicode.com/posts/999') .then((response) => { console.log('response', response); if (!response.ok) { // 判断ok属性为false,然后手动抛出Error throw new Error(`status code: ${response.status}`); } }) .catch((err) => { console.log({ err }); // err: Error: status code: 404 });
-
-
只有遇到阻止request发出的Error才会reject
-
client side network failure 或者 request init options不规范会抛出
TypeError
fetch('https://jsonplaceholder.typicode.com/posts/999', { method: 'GET', body: new FormData(), // GET方法不能带body,因此request会被阻止,抛出TypeError }) .then((response) => { console.log('response', response); }) .catch((err) => { console.log({ err }); // err: TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body. });
-
通过
AbortController
中止一个请求的时候会抛出AbortError
-
具体可以参考MDN给出的Exceptions发生的原因
-
Response
- Response对象是对返回结果的一层封装
- 可以通过
ok
,status
,statusText
等字段获取返回结果的状态 - 真正从server返回的数据在
body
里,但是被封装成了ReadableStream
,需要调用原型链上的方法解析
Fetch接口
Headers
-
Headers
对象有多种创建方式// ✅ string object, 显示地指定key为string new Headers({ 'Content-type': 'application/json; charset=UTF-8' }); // ❌ 直接写成camelCased会多出一个无法识别的header,并且真正的content-type会被默认指定为‘text/plain’ new Headers({ ContentType: 'application/json; charset=UTF-8' }); // 也可以通过二维数组创建 const headers = [ ['Set-Cookie', 'greeting=hello'], ['Set-Cookie', 'name=world'] ]; const myHeaders = new Headers(headers);
Headers
类似于Map,也具有get()
,set()
,entries()
等方法Headers
具有Guard机制,即在特定类型的请求下,限制一些header names不能出现在Headers
里
Response
ok
: 标记这个请求是否成功(HTTP status code 属于200-209)status
andstatusText
:- 分别返回HTTP status code和其对应的status message,具体可以参考HTTP response status codes
- HTTP/2 不支持status message,会返回空字符串
headers
: 返回一个Headers
对象body
andbodyUsed
:-
body
的返回类型是一个ReadableStream
,需要调用它的reader去读取stream里面的数据/* Response.body 手动处理ReadableStream */ fetch('https://jsonplaceholder.typicode.com/posts/1') .then((response) => { console.log('response', response); return response.body; }) .then((body) => { const reader = body.getReader(); return reader.read(); }) .then((data) => { const { value } = data; const utf8Decoder = new TextDecoder('utf-8'); console.log('raw', data); console.log('parsed', JSON.parse(utf8Decoder.decode(data.value))); }) .catch((err) => console.log(err));
-
实际上不需要这么麻烦,
Response
原型链上提供了若干封装好的reader,调用它们可以直接返回解析对应数据的Promise
-
json()
,blob()
,formData()
,text()
,arrayBuffer()
/* parse image file by Response.blob() */ const myImage = document.querySelector('img'); const myRequest = new Request('flowers.jpg'); fetch(myRequest) .then((response) => { console.log('bodyUsedBefore', response.bodyUsed); // false const promise = response.blob(); console.log('bodyUsedAfter', response.bodyUsed); // true return promise }) .then((myBlob) => { const objectURL = URL.createObjectURL(myBlob); myImage.src = objectURL; });
-
ReadableStream
的处理是Fetch API的另一个话题,感兴趣可以阅读这篇来自网易云音乐技术团队的博客从 Fetch 到 Streams —— 以流的角度处理网络请求
-
-
bodyUsed
用于标记这个Response.body
的stream是否被读取过
-
redirected
:- 用于标记这个
Response
是否来自于重定向 - 但是MDN不建议通过该字段来过滤重定向,因为不够安全
- 更推荐的做法是在发起请求时配置
init options
:fetch(’url’, { redirect: 'error' });
- 用于标记这个
Request
fetch()
方法也接受一个Request对象作为参数。Request对象的构造器需要的参数和fetch()方法相同。
const initOptions = {
method: 'POST',
body: JSON.stringify({
title: 'Test',
body: 'Fetch API test',
userId: 233,
}),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
mode: 'same-origin',
credentials: 'omit',
referrerPolicy: 'no-referrer',
redirect: 'error',
};
// 常见调用
fetch('https://jsonplaceholder.typicode.com/posts/1', initOptions);
// 使用Request对象
const request = new Request('https://jsonplaceholder.typicode.com/posts/1', initOptions);
fetch(request);
console.log(request);
中断请求
如果需要中止fetch请求,可以使用 AbortController
接口。 AbortController
具体使用分为四步:
- 实例化一个
AbortController
对象 - 从
AbortController
对象的signal
属性上获取到一个AbortSignal
对象 - 在fetch方法的
init options
配置里携带这个AbortSignal
对象 - 使用
AbortController
对象的abort()
方法中止一个fetch请求
⚠️
abort()
不是中止了一次fetch请求,而至永久中止了携带该signal
的请求。如果仅需要中止一次,需要在下次请求的时候重新实例化一个AbortController
对象,重新携带新的signal
。
/* Abort a request */
// 获取AbortController对象
const abortController = new AbortController();
// 获取AbortSignal对象
const { signal } = abortController;
// 在配置中携带该signal
fetch('https://jsonplaceholder.typicode.com/posts/1', { signal })
.then((res) => {
console.log('res', res);
})
.catch((err) => {
// 会捕捉到一个AbortError
console.log('1st abort error:\n', err);
});
// 请求发出后立刻中止请求
abortController.abort();
// 携带已经被被abort的signal再次发送请求会失败
fetch('https://jsonplaceholder.typicode.com/posts/1', { signal })
.then((res) => {
console.log(res);
})
.catch((err) => {
// 请求失败,捕捉到异常
console.log('2st abort error:\n', err);
});
// 重新获取一个signal才能成功发送请求
fetch('https://jsonplaceholder.typicode.com/posts/1', {
signal: new AbortController().signal,
})
.then((res) => {
console.log('new res', res);
})
.catch((err) => {
// 请求成功,没有被捕捉到异常
console.log('3st abort error:\n', err);
});
其他值得一读的文章
转载自:https://juejin.cn/post/7133975279751413768