使用Fetch

2018-01-23 10:11 更新

Fetch API提供了一个 JavaScript 接口用于访问和操作HTTP管道的零件,如请求和响应。它还提供了一种全局fetch()方法,可以提供一种简单,合理的方式在网络上异步获取资源。

此类功能以前是使用 XMLHttpRequest 实现的。Fetch提供了一个更好的替代方法,可以很容易地被其他技术使用,如Service Workers。Fetch还提供了一个单一的逻辑位置来定义其他HTTP相关的概念,如CORS和HTTP的扩展。

请注意,Fetch规格不同于jQuery.ajax(),主要体现在两个方面:

  • 即使响应是HTTP 404或500,从 fetch() 返回的Promise也不会拒绝HTTP错误状态。相反,它将正常解析(ok状态设置为false),并且它只会在网络故障时拒绝,或者如果任何东西阻止了请求的完成
  • 默认情况下, 如果站点依靠维护用户会话(发送cookie,必须设置credentials init选项),则fetch不会发送或接收来自服务器的任何cookie,从而导致未经身份验证的请求。

Fetch请求

基本的Fetch请求非常容易设置。看看下面的代码:

var myImage = document.querySelector('img');

fetch('flowers.jpg').then(function(response) {
  return response.blob();
}).then(function(myBlob) {
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
});

在这里,我们通过网络获取图像并将其插入到一个<img>元素中。最简单的用法是fetch()带一个参数 - 要获取的资源的路径 - 并返回一个包含响应(Response对象)的promise。

这当然只是一个HTTP响应,而不是实际的图像。为了从响应中提取图像主体内容,我们使用blob()方法(在Body mixin混合中定义,由Request对象和Response对象实现)。

注意:Body mixin也有类似的方法来提取其他类型的Body内容;请参阅正文部分了解更多信息。

一个objectURL接着从所提取的Blob创建,然后将其插入img。

获取请求由内容安全策略connect-src指令控制,而不是由它检索的资源指令控制。

提供请求选项

该fetch()方法可以选择性地接受第二个参数,一个 init 对象,允许你控制许多不同的设置:

有关可用选项的详细说明,请参阅fetch()。

var myHeaders = new Headers();

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

fetch('flowers.jpg', myInit).then(function(response) {
  return response.blob();
}).then(function(myBlob) {
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
});

发送包含凭据的请求

要使浏览器发送包含凭据的请求,即使是跨源调用,请向传递给fetch()方法的init对象添加credentials: 'include':

fetch('https://example.com', {
  credentials: 'include'  
})

如果您只想在请求URL与调用脚本位于相同的源时发送凭据,请添加  credentials: 'same-origin'。

// The calling script is on the origin 'https://example.com'

fetch('https://example.com', {
  credentials: 'same-origin'  
})

要改为确保浏览器不在请求中包含凭据,请使用credentials: 'omit'。

fetch('https://example.com', {
  credentials: 'omit'  
})

上传JSON数据

使用fetch()开机自检JSON编码的数据。

var url = 'https://example.com/profile';
var data = {username: 'example'};

fetch(url, {
  method: 'POST', // or 'PUT'
  body: JSON.stringify(data), 
  headers: new Headers({
    'Content-Type': 'application/json'
  })
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));

上传文件

可以使用 HTML <input type="file"/> input 元素、FormData () 和fetch()来上载文件。

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));

检查fetch是否成功

当遇到网络错误或服务器端的 CORS 配置不正确时,一个fetch()承诺将拒绝TypeError,尽管这通常意味着权限问题或类似的情况 - 例如,404不构成一个网络错误。一个成功的fetch()的准确检查将包括检查承诺解决,然后检查该Response.ok属性的值为true。代码看起来像这样:

fetch('flowers.jpg').then(function(response) {
  if(response.ok) {
    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);
});

提供您自己的请求对象

您可以使用Request()构造函数创建请求对象,并将其作为fetch()方法参数传入,而不是将要请求的资源的路径传递到 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()方法完全相同的参数。您甚至可以传入现有的请求对象来创建它的副本:

var anotherRequest = new Request(myRequest, myInit);

这非常有用,因为请求和响应主体只是一个用途。制作这样的副本可以让您再次使用请求/响应,同时根据需要改变init选项。复制必须在阅读正文之前完成,并且阅读正文中的正文也将其标记为原始请求中的正文。

