JavaScript 中如何攔截全域性 Fetch API 的請求和響應?

我不吃餅乾呀發表於2023-01-05

本文翻譯自 Intercepting JavaScript Fetch API requests and responses

攔截器是可用於預處理或後處理 HTTP 請求的程式碼塊,有助於全域性錯誤處理、身份驗證、日誌記錄等。在本文中,你將學習如何攔截 JavaScript Fetch API 請求

攔截 HTTP 請求一般有兩種事件:請求和響應事件。請求攔截器應該在傳送實際 HTTP 請求之前執行,而響應攔截器應該在到達發出請求的應用程式程式碼之前執行。

在深入研究程式碼之前,我們需要了解一些重要的事情。首先,Fetch API 本身不支援攔截器。其次,在 Node.js 中使用 Fetch API 需要額外的包。

JavaScript Fetch API

首先,讓我們介紹一些 Fetch API 的基礎,例如語法:

const fetchResponsePromise = fetch(resource [, init])

resource 定義要獲取的資源,該資源可以是 Request 物件,也可以是 URL。init 是一個可選物件,它將包含你想應用於此特定請求的任何自定義配置。

Fetch API 是基於 Promise 的。因此,當你呼叫 Fetch 方法時,你將得到一個 Promise 響應。在這裡,它被稱為 fetchResponsePromise,如上面的示例所示。

預設情況下,Fetch 使用 GET 方法呼叫 API,如下所示:

fetch('https://jsonplaceholder.typicode.com/todos/1')
.then((response) => response.json())
.then((json) => console.log(json));

下面是一個使用 Fetch 傳送 POST 請求的示例:

fetch('https://jsonplaceholder.typicode.com/todos', {
  method: 'POST',
  body: JSON.stringify({
    completed: false,
    id: 1,
    title: 'New Todo',
    userId: 1,
  }),
  headers: new Headers({
    'Content-Type': 'application/json; charset=UTF-8',
  }),
})
.then((response) => response.json())
.then((json) => console.log(json));

POST 請求必須有 body。 檢視 Fetch 文件 瞭解更多詳細資訊。

實現攔截

有兩種方法可以在 Fetch API 請求時新增攔截器:使用猴子補丁或者使用庫 fetch-intercept

對 Fetch 使用猴子補丁(monkey patching)

為任何 JavaScript 函式或方法建立攔截器的一種方法是對其進行猴子修補。猴子補丁是一種用自己的函式版本覆蓋原始函式的方法。

讓我們一步一步地看看如何使用猴子補丁為 Fetch API 建立攔截器:

const { fetch: originalFetch } = window;

window.fetch = async (...args) => {
    let [resource, config ] = args;
    // request interceptor here
    const response = await originalFetch(resource, config);
    // response interceptor here
    return response;
};

上面的程式碼使用自定義實現重寫原始 Fetch 方法,並在其中呼叫原始 Fetch 方法。你可以使用這個樣例程式碼來建立請求和響應攔截器。

請求攔截器

在下面的示例中,我們將建立一個簡單的請求攔截器,用於更改一個請求示例的 URL:

const { fetch: originalFetch } = window;
window.fetch = async (...args) => {
    let [resource, config ] = args;

    // request interceptor starts
    resource = 'https://jsonplaceholder.typicode.com/todos/2';
    // request interceptor ends

    const response = await originalFetch(resource, config);

    // response interceptor here
    return response;
};


fetch('https://jsonplaceholder.typicode.com/todos/1')
.then((response) => response.json())
.then((json) => console.log(json));

// log
// {
//   "userId": 1,
//   "id": 2,
//   "title": "quis ut nam facilis et officia qui",
//   "completed": false
// }

這個 API 請求將從 https://jsonplaceholder.typicode.com/todos/2 獲取資料,而不是 https://jsonplaceholder.typicode.com/todos/1,並展示 ID 為 2todo 資料。

注意: 請求攔截器最常見的用例之一是更改身份驗證的 headers。

響應攔截器

響應攔截器將在 API 響應傳遞給實際呼叫者之前攔截它。讓我們看看下面的程式碼:

