使用Fetch

2018-01-23 10:11 更新

Fetch API提供了一個(gè) JavaScript 接口用于訪問(wèn)和操作HTTP管道的零件,如請(qǐng)求和響應(yīng)。它還提供了一種全局fetch()方法,可以提供一種簡(jiǎn)單,合理的方式在網(wǎng)絡(luò)上異步獲取資源。

此類功能以前是使用 XMLHttpRequest 實(shí)現(xiàn)的。Fetch提供了一個(gè)更好的替代方法,可以很容易地被其他技術(shù)使用,如Service Workers。Fetch還提供了一個(gè)單一的邏輯位置來(lái)定義其他HTTP相關(guān)的概念,如CORS和HTTP的擴(kuò)展。

請(qǐng)注意,F(xiàn)etch規(guī)格不同于jQuery.ajax(),主要體現(xiàn)在兩個(gè)方面:

  • 即使響應(yīng)是HTTP 404或500,從 fetch() 返回的Promise也不會(huì)拒絕HTTP錯(cuò)誤狀態(tài)。相反,它將正常解析(ok狀態(tài)設(shè)置為false),并且它只會(huì)在網(wǎng)絡(luò)故障時(shí)拒絕,或者如果任何東西阻止了請(qǐng)求的完成
  • 默認(rèn)情況下, 如果站點(diǎn)依靠維護(hù)用戶會(huì)話(發(fā)送cookie,必須設(shè)置credentials init選項(xiàng)),則fetch不會(huì)發(fā)送或接收來(lái)自服務(wù)器的任何cookie,從而導(dǎo)致未經(jīng)身份驗(yàn)證的請(qǐng)求。

Fetch請(qǐng)求

基本的Fetch請(qǐng)求非常容易設(shè)置??纯聪旅娴拇a:

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

在這里,我們通過(guò)網(wǎng)絡(luò)獲取圖像并將其插入到一個(gè)<img>元素中。最簡(jiǎn)單的用法是fetch()帶一個(gè)參數(shù) - 要獲取的資源的路徑 - 并返回一個(gè)包含響應(yīng)(Response對(duì)象)的promise。

這當(dāng)然只是一個(gè)HTTP響應(yīng),而不是實(shí)際的圖像。為了從響應(yīng)中提取圖像主體內(nèi)容,我們使用blob()方法(在Body mixin混合中定義,由Request對(duì)象和Response對(duì)象實(shí)現(xiàn))。

注意:Body mixin也有類似的方法來(lái)提取其他類型的Body內(nèi)容;請(qǐng)參閱正文部分了解更多信息。

一個(gè)objectURL接著從所提取的Blob創(chuàng)建,然后將其插入img。

獲取請(qǐng)求由內(nèi)容安全策略connect-src指令控制,而不是由它檢索的資源指令控制。

提供請(qǐng)求選項(xiàng)

該fetch()方法可以選擇性地接受第二個(gè)參數(shù),一個(gè) init 對(duì)象,允許你控制許多不同的設(shè)置:

有關(guān)可用選項(xiàng)的詳細(xì)說(shuō)明,請(qǐng)參閱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;
});

發(fā)送包含憑據(jù)的請(qǐng)求

要使瀏覽器發(fā)送包含憑據(jù)的請(qǐng)求,即使是跨源調(diào)用,請(qǐng)向傳遞給fetch()方法的init對(duì)象添加credentials: 'include':

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

如果您只想在請(qǐng)求URL與調(diào)用腳本位于相同的源時(shí)發(fā)送憑據(jù),請(qǐng)?zhí)砑?nbsp; credentials: 'same-origin'。

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

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

要改為確保瀏覽器不在請(qǐng)求中包含憑據(jù),請(qǐng)使用credentials: 'omit'。

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

上傳JSON數(shù)據(jù)

使用fetch()開(kāi)機(jī)自檢JSON編碼的數(shù)據(jù)。

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()來(lái)上載文件。

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是否成功

