【js】Fetch API

概述

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

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

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

  • 当接收到一个代表错误的 HTTP 状态码时,从fetch()返回的 Promise 不会被标记为 reject, 即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
  • fetch() 不会接受跨域 cookies;你也不能使用 fetch() 建立起跨域会话。其他网站的 Set-Cookie 头部字段将会被无视。
  • fetch 不会发送 cookies。除非你使用了credentials 的初始化选项。(自 2017 年 8 月 25 日以后,默认的 credentials 政策变更为 same-origin。Firefox 也在 61.0b13 版本中进行了修改)

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

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();   //由于返回值不是真的json,而是json形式的文本,需要调用json()进行转换
  })
  .then(function(myJson) {
    console.log(myJson);
  });

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

当然它只是一个HTTP 响应,而不是真的JSON。为了获取JSON的内容,我们需要使用 json()方法json()方法在 Body mixin 接口中定义, Request 和 Response 对象实现该接口,因此也实现了该方法)。确切来说,此时只是获取了一个Json形式的文本,通过Body.json(),读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 JSON 格式的 Promise 对象。

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

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

支持的请求参数

语法

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

参数

  • 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 , 必须提供这个选项, 从 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=)。

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

参考 fetch(),查看所有可选的配置和更多描述。

POST请求为例:

// Example POST method implementation:

postData('http://example.com/answer', {answer: 42})
  .then(data => console.log(data)) //  JSON 是经 `response.json()` 转化后的结果
  .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()) // 转换 response 为 JSON
}

检测请求是否成功

如果是一般故障,比如遇到网络故障,fetch() promise 识别该故障,将会返回 reject(reject表示promise结果是错误的),并带上一个 TypeError 对象。但如果是权限问题或类似问题——比如 404 ,fetch() promise 不识别该故障,会返回resolved (resolved表示promise结果是成功的)。因此想要精确的判断 fetch() 是否成功,需要先判断是否为 promise resolved的情况,此时再判断 Response.ok 是不是为 true。

类似以下代码:

fetch('flowers.jpg').then(function(response) {
  if(response.ok) {              //需要进一步判断是否成功,避免404场景
    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 规范与 jQuery.ajax()区别时,其中有一点就是针对404等错误的处理,fetch此时返回结果状态是resolve ,但是ok 属性设置为 false

自定义请求对象

除了传给fetch() 一个资源的地址,你还可以通过使用 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 参数。

什么叫只能用一次?待续

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

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")); // [ ]

虽然一些操作只能在 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 是否正确,比如:

判断如果是json类型,才使用json转化的语法response.json()

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!");
  }
});

Response 对象

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

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

  • Response.status — 整数(默认值为200)为response的状态码。
  • Response.statusText — 字符串(默认值为"OK"),该值与 HTTP 状态码消息对应。
  • Response.ok — 如上所示,该属性是来检查response的状态是否在200 - 299(包括200 和 299)这个范围内。该属性返回一个布尔值。

Response继承了Body,下文的源码中可以看到,因此扩展了json()、blob()等方法。

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

var myBody = new Blob();

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

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

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

源码定义:

//lib.dom.d.ts
interface Body {
    readonly bodyUsed: boolean;
    arrayBuffer(): Promise<ArrayBuffer>;
    blob(): Promise<Blob>;
    formData(): Promise<FormData>;
    json(): Promise<any>;
    text(): Promise<string>;
}
interface Response extends Body {   //
    readonly body: ReadableStream | null;
    readonly headers: Headers;
    readonly ok: boolean;
    readonly redirected: boolean;
    readonly status: number;
    readonly statusText: string;
    readonly type: ResponseType;
    readonly url: string;
    clone(): Response;
}

Body

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

  • ArrayBuffer
  • ArrayBufferView (Uint8Array等)
  • Blob/File
  • string
  • URLSearchParams
  • FormData

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

  • arrayBuffer()
  • blob()
  • json()
  • text()
  • formData()

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值