const { fetch: originalFetch } = window;
window.fetch = async (...args) => {
  let [resource, config] = args;

  let response = await originalFetch(resource, config);

  // response interceptor
  const json = () =>
    response
      .clone()
      .json()
      .then((data) => ({ ...data, title: `Intercepted: ${data.title}` }));

  response.json = json;
  return response;
};

fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then((response) => response.json())
  .then((json) => console.log(json));

// log
// {
//     "userId": 1,
//     "id": 1,
//     "title": "Intercepted: delectus aut autem",
//     "completed": false
// }

在上面的程式碼中,我們更改了 json 方法以返回一些自定義資料來替代原始資料。檢視文件瞭解更多 可以更改的屬性

注意: response 只允許使用一次。因此,每次需要使用 response 時,都需要 克隆 response

錯誤處理

透過檢查 response.okresponse.status 的值,可以很容易地處理請求的錯誤。在下面的程式碼片段中,可以攔截 404 錯誤

const { fetch: originalFetch } = window;
window.fetch = async (...args) => {
  let [resource, config] = args;
  let response = await originalFetch(resource, config);
  if (!response.ok && response.status === 404) {
    // 404 error handling
    return Promise.reject(response);
  }
  return response;
};
fetch('https://jsonplaceholder.typicode.com/todos/1000000')
  .then((response) => response.json())
  .then((json) => console.log(json))
  .catch((error) => console.error(error));

Node.js

你可以在 Node.js 中使用相同的方法。然而,Node.js 原生不支援 Fetch API (儘管對 Fetch API 的原生支援將在 Node.js 的未來版本中提供)。現在,你需要安裝 Node Fetch 包,然後對 fetch 方法使用猴子補丁。【譯:Node18已經支援了!】

使用 fetch-intercept 庫

如果你不喜歡做這些 dirty 的事情(雙關語【譯:我也不懂指什麼?】) ,那麼 fetch-intercept 庫允許您使用更乾淨的 API 註冊攔截器。您可以使用 npm 或 Yarn 來安裝這個庫,如下所示:

npm install fetch-intercept whatwg-fetch --save
// or
yarn install fetch-intercept whatwg-fetch

注意: fetch-intercept 只支援瀏覽器,不能在 Node.js 中工作。因此,它還需要使用 whatwg-fetch 作為依賴項。

透過下面的程式碼,我們可以實現與我們的猴子補丁示例相同的請求和響應攔截器:

import * as fetchIntercept from 'fetch-intercept';

const unregister = fetchIntercept.register({
  request: function (url, config) {
    const modifiedUrl = `https://jsonplaceholder.typicode.com/todos/2`;
    return [modifiedUrl, config];
  },

  requestError: function (error) {
    return Promise.reject(error);
  },

  response: function (response) {
    const clonedResponse = response.clone();
    const json = () =>
      clonedResponse
        .json()
        .then((data) => ({ ...data, title: `Intercepted: ${data.title}` }));

    response.json = json;
    return response;
  },

  responseError: function (error) {
    return Promise.reject(error);
  },
});

fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then((response) => response.json())
  .then((json) => console.log(json));

// unregister interceptors
unregister();

register 方法允許你為 Fetch API 請求註冊攔截器。它接受一個帶有 requestrequestErrorresponse, 和 responseError 回撥的物件。register 方法返回另一個可用於登出攔截器的方法。

Fetch API 本身不支援攔截器。但是,還有其他支援攔截器的 HTTP 請求庫。看一下 Axios,它提供了開箱即用的功能。

總結

在本文中,我們介紹了什麼是 JavaScript 攔截器,學習瞭如何透過給 Fetch API 使用猴子補丁和使用 fetch-intercept 庫來建立攔截器。

攔截器最初由 Angular 引入,對於各種各樣的用例都很有幫助,比如幫助處理全域性錯誤、身份驗證、日誌記錄等等。你可以使用本文描述的方法將攔截器新增到 JavaScript 應用程式中,但是,請記住在 Node.js 需要新增額外的依賴。

我希望你喜歡這篇文章,如果有任何問題一定要留下評論。Happy coding!

相關文章