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 需要兩個事件監聽器來處理成功和錯誤案例,以及對 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);
});

回應類型

提出擷取要求時,系統會將 response.type 的回應提供給「basic」、「cors」或「opaque」。這些 types 會顯示資源的來源,您可以使用這些事件判斷回應物件的處理方式。

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

如果對另一個來源的資源發出要求,且該來源傳回 CORs 標頭,則類型為 corscors 回應和 basic 回應類似,但會將您檢視的標頭限制為 Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma

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

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

  • same-origin 只會針對相同來源的資產要求成功,而拒絕所有其他要求。
  • cors 允許針對相同來源及其他來源的資產提出要求,這些來源會傳回適當 COR 標頭。
  • 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'
})