Fetch() 簡介

Matt Gaunt

fetch() 可讓您發出類似 XMLHttpRequest (XHR) 的網路要求。主要差異在於 Fetch API 使用 Promise,後者提供更簡單的 API,可協助您避免使用 XMLHttpRequest API 中的複雜回呼。

瀏覽器支援

  • Chrome:42。
  • Edge:14。
  • Firefox:39。
  • Safari:10.1。

資料來源

如果您從未使用過 Promise,請參閱 JavaScript Promise 簡介

基本擷取要求

以下是使用 XMLHttpRequestfetch 實作的範例。我們要要求網址、取得回應,並將其剖析為 JSON。

XMLHttpRequest

XMLHttpRequest 需要兩個事件監聽器來處理成功和錯誤案例,以及對 open()send() 的呼叫。MDN 文件中的範例

function reqListener () {
  const data = JSON.parse(this.responseText);
  console.log(data);
}

function reqError (err) {
  console.log('Fetch Error :-S', err);
}

const oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();

擷取

我們的擷取要求如下所示:

fetch('./api/some.json')
  .then(response => {
    if (response.status !== 200) {
      console.log(`Looks like there was a problem. Status Code: ${response.status}`);

      return;
    }

    // Examine the text in the response
    response.json().then(function(data) {
      console.log(data);
    });
  })
  .catch(err => {
    console.log('Fetch Error :-S', err);
  });

fetch() 要求只需要一個呼叫,即可執行與 XHR 範例相同的工作。為了處理回應,我們會先檢查回應狀態是否為 200,然後將回應解析為 JSON。fetch() 要求的回應是 Stream 物件,也就是說,在呼叫 json() 方法後,系統會傳回 Promise。串流會以非同步方式發生。

回應中繼資料

上一個範例顯示了 Response 物件的狀態,以及如何將回應解析為 JSON。以下說明如何處理您可能要存取的其他中繼資料,例如標頭:

fetch('users.json').then(response => {
  console.log(response.headers.get('Content-Type'));
  console.log(response.headers.get('Date'));

  console.log(response.status);
  console.log(response.statusText);
  console.log(response.type);
  console.log(response.url);
});

回應類型

當我們提出擷取要求時,回應會收到「basic」、「cors」或「opaque」的 response.type。這些 types 會顯示資源的來源,您可以使用這些資訊來決定如何處理回應物件。

當瀏覽器要求相同來源的資源時,回應會具有 basic 類型,並限制您可以從回應中查看的內容。

如果要求是針對其他來源的資源,且該來源傳回 CORs 標頭,則類型為 corscors 回應類似於 basic 回應,但可查看的標頭會受到限制,僅限於 Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma

opaque 回應來自不同的來源,且不會傳回 CORS 標頭。由於回應內容不透明,我們無法讀取傳回的資料或查看要求狀態,也就是說,您無法檢查要求是否成功。

您可以為擷取要求定義模式,讓系統只解析特定要求類型。您可以設定的模式如下:

  • same-origin 只會針對同一個來源的素材資源要求成功,並拒絕所有其他要求。
  • cors 允許針對同源和其他來源的資產提出要求,並傳回適當的 CORS 標頭。
  • cors-with-forced-preflight 會在提出任何要求前執行預先檢查
  • no-cors 旨在向沒有 CORS 標頭的其他來源提出要求,並產生 opaque 回應,但如前所述,目前無法在視窗全域範圍中執行這項操作。

如要定義模式,請在 fetch 要求中新增選項物件做為第二個參數,並在該物件中定義模式:

fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
  .then(response => response.text())
  .then(text => {
    console.log('Request successful', text);
  })
  .catch(error => {
    log('Request failed', error)
  });

承諾鏈結

Promise 的一大優點是可以鏈結在一起。對於 fetch(),這可讓您在擷取要求之間共用邏輯。

如果您使用的是 JSON API,則需要檢查狀態,並剖析每個回應的 JSON。您可以透過在傳回承諾的個別函式中定義狀態和 JSON 剖析,並使用擷取要求只處理最終資料和錯誤情況,藉此簡化程式碼。

function status (response) {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  } else {
    return Promise.reject(new Error(response.statusText))
  }
}

function json (response) {
  return response.json()
}

fetch('users.json')
  .then(status)
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  }).catch(error => {
    console.log('Request failed', error);
  });

這個範例定義了 status 函式,用於檢查 response.status,並傳回已解析的 Promise 做為 Promise.resolve(),或傳回已拒絕的 Promise 做為 Promise.reject()。這是 fetch() 鏈結中第一個呼叫的方法。

如果 Promise 已解析,則指令碼會呼叫 json() 方法,該方法會從 response.json() 呼叫傳回第二個 Promise,並建立包含已剖析 JSON 的物件。如果剖析失敗,承諾會遭到拒絕,並執行 catch 陳述式。

這個結構可讓您在所有擷取要求中共用邏輯,讓程式碼更容易維護、閱讀和測試。

POST 要求

有時,網路應用程式需要使用 POST 方法呼叫 API,並在要求主體中加入一些參數。如要這麼做,請在 fetch() 選項中設定 methodbody 參數:

fetch(url, {
    method: 'post',
    headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
  })
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(error => {
    console.log('Request failed', error);
  });

透過擷取要求傳送憑證

如要使用 cookie 等憑證提出擷取要求,請將要求的 credentials 值設為 "include"

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