注意:还有一种clone()方法可以创建副本。如果原始请求或响应的主体已经被读取,则创建副本的两种方法都将失败,但是读取克隆的响应或请求的主体不会导致它在原始中被标记为已读。

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.get("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]
 
myHeaders.delete("X-Custom-Header");
console.log(myHeaders.get("X-Custom-Header")); // [ ]

其中的一些操作只是在 ServiceWorkers 中很有用,但是它们提供了一个更好的API来处理headers。

如果使用的headers名不是有效的HTTP Header名称称,则所有的Headers方法都会抛出一个TypeError。如果有一个不变的guar,变异操作将会抛出一个TypeError(见下文)。否则,他们默默地失败。例如:

var myResponse = Response.error();
try {
  myResponse.headers.set("Origin", "http://mybank.com");
} catch(e) {
  console.log("Cannot pretend to be a bank!");
}

headers的一个很好的用例是在进一步处理之前检查内容类型是否正确。例如:

fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then(function(json) { /* process your JSON further */ })
  .catch(function(error) { console.log(error); });

Guard

由于headers可以在请求中发送,并在响应中收到,并且对于哪些信息可以并且应该是可变的有各种限制,headers对象具有guard属性。这不会暴露给Web,但会影响headers对象上允许使用的变异操作。

可能的guard值是:

  • none:默认。
  • request:从请求(Request.headers)中获得的headers对象的guard。
  • request-no-cors:从使用创建的请求获取headers对象的guard。Request.mode no-cors
  • response:从响应(Response.headers)获得的headers的guard。
  • immutable:主要用于ServiceWorkers;呈现只读headers对象。

注意:您不能追加或设置request保护的Headers的 Content-Length headers。同样,Set-Cookie不允许插入响应头:ServiceWorkers不允许通过合成响应来设置cookie。

Response对象

正如你上面看到的,当fetch() promise被解析时,Response实例被返回。

您将使用的最常见的响应属性是:

  • Response.status - 包含响应状态码的整数(默认值200)。
  • Response.statusText - 一个字符串(默认值“OK”),对应于HTTP状态码消息。
  • Response.ok - 在使用上面看,这是一个用于检查状态是否在200-299范围内的简写。这返回一个Boolean。

它们也可以通过JavaScript以编程方式创建,但在ServiceWorkers中,当您使用respondWith()方法提供对接收到的请求的自定义响应时,这只会非常有用:

var myBody = new Blob();

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

所述Response()构造函数有两个可选的参数- 一个用于响应的主体,和一个初始化对象(类似于接受的Request())

    注意:静态方法error()只是返回错误响应。同样,redirect()返回一个响应导致重定向到指定的URL。这些也只与Service Workers有关。

    Body

    请求和响应都可能包含body数据。一个body是以下任何一种类型的实例:

    • ArrayBuffer
    • ArrayBufferView (Uint8Array和扩展)
    • Blob/File
    • string
    • URLSearchParams
    • FormData

    Body mixin定义了以下方法来提取体(由得到的Request和Response实施)。这些都会返回一个最终解决实际内容的承诺。

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

    这使得使用非文本数据比使用XHR更容易。

    请求体可以通过传递身体参数来设置:

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

    请求和响应(以及扩展fetch()功能)都将尝试智能地确定内容类型。请求会自动设置Content-Type header,如果字典中没有设置。

    Feature检测

    通过检查Window或Worker范围中的Headers,Request,Response或fetch()是否存在,可以检测到Fetch API支持。例如:

    if (self.fetch) {
        // run my fetch request here
    } else {
        // do something with XMLHttpRequest?
    }

    Polyfill

    要在不受支持的浏览器中使用Fetch,可以使用Fetch Polyfill,为不支持的浏览器重新创建功能。

    规范

    规范状态评论
    Fetch
    Living Standard
    Initial definition

    浏览器兼容性

    • 电脑端
    Feature
    Chrome
    Edge
    Firefox(Gecko)Internet Explorer
    Opera
    Safari(WebKit)
    基本的支持支持:42支持:14
    支持:
    39(39)
    34(34)[1] 
    52(52)[2]
    不支持支持:29、28 [1]支持:10.1
    • 移动端
    FeatureAndroid WebviewChrome for AndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
    基本的支持
    支持:42支持:42支持不支持?支持:10.1

    对应的角标解释:

    [1]这个API是在首选项后面实现的。

    [2]在Firefox 52之前,get()只返回指定头文件中的第一个值,getAll()返回所有的值。从52开始,get()现在返回所有值getAll()并被删除。

    以上内容是否对您有帮助:
    在线笔记
    App下载
    App下载

    扫描二维码

    下载编程狮App

    公众号
    微信公众号

    编程狮公众号