/ JavaScript

Перехват запросов и ответов JavaScript Fetch API

Перехватчики - это блоки кода, которые вы можете использовать для предварительной или последующей обработки HTTP-вызовов, помогая в обработке глобальных ошибок, аутентификации, логирования, изменения тела запроса и многом другом. В этой статье вы узнаете, как перехватывать вызовы JavaScript Fetch API.

Есть два типа событий, для которых вы можете захотеть перехватить HTTP-вызовы: события запроса и события ответа. Перехватчик запроса должен быть выполнен до отправки фактического HTTP-запроса, в то время как перехватчик ответа должен быть выполнен до того, как он достигнет кода приложения, выполнившего вызов.

Прежде чем погрузиться в код, нам необходимо понять несколько важных факторов. Во-первых, Fetch API не поддерживает перехватчики по умолчанию. Кроме того, для использования Fetch API в Node.js требуются дополнительные пакеты.

JavaScript Fetch API

Сначала рассмотрим некоторые основы Fetch API, например, синтаксис:

const fetchResponsePromise = fetch(resource [, init])

resource определяет ресурс, к которому вы делаете запрос: это может быть либо объект Request, либо URL.
init - необязательный объект, который будет содержать любую пользовательскую конфигурацию, которую вы хотите применить к данному конкретному запросу.

Fetch API основан на промисах. Поэтому, когда вы вызываете метод Fetch, вы получаете промис ответа. Здесь оно обозначается как fetchResponsePromise, как показано в примере выше.

По умолчанию Fetch использует метод GET для вызовов API, как показано ниже:

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

Ниже приведен пример запроса POST с помощью Fetch:

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

Один из способов создания перехватчика для любой функции или метода JavaScript - это обезьянье исправление. Обезьянье исправление - это подход к переопределению оригинальной функциональности с помощью вашей версии функции.

Давайте рассмотрим шаг за шагом, как можно создать перехватчик для Fetch API с помощью обезьяньего патча:

const { fetch: originalFetch } = window;

window.fetch = async (...args) => {
    let [resource, config ] = args;
    // перехватчик запросов здесь
    const response = await originalFetch(resource, config);
    // перехватчик ответов здесь
    return response;
};

Приведенный выше код переопределяет исходный метод Fetch пользовательской реализацией и вызывает исходный метод Fetch внутри себя. Вы будете использовать этот шаблонный код для создания перехватчиков запросов и ответов.

Перехватчик запроса

В следующем примере мы создадим простой перехватчик запросов, который изменяет URL ресурса иллюстрации:

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

    // начало перехватчика запроса
    resource = 'https://jsonplaceholder.typicode.com/todos/2';
    // конец перехватчика запроса

    const response = await originalFetch(resource, config);

    // перехватчик ответа
    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, которое равно 2.

Примечание: одним из наиболее распространенных случаев использования перехватчиков запросов является изменение заголовков для аутентификации.

Перехватчик ответа

Перехватчик ответа будет перехватывать ответ API до того, как он будет доставлен вызывающему коду. Давайте посмотрим на следующий пример:

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

  let response = await originalFetch(resource, config);

  // перехватчик ответа
  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.ok и response.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 ошибки
    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.

Использование библиотеки fetch-intercept

Если вы не любите делать грязную работу (каламбур не удался), библиотека 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();

Метод register позволяет зарегистрировать перехватчики для вызовов Fetch API. Он принимает объект с колбеком request, requestError, response и responseError. Метод register возвращает другой метод, который можно использовать для отмены регистрации перехватчиков.

Fetch API не поддерживает перехватчики изначально. Однако существуют другие библиотеки для выполнения HTTP-вызовов, которые поддерживают перехватчики. Взгляните на Axios, которая предоставляет эту функциональность из коробки.

Резюме

В этой статье мы рассмотрели, что такое перехватчики JavaScript, научились создавать перехватчики как с помощью обезьяньего патчинга Fetch API, так и с помощью библиотеки fetch-intercept.

Перехватчики, изначально появившиеся в Angular, полезны в самых разных случаях, например, при обработке глобальных ошибок, аутентификации, протоколировании и т.п. Вы можете использовать методы, описанные в этой статье, для добавления перехватчиков в ваши JavaScript-приложения, однако помните о дополнительных необходимых зависимостях для Node.js.