Fetch

680 阅读7分钟

Fetch

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

一、概念

Fetch 提供了对 RequestResponse 对象的通用定义。使之今后可以被使用到更多地应用场景中:无论是 service workerCache API、又或者是其他处理请求和响应的方式,甚至是任何一种需要你自己在程序中生成响应的方式。

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

二、语法

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

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

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

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

  • method: 请求使用的方法,如 GETPOST
  • headers: 请求的头信息,形式为 Headers 的对象或包含
  • ByteString 值的对象字面量。 body: 请求的 body 信息:可能是一个 BlobBufferSourceFormDataURLSearchParams 或者 USVString 对象。注意 GETHEAD 方法的请求不能包含 body 信息。
  • mode: 请求的模式,如 corsno-cors 或者 same-origin
  • credentials: 请求的 credentials,如 omitsame-origin 或者 include为了在当前域名内自动发送 cookie , 必须提供这个选项,也可以接受 FederatedCredential 实例或是一个 PasswordCredential 实例。
  • cache: 请求的 cache 模式: defaultno-storereloadno-cacheforce-cache 或者 only-if-cached
  • redirect: 可用的 redirect 模式: follow (自动重定向), error (如果产生重定向将自动终止并且抛出一个错误), 或者 manual (手动处理重定向). 默认值是 manual
  • referrer: 一个 USVString 可以是 no-referrerclient或一个 URL。默认是 client
  • referrerPolicy: 指定了HTTP头部referer字段的值。可能为以下值之一: no-referrerno-referrer-when-downgradeoriginorigin-when-cross-originunsafe-url
  • integrity: 包括请求的 subresource integrity 值 ( 例如: sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=)。
2、返回值

一个 Promiseresolve 时回传 Response 对象。

三、Fetch和Ajax的不同

  1. 当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject, 即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve 但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject
  2. fetch() 可以接受跨域 cookies;你也可以使用 fetch() 建立起跨域会话。
  3. fetch 不会默认发送 cookies。除非你使用了credentials 的初始化选项。credentials 值为 same-origin。

四、请求示例

示例
fetch('https://api.github.com/users/chriscoyier/repos').then(res => {
	console.log(res)
}).catch((e) => {
	console.log(e)
})
打印的日志
body: ReadableStream
bodyUsed: true
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "https://api.github.com/users/chriscoyier/repos"
__proto__: Response
转化(Body mixin)
属性

Body.body 一个简单的getter用于暴露一个ReadableStream类型的主体内容。 Body.bodyUsed 一个Boolean 值指示是否body已经被标记读取。

方法

Body.arrayBuffer() 使Response挂起一个流操作并且在完成时读取其值,它返回一个Promise对象,其resolve参数类型是ArrayBuffer。此操作会将bodyUsed状态改为已使用(true)

Body.blob() 使Response挂起一个流操作并且在完成时读取其值,它返回一个Promise对象,其resolve参数类型是Blob。此操作会将bodyUsed状态改为已使用(true)

Body.formData() 使Response挂起一个流操作并且在完成时读取其值,它返回一个Promise对象,其resolve参数类型是FormData表单。此操作会将bodyUsed状态改为已使用(true)

Body.json() 使Response挂起一个流操作并且在完成时读取其值,它返回一个Promise对象,其resolve参数类型是使用JSON解析body文本的结果。此操作会将bodyUsed状态改为已使用(true)

Body.text() 使Response挂起一个流操作并且在完成时读取其值,它返回一个Promise对象,其resolve参数类型是USVString(文本)。此操作会将bodyUsed状态改为已使用(true)

完整示例

fetch('https://api.github.com/users/chriscoyier/repos').then(res => {
	console.log('res', res)
	console.log('res.body', res.body)
	console.log('res.bodyUsed', res.bodyUsed)
	res.json().then(_res => {
		console.log('res.bodyUsed', res.bodyUsed)
		console.log('_res', _res)
	}).catch(e => {
		console.log(e)
	})
}).catch((e) => {
	console.log(e)
})

打印日志