當(dāng)遇到網(wǎng)絡(luò)錯(cuò)誤或服務(wù)器端的 CORS 配置不正確時(shí),一個(gè)fetch()承諾將拒絕TypeError,盡管這通常意味著權(quán)限問(wèn)題或類似的情況 - 例如,404不構(gòu)成一個(gè)網(wǎng)絡(luò)錯(cuò)誤。一個(gè)成功的fetch()的準(zhǔn)確檢查將包括檢查承諾解決,然后檢查該Response.ok屬性的值為true。代碼看起來(lái)像這樣:

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

提供您自己的請(qǐng)求對(duì)象

您可以使用Request()構(gòu)造函數(shù)創(chuàng)建請(qǐng)求對(duì)象,并將其作為fetch()方法參數(shù)傳入,而不是將要請(qǐng)求的資源的路徑傳遞到 fetch () 調(diào)用中。

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()方法完全相同的參數(shù)。您甚至可以傳入現(xiàn)有的請(qǐng)求對(duì)象來(lái)創(chuàng)建它的副本:

var anotherRequest = new Request(myRequest, myInit);

這非常有用,因?yàn)檎?qǐng)求和響應(yīng)主體只是一個(gè)用途。制作這樣的副本可以讓您再次使用請(qǐng)求/響應(yīng),同時(shí)根據(jù)需要改變init選項(xiàng)。復(fù)制必須在閱讀正文之前完成,并且閱讀正文中的正文也將其標(biāo)記為原始請(qǐng)求中的正文。

注意:還有一種clone()方法可以創(chuàng)建副本。如果原始請(qǐng)求或響應(yīng)的主體已經(jīng)被讀取,則創(chuàng)建副本的兩種方法都將失敗,但是讀取克隆的響應(yīng)或請(qǐng)求的主體不會(huì)導(dǎo)致它在原始中被標(biāo)記為已讀。

Headers

該Headers接口允許您通過(guò)Headers()構(gòu)造函數(shù)創(chuàng)建自己的headers對(duì)象。headers對(duì)象是名稱到值的簡(jiǎn)單多重映射:

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

同樣可以通過(guò)傳遞一個(gè)數(shù)組或一個(gè)對(duì)象字面值給構(gòu)造函數(shù)來(lái)實(shí)現(xiàn):

myHeaders = new Headers({
  "Content-Type": "text/plain",
  "Content-Length": content.length.toString(),
  "X-Custom-Header": "ProcessThisImmediately",
});

內(nèi)容可以被查詢和檢索:

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 中很有用,但是它們提供了一個(gè)更好的API來(lái)處理headers。

如果使用的headers名不是有效的HTTP Header名稱稱,則所有的Headers方法都會(huì)拋出一個(gè)TypeError。如果有一個(gè)不變的guar,變異操作將會(huì)拋出一個(gè)TypeError(見(jiàn)下文)。否則,他們默默地失敗。例如:

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

headers的一個(gè)很好的用例是在進(jìn)一步處理之前檢查內(nèi)容類型是否正確。例如:

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可以在請(qǐng)求中發(fā)送,并在響應(yīng)中收到,并且對(duì)于哪些信息可以并且應(yīng)該是可變的有各種限制,headers對(duì)象具有g(shù)uard屬性。這不會(huì)暴露給Web,但會(huì)影響headers對(duì)象上允許使用的變異操作。

可能的guard值是:

  • none:默認(rèn)。
  • request:從請(qǐng)求(Request.headers)中獲得的headers對(duì)象的guard。
  • request-no-cors:從使用創(chuàng)建的請(qǐng)求獲取headers對(duì)象的guard。Request.mode no-cors
  • response:從響應(yīng)(Response.headers)獲得的headers的guard。
  • immutable:主要用于ServiceWorkers;呈現(xiàn)只讀headers對(duì)象。

