Fetch API 全了解
Fetch API 提供了一个获取资源的接口(包括跨域请求)。
概念和用法
Fetch 提供了对 Request 和 Response (以及其他与网络请求有关的)对象的通用定义。使之今后可以被使用到更多地应用场景中:无论是 service worker、Cache API、又或者是其他处理请求和响应的方式,甚至是任何一种需要你自己在程序中生成响应的方式。
发送请求或者获取资源,需要使用 WindowOrWorkerGlobalScope.fetch() 方法。
它在很多接口中都被实现了,更具体地说,是在 Window 和 WorkerGlobalScope 接口上。因此在几乎所有环境中都可以用这个方法获取到资源。
fetch() 必须接受一个参数——资源的路径。无论请求成功与否,它都返回一个 Promise 对象,resolve 对应请求的 Response。你也可以传一个可选的第二个参数 init(参见 Request)。
一旦 Response 被返回,就可以使用一些方法来定义内容的形式,以及应当如何处理内容(参见 Body)。
你也可以通过 Request() 和 Response() 的构造函数直接创建请求和响应,但是我们不建议这么做。他们应该被用于创建其他 API 的结果(比如,service workers 中的 FetchEvent.respondWith)。
中止 fetch
浏览器已经开始为 AbortController 和 AbortSignal 接口(也就是Abort API)添加实验性支持,允许像 Fetch 和 XHR 这样的操作在还未完成时被中止 。
Fetch 接口
- WindowOrWorkerGlobalScope.fetch()
包含了fetch() 方法,用于获取资源。 - Headers
相当于 response/request 的头信息,可以使你查询到这些头信息,或者针对不同的结果做不同的操作。 - Request
相当于一个资源请求。 - Response
相当于请求的响应
Fetch mixin
Body
提供了与 response/request 中的 body 有关的方法,可以定义它的内容形式以及处理方式。
使用 Fetch
Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。
Fetch API提供了一个全局 fetch() 方法。
:zap: 一个基本的 fetch 请求设置起来很简单。看看下面的代码:
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
fetch语法
Promise<Response> fetch(input[, init]);
//类型定义
declare function fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
type RequestInfo = Request | string;
interface RequestInit {
body?: BodyInit | null;
cache?: RequestCache;
credentials?: RequestCredentials;
headers?: HeadersInit;
integrity?: string;
keepalive?: boolean;
method?: string;
mode?: RequestMode;
redirect?: RequestRedirect;
referrer?: string;
referrerPolicy?: ReferrerPolicy;
signal?: AbortSignal | null;
window?: null;
}
相关类型:
interface Body {
readonly body: ReadableStream<Uint8Array> | null;
readonly bodyUsed: boolean;
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
formData(): Promise<FormData>;
json(): Promise<any>;
text(): Promise<string>;
}
interface Request extends Body {
readonly cache: RequestCache;
readonly credentials: RequestCredentials;
readonly destination: RequestDestination;
readonly headers: Headers;
readonly integrity: string;
readonly keepalive: boolean;
/** Returns request's HTTP method, which is "GET" by default. */
readonly method: string;
readonly mode: RequestMode;
readonly redirect: RequestRedirect;
readonly referrer: string;
readonly referrerPolicy: ReferrerPolicy;
readonly signal: AbortSignal;
/** Returns the URL of request as a string. */
readonly url: string;
clone(): Request;
}
declare var Request: {
prototype: Request;
new(input: RequestInfo, init?: RequestInit): Request;
};
/** This Fetch API interface represents the response to a request. */
interface Response extends Body {
readonly headers: Headers;
readonly ok: boolean;
readonly redirected: boolean;
readonly status: number;
readonly statusText: string;
readonly type: ResponseType;
readonly url: string;
clone(): Response;
}
declare var Response: {
prototype: Response;
new(body?: BodyInit | null, init?: ResponseInit): Response;
error(): Response;
redirect(url: string | URL, status?: number): Response;
};
参数详解
?input
定义要获取的资源。这可能是:
init 可选
一个配置项对象,包括所有对请求的设置。可选的参数有:
method
: 请求使用的方法,如GET、POST。
headers
: 请求的头信息,形式为Headers
的对象或包含ByteString
值的对象字面量。body
: 请求的 body 信息:可能是一个Blob
、BufferSource
(en-US)、FormData
、URLSearchParams
或者USVString
对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。mode
: 请求的模式,如cors、
no-cors 或者
same-origin。
credentials
: 请求的 credentials,如omit、same-origin 或者
include
。为了在当前域名内自动发送 cookie , 必须提供这个选项, 从 Chrome 50 开始, 这个属性也可以接受FederatedCredential
(en-US) 实例或是一个PasswordCredential
(en-US) 实例。cache
: 请求的 cache 模式:default
、no-store
、reload
、no-cache
、force-cache
或者only-if-cached
。redirect
: 可用的 redirect 模式:follow
(自动重定向),error
(如果产生重定向将自动终止并且抛出一个错误), 或者manual
(手动处理重定向). 在Chrome中默认使用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=)。
返回值
一个 Promise
,resolve 时回传 Response
对象。
例外
类型 | 描述 |
---|---|
AbortError |
请求被AbortController.abort() 终止。 |
TypeError |
从Firefox 43开始,如果fetch() 接收到含有用户名和密码的URL(例如http://user:password@example.com ),它将会抛出一个TypeError 。 |
Fetch使用示例
请求参数
// Example POST method implementation:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
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',
},
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
});
return response.json(); // parses JSON response into native JavaScript objects
}
postData('https://example.com/answer', { answer: 42 })
.then(data => {
console.log(data); // JSON data parsed by `data.json()` call
});
上传json数据
即发送post请求
const data = { username: 'example' };
fetch('https://example.com/profile', {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
上传文件
可以通过 HTML <input type="file" />
元素,FormData()
和 fetch()
上传文件。
//上传单个文件
const formData = new FormData();
const 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())
.then(result => {
console.log('Success:', result);
})
.catch(error => {
console.error('Error:', error);
});
//上传多个文件
//可以通过 HTML <input type="file" multiple /> 元素,FormData() 和 fetch() 上传文件。
const formData = new FormData();
const photos = document.querySelector('input[type="file"][multiple]');
formData.append('title', 'My Vegas Vacation');
for (let i = 0; i < photos.files.length; i++) {
formData.append(`photos_${i}`, photos.files[i]);
}
fetch('https://example.com/posts', {
method: 'POST',
body: formData,
})
.then(response => response.json())
.then(result => {
console.log('Success:', result);
})
.catch(error => {
console.error('Error:', error);
});
逐行处理文本文件
从响应中读取的分块不是按行分割的,并且是 Uint8Array
数组类型(不是字符串类型)。如果你想通过 fetch()
获取一个文本文件并逐行处理它,那需要自行处理这些复杂情况。
以下示例展示了一种创建行迭代器来处理的方法(简单起见,假设文本是 UTF-8 编码的,且不处理 fetch()
的错误)。
async function* makeTextFileLineIterator(fileURL) {
const utf8Decoder = new TextDecoder('utf-8');
const response = await fetch(fileURL);
const reader = response.body.getReader();
let { value: chunk, done: readerDone } = await reader.read();
chunk = chunk ? utf8Decoder.decode(chunk) : '';
const re = /\n|\r|\r\n/gm;
let startIndex = 0;
let result;
for (;;) {
let result = re.exec(chunk);
if (!result) {
if (readerDone) {
break;
}
let remainder = chunk.substr(startIndex);
({ value: chunk, done: readerDone } = await reader.read());
chunk = remainder + (chunk ? utf8Decoder.decode(chunk) : '');
startIndex = re.lastIndex = 0;
continue;
}
yield chunk.substring(startIndex, result.index);
startIndex = re.lastIndex;
}
if (startIndex < chunk.length) {
// last line didn't end in a newline char
yield chunk.substr(startIndex);
}
}
async function run() {
for await (let line of makeTextFileLineIterator(urlOfFile)) {
processLine(line);
}
}
run();
检测请求是否成功
如果遇到网络故障或服务端的 CORS 配置错误时,fetch()
promise 将会 reject,带上一个 TypeError
对象。
虽然这个情况经常是遇到了权限问题或类似问题——比如 404 不是一个网络故障。
想要精确的判断 fetch()
是否成功,需要包含 promise resolved 的情况,此时再判断 Response.ok
是否为 true。
类似以下代码:
fetch('flowers.jpg')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not OK');
}
return response.blob();
})
.then(myBlob => {
myImage.src = URL.createObjectURL(myBlob);
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
});
HTTP 404 状态并不被认为是网络错误。
自定义请求对象
const myHeaders = new Headers();
const myRequest = new Request('flowers.jpg', {
method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default',
});
fetch(myRequest)
.then(response => response.blob())
.then(myBlob => {
myImage.src = URL.createObjectURL(myBlob);
});
Request()
和 fetch()
接受同样的参数。你甚至可以传入一个已存在的 request 对象来创造一个拷贝:
const anotherRequest = new Request(myRequest, myInit);
备注:
clone()
方法也可以用于创建一个拷贝。它和上述方法一样,如果 request 或 response 的 body 已经被读取过,那么将执行失败。区别在于,clone()
出的 body 被读取不会导致原 body 被标记为已读取。
Headers
Fetch API 的 Headers 接口允许您对HTTP请求和响应头执行各种操作。 这些操作包括检索,设置,添加和删除。
一个Headers对象具有关联的头列表,它最初为空,由零个或多个键值对组成。你可以使用 append()
方法添加 之类的方法添加到此
一个Headers对象也有一个关联的guard,它具有不可变的值,request
,request-no-cors
,response
或none
。 这会影响 set()
, delete()
, 和append()
方法 改变header.
可以通过 Request.headers
和Response.headers
属性检索一个Headers
对象, 并使用 Headers.Headers()
构造函数创建一个新的Headers
对象.
一个实现了Headers
的对象可以直接用于 for...of
结构中, 而不是 entries()
: for (var p of myHeaders)
等价于 for (var p of myHeaders.entries())
.
构造函数
创建一个新的Headers对象.
var myHeaders = new Headers(init);
init 可选
通过一个包含任意 HTTP headers 的对象来预设你的 Headers
. 可以是一个ByteString
对象; 或者是一个已存在的 Headers
对象.
Headers方法
给现有的header添加一个值, 或者添加一个未存在的header并赋值.
从Headers对象中删除指定header.
以 迭代器的形式返回Headers对象中所有的键值对.
以 ByteString
的形式从Headers对象中返回指定header的全部值.
以布尔值的形式从Headers对象中返回是否存在指定的header.
以迭代器
的形式返回Headers对象中所有存在的header名.
替换现有的header的值, 或者添加一个未存在的header并赋值.
以迭代器的形式返回Headers对象中所有存在的header的值.
Headers.set()
将会用新的值覆盖已存在的值, 但是Headers.append()
会将新的值添加到已存在的值的队列末尾
:zap: 示例
//构造一个空的Headers
let myHeaders = new Headers();
myHeaders.append('Content-Type', 'text/xml');
myHeaders.get('Content-Type');
// should return 'text/xml'
// 通过init构造
var httpHeaders = { 'Content-Type' : 'image/jpeg', 'Accept-Charset' : 'utf-8', 'X-My-Custom-Header' : 'Zeke are cool' };
var myHeaders = new Headers(httpHeaders);
//通过init属性将一个已存在的Headers对象来创建另一个新的Headers对象
var secondHeadersObj = new Headers(myHeaders);
secondHeadersObj.get('Content-Type');
// Would return 'image/jpeg' — it inherits it from the first headers object
Request 对象
Fetch API 的 Request接口?用来表示资源请求。
构造器
Request.Request()
创建一个新的 Request 对象。
var myRequest = new Request(input[, init]);
input
定义你想要fetch的资源。可以是下面两者之一:
- 一个直接包含你希望 fetch 的资源的 URL 的
USVString
。 - 一个
Request
对象。
init 可选
**一个可选对象,包含希望被包括到请求中的各种自定义选项。**可用的选项如下:
method
: 请求的方法,例如:GET
,POST。
headers
: 任何你想加到请求中的头,其被放在Headers
对象或内部值为ByteString
的对象字面量中。body
: 任何你想加到请求中的body,可以是Blob
,BufferSource
(en-US),FormData
,URLSearchParams
,USVString
,或ReadableStream
对象。注意GET
和HEAD
请求没有body。mode
: 请求的模式, 比如cors
,no-cors
,same-origin
, 或navigate
。默认值为cors
。credentials
: 想要在请求中使用的credentials::omit
,same-origin
, 或include
。默认值应该为omit
。但在Chrome中,Chrome 47 之前的版本默认值为same-origin
,自Chrome 47起,默认值为include。
cache
: 请求中想要使用的 cache moderedirect
: 对重定向处理的模式:follow
,error
, ormanual
。在Chrome中,Chrome 47 之前的版本默认值为manual
,自Chrome 47起,默认值为follow。
referrer
: 一个指定了no-referrer
,client
, 或一个 URL的USVString
。默认值是about:client
。integrity
: 包括请求的 subresource integrity 值 (e.g.,sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=
).
属性
-
包含请求的方法 (
GET
,POST
, 等.) -
Request.url
只读包含这个请求的URL。
-
包含请求相关的
Headers
对象。 -
包含请求的上下文(例如:
audio
,image
,iframe
, 等) -
?包含请求的来源 (例如:
client
)。 -
Request.referrerPolicy
(en-US) 只读?包含请求来源的策略 (例如:
no-referrer
)。 -
Request.mode
只读包含请求的模式 (例如:
cors
,no-cors
,same-origin
,navigate
). -
包含请求的证书(例如:
omit
,same-origin
). -
包含?如何处理重定向模式,它可能是一个
follow
,error
或者manual
。 -
包含请求的子资源的完整性值 (例如:
sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=
). -
包含请求的缓存模式 (例如:
default
,reload
,no-cache
).
Request
实现了Body
, 所以它还具有以下属性可用:
-
一个简单getter用于曝光一个
ReadableStream
的主体内容. -
存储一个
Boolean
(en-US)判断主体是否已经被用于一个响应中.
方法
-
创建当前request的副本。
Request
实现 Body
, 因此它也有以下方法可用:
-
返回解决一个
ArrayBuffer
表示的请求主体的promise. -
返回解决一个
Blob
表示的请求主体的promise. -
返回解决一个
FormData
表示的请求主体的promise. -
返回解决一个
JSON
表示的请求主体的promise. -
返回解决一个
USVString
(文本)表示的请求主体的promise.
注意:这些Body功能只能运行一次; 随后的调用将通过空strings/ ArrayBuffers解析.
:zap: 示例
// 使用url构造
const myRequest = new Request('http://localhost/flowers.jpg');
const myURL = myRequest.url; // http://localhost/flowers.jpg
const myMethod = myRequest.method; // GET
const myCred = myRequest.credentials; // omit
// 使用request
fetch(myRequest)
.then(response => response.blob())
.then(blob => {
myImage.src = URL.createObjectURL(blob);
});
// 传入两个参数
const myRequest = new Request('http://localhost/api', {method: 'POST', body: '{"foo":"bar"}'});
const myURL = myRequest.url; // http://localhost/api
const myMethod = myRequest.method; // POST
const myCred = myRequest.credentials; // omit
const bodyUsed = myRequest.bodyUsed;
Response 对象
Fetch API 的 Response 接口呈现了对一次请求的响应数据。
构造函数
let myResponse = new Response(body, init);
body 可选
一个定义 response 中 body 的对象. 可以为 null
,或是以下其中一个:
init 可选
一个参数(options)对象,包含要应用到 response 上的任何自定义设置. 可能参数(options)是:
status
: response 的状态码, 例如:200
.statusText
: 和状态码关联的状态消息, 例如:OK
.headers
: 你想加到 response 上的任何 headers, 包含了一个Headers
对象或满足对象语法的ByteString
key/value 对 (详见 HTTP headers).
例如:
var myBlob = new Blob();
var init = { "status" : 200 , "statusText" : "SuperSmashingGreat!" };
var myResponse = new Response(myBlob,init);
属性
-
包含此 Response 所关联的
Headers
对象。 -
Response.ok
只读包含了一个布尔值,标示该 Response 成功(HTTP 状态码的范围在 200-299)。
-
表示该 Response 是否来自一个重定向,如果是的话,它的 URL 列表将会有多个条目。
-
包含 Response 的状态码 (例如
200
表示成功)。 -
包含了与该 Response 状态码一致的状态信息(例如,OK对应
200
)。 -
包含 Response 的类型(例如,
basic
、cors
)。 -
Response.url
只读包含 Response 的URL。
-
Response.useFinalURL
包含了一个布尔值,来标示这是否是该 Response 的最终 URL。
Response
实现了 Body
接口,所以以下属性亦可用:
-
一个简单的 getter,用于暴露一个
ReadableStream
类型的 body 内容。 -
包含了一个
布尔值
(en-US)来标示该 Response 是否读取过Body
。
方法
-
创建一个
Response
对象的克隆。 -
返回一个绑定了网络错误的新的
Response
对象。 -
用另一个 URL 创建一个新的
Response
。
Response
实现了 Body
接口,所以以下方法同样可用:
-
读取
Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为ArrayBuffer
格式的 Promise 对象。 -
读取
Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为Blob
格式的 Promise 对象。 -
读取
Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为FormData
格式的 Promise 对象。 -
读取
Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为JSON
格式的 Promise 对象。 -
读取
Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为USVString
格式的 Promise 对象。
:zap: 示例
const image = document.querySelector('.my-image');
fetch('flowers.jpg').then(function(response) {
return response.blob();
}).then(function(blob) {
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
});
//Body.json() (en-US),reponse继承body
//json()返回一个被解析为 JSON 格式的 Promise 对象。
const myList = document.querySelector('ul');
const myRequest = new Request('products.json');
fetch(myRequest)
.then(response => response.json())
.then(data => {
for (const product of data.products) {
let listItem = document.createElement('li');
listItem.appendChild(
document.createElement('strong')
).textContent = product.Name;
listItem.append(
` can be found in ${
product.Location
}. Cost: `
);
listItem.appendChild(
document.createElement('strong')
).textContent = `£${product.Price}`;
myList.appendChild(listItem);
}
})
.catch(console.error);
Fetch支持特性检测
Fetch API 的支持情况,可以通过检测 Headers
, Request
, Response
或 fetch()
是否在 Window
或 Worker
域中来判断。例如:
if (window.fetch) {
// run my fetch request here
} else {
// do something with XMLHttpRequest?
}