'res' 
	body: (...)
	bodyUsed: true
	headers: Headers {}
	ok: true
	redirected: false
	status: 200
	statusText: "OK"
	type: "cors"
	url: "https://api.github.com/users/chriscoyier/repos"

'res.body' ReadableStream {locked: false}

'res.bodyUsed' false

'res.bodyUsed' true

//res.json()解析之后的值
'_res' (30) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
async/await
(async () => {
	let res = await fetch('https://api.github.com/users/chriscoyier/repos')
	console.log(res)
})();
示例
1、携带cookie

credentials

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

fetch(url, {
  	credentials: 'include'  
})

如果你只想在请求URL与调用脚本位于同一起源处时发送凭据,请添加 credentials: 'same-origin'。 要改为确保浏览器不在请求中包含凭据,请使用 credentials: 'omit'

fetch(url, {
  	credentials: 'same-origin' //credentials: 'omit' 
})
2、上传JSON数据

'Content-Type': 'application/json'

let url = 'https://xxx';
let data = { username: 'example' };

fetch(url, {
    method: 'POST', 
    body: JSON.stringify(data),
    headers: new Headers({
        'Content-Type': 'application/json'
    })
}).then(res => res.json())
.catch(error => console.error('Error:', error));
3、上传文件

FormData()

let formData = new FormData();
let fileField = document.querySelector("input[type='file']");

formData.append('name', 'abc123');
formData.append('pic', fileField.files[0]);

fetch(url, {
    method: 'PUT',
    body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error));
4、上传多个文件

<input type="file" mutiple/>

let formData = new FormData();
let 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(url, {
    method: 'POST',
    body: formData
})
.then(response => response.json())
.then(response => console.log('Success:', JSON.stringify(response)));
5、自定义请求对象

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

let myHeaders = new Headers();

let myInit = {
    method: 'GET',
    headers: myHeaders,
    mode: 'cors',
    cache: 'default'
};

let myRequest = new Request('flowers.jpg', myInit);

fetch(myRequest).then((response) => {
}).then((myBlob) => {});

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

var anotherRequest = new Request(myRequest,myInit);

五、Headers

可以通过 Headers() 构造函数来创建一个你自己的headers 对象。一个 headers 对象是一个简单的多名值对。

let content = "Hello World";
let 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",
});
Guard属性

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

  • none:默认的
  • request:从 request 中获得的 headers(Request.headers)只读
  • request-no-cors:从不同域(Request.mode no-cors)的request 中获得的 headers 只读
  • response:从 response 中获得的 headers(Response.headers)只读
  • immutable:在 ServiceWorkers 中最常用的,所有的 headers 都只读。

六、Response 对象

Response 实例是在 fetch() 处理完 promise 之后返回的。

属性
  • Response.headers 只读 包含此 Response 所关联的 Headers 对象。

  • Response.ok 只读 包含了一个布尔值,标示该 Response 成功(HTTP 状态码的范围在 200-299)。

  • Response.redirected 只读 表示该 Response 是否来自一个重定向,如果是的话,它的 URL 列表将会有多个条目。

  • Response.status 只读 包含 Response 的状态码 (例如 200 表示成功)。

  • Response.statusText 只读 包含了与该 Response 状态码一致的状态信息(例如,OK对应 200)。

  • Response.type 只读 包含 Response 的类型(例如,basic、cors)。

  • Response.url 只读 包含 Response 的URL。

  • Response.useFinalURL 包含了一个布尔值,来标示这是否是该 Response 的最终 URL。 Response 实现了 Body 接口,所以以下属性亦可用:

  • Body.body 只读 一个简单的 getter,用于暴露一个 ReadableStream 类型的 body 内容。

  • Body.bodyUsed 只读 包含了一个布尔值来标示该 Response 是否读取过 Body。

方法
  • Response.clone() 创建一个 Response 对象的克隆。
  • Response.error() 返回一个绑定了网络错误的新的 Response 对象。
  • Response.redirect() 用另一个 URL 创建一个新的 Response。 Response 实现了 Body 接口,所以Body的方法都可以用