注意:您不能追加或設(shè)置request保護(hù)的Headers的 Content-Length headers。同樣,Set-Cookie不允許插入響應(yīng)頭:ServiceWorkers不允許通過(guò)合成響應(yīng)來(lái)設(shè)置cookie。

Response對(duì)象

正如你上面看到的,當(dāng)fetch() promise被解析時(shí),Response實(shí)例被返回。

您將使用的最常見(jiàn)的響應(yīng)屬性是:

  • Response.status - 包含響應(yīng)狀態(tài)碼的整數(shù)(默認(rèn)值200)。
  • Response.statusText - 一個(gè)字符串(默認(rèn)值“OK”),對(duì)應(yīng)于HTTP狀態(tài)碼消息。
  • Response.ok - 在使用上面看,這是一個(gè)用于檢查狀態(tài)是否在200-299范圍內(nèi)的簡(jiǎn)寫(xiě)。這返回一個(gè)Boolean。

它們也可以通過(guò)JavaScript以編程方式創(chuàng)建,但在ServiceWorkers中,當(dāng)您使用respondWith()方法提供對(duì)接收到的請(qǐng)求的自定義響應(yīng)時(shí),這只會(huì)非常有用:

var myBody = new Blob();

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

所述Response()構(gòu)造函數(shù)有兩個(gè)可選的參數(shù)- 一個(gè)用于響應(yīng)的主體,和一個(gè)初始化對(duì)象(類似于接受的Request())

    注意:靜態(tài)方法error()只是返回錯(cuò)誤響應(yīng)。同樣,redirect()返回一個(gè)響應(yīng)導(dǎo)致重定向到指定的URL。這些也只與Service Workers有關(guān)。

    Body

    請(qǐng)求和響應(yīng)都可能包含body數(shù)據(jù)。一個(gè)body是以下任何一種類型的實(shí)例:

    • ArrayBuffer
    • ArrayBufferView (Uint8Array和擴(kuò)展)
    • Blob/File
    • string
    • URLSearchParams
    • FormData

    Body mixin定義了以下方法來(lái)提取體(由得到的Request和Response實(shí)施)。這些都會(huì)返回一個(gè)最終解決實(shí)際內(nèi)容的承諾。

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

    這使得使用非文本數(shù)據(jù)比使用XHR更容易。

    請(qǐng)求體可以通過(guò)傳遞身體參數(shù)來(lái)設(shè)置:

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

    請(qǐng)求和響應(yīng)(以及擴(kuò)展fetch()功能)都將嘗試智能地確定內(nèi)容類型。請(qǐng)求會(huì)自動(dòng)設(shè)置Content-Type header,如果字典中沒(méi)有設(shè)置。

    Feature檢測(cè)

    通過(guò)檢查Window或Worker范圍中的Headers,Request,Response或fetch()是否存在,可以檢測(cè)到Fetch API支持。例如:

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

    Polyfill

    要在不受支持的瀏覽器中使用Fetch,可以使用Fetch Polyfill,為不支持的瀏覽器重新創(chuàng)建功能。

    規(guī)范

    規(guī)范狀態(tài)評(píng)論
    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
    • 移動(dòng)端
    FeatureAndroid WebviewChrome for AndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
    基本的支持
    支持:42支持:42支持不支持?支持:10.1

    對(duì)應(yīng)的角標(biāo)解釋:

    [1]這個(gè)API是在首選項(xiàng)后面實(shí)現(xiàn)的。

    [2]在Firefox 52之前,get()只返回指定頭文件中的第一個(gè)值,getAll()返回所有的值。從52開(kāi)始,get()現(xiàn)在返回所有值getAll()并被刪除。

    以上內(nèi)容是否對(duì)您有幫助:
    在線筆記
    App下載
    App下載

    掃描二維碼

    下載編程獅App

    公眾號(hào)
    微信公眾號(hào)

    編程獅公眾號(hào)