Skip to content

Latest commit

 

History

History
223 lines (158 loc) · 17.7 KB

File metadata and controls

223 lines (158 loc) · 17.7 KB

Fetch API

На данный момент мы уже многое знаем про fetch.

Давайте рассмотрим оставшуюся часть API, чтобы охватить все возможности.

Заметим: большинство этих возможностей используются редко. Вы можете пропустить эту главу и, несмотря на это, нормально использовать `fetch`.

Тем не менее, полезно знать, что вообще может `fetch`, чтобы, когда появится необходимость, вернуться и прочитать конкретные детали.

Нижеследующий список - это все возможные опции для fetch с соответствующими значениями по умолчанию (в комментариях указаны альтернативные значения):

let promise = fetch(url, {
  method: "GET", // POST, PUT, DELETE, etc.
  headers: {
    // значение этого заголовка обычно ставится автоматически,
    // в зависимости от тела запроса
    "Content-Type": "text/plain;charset=UTF-8"
  },
  body: undefined, // string, FormData, Blob, BufferSource или URLSearchParams
  referrer: "about:client", // или "" для того, чтобы не послать заголовок Referer,
  // или URL с текущего источника
  referrerPolicy: "strict-origin-when-cross-origin", // no-referrer-when-downgrade, no-referrer, origin, same-origin...
  mode: "cors", // same-origin, no-cors
  credentials: "same-origin", // omit, include
  cache: "default", // no-store, reload, no-cache, force-cache или only-if-cached
  redirect: "follow", // manual, error
  integrity: "", // контрольная сумма, например "sha256-abcdef1234567890"
  keepalive: false, // true
  signal: undefined, // AbortController, чтобы прервать запрос
  window: window // null
});

Довольно-таки внушительный список, не так ли?

В главе info:fetch мы разобрали параметры method, headers и body.

Опция signal разъяснена в главе в info:fetch-abort.

Теперь давайте пройдёмся по оставшимся возможностям.

referrer, referrerPolicy

Данные опции определяют, как fetch устанавливает HTTP-заголовок Referer.

Обычно этот заголовок ставится автоматически и содержит URL-адрес страницы, с которой пришёл запрос. В большинстве случаев он совсем неважен, в некоторых случаях, с целью большей безопасности, имеет смысл убрать или укоротить его.

Опция referrer позволяет установить любой Referer в пределах текущего источника или же убрать его.

Чтобы не отправлять Referer, нужно указать значением пустую строку:

fetch('/page', {
*!*
  referrer: "" // не ставить заголовок Referer
*/!*
});

Для того, чтобы установить другой URL-адрес (должен быть с текущего источника):

fetch('/page', {
  // предположим, что мы находимся на странице https://javascript.info
  // мы можем установить любое значение Referer при условии, что оно принадлежит текущему источнику
*!*
  referrer: "https://javascript.info/anotherpage"
*/!*
});

Опция referrerPolicy устанавливает общие правила для Referer.

Выделяется 3 типа запросов:

  1. Запрос на тот же источник.
  2. Запрос на другой источник.
  3. Запрос с HTTPS to HTTP (с безопасного протокола на небезопасный).

В отличие от настройки referrer, которая позволяет задать точное значение Referer, настройка referrerPolicy сообщает браузеру общие правила, что делать для каждого типа запроса.

Возможные значения описаны в спецификации Referrer Policy:

  • "strict-origin-when-cross-origin" -- значение по умолчанию: для "same-origin" отправлять полный Referer, для "cross-origin" отправлять только "origin", если только это не HTTPS→HTTP запрос, тогда не отправлять ничего.
  • "no-referrer-when-downgrade" -- всегда отправлять полный Referer, за исключением случаев, когда мы отправляем запрос с HTTPS на HTTP (на менее безопасный протокол).
  • "no-referrer" -- никогда не отправлять Referer.
  • "origin" -- отправлять в Referer только текущий источник, а не полный URL-адрес страницы, например, посылать только http://site.com вместо http://site.com/path.
  • "origin-when-cross-origin" -- отправлять полный Referer для запросов в пределах текущего источника, но для запросов на другой источник отправлять только сам источник (как выше).
  • "same-origin" -- отправлять полный Referer для запросов в пределах текущего источника, а для запросов на другой источник не отправлять его вообще.
  • "strict-origin" -- отправлять только значение источника, не отправлять Referer для HTTPS→HTTP запросов.
  • "unsafe-url" -- всегда отправлять полный URL-адрес в Referer, даже при запросах HTTPS→HTTP.

Вот таблица со всеми комбинациями:

Значение На тот же источник На другой источник HTTPS→HTTP
"no-referrer" - - -
"no-referrer-when-downgrade" full full -
"origin" origin origin origin
"origin-when-cross-origin" full origin origin
"same-origin" full - -
"strict-origin" origin origin -
"strict-origin-when-cross-origin" или "" (по умолчанию) full origin -
"unsafe-url" full full full

Допустим, у нас есть админка со структурой URL, которая не должна стать известной снаружи сайта.

Если мы отправляем запрос fetch, то по умолчанию он всегда отправляет заголовок Referer с полным URL-адресом нашей админки (исключение - это когда мы делаем запрос от HTTPS в HTTP, в таком случае Referer не будет отправляться).

Например, Referer: https://javascript.info/admin/secret/paths.

Если мы хотим, чтобы другие сайты получали только источник, но не URL-путь, это сделает такая настройка:

fetch('https://another.com/page', {
  // ...
  referrerPolicy: "origin-when-cross-origin" // Referer: https://javascript.info
});

Мы можем поставить её во все вызовы fetch, возможно, интегрировать в JavaScript-библиотеку нашего проекта, которая делает все запросы и внутри использует fetch.

Единственным отличием в поведении будет то, что для всех запросов на другой источник fetch будет посылать только источник в заголовке Referer (например, https://javascript.info, без пути). А для запросов на наш источник мы продолжим получать полный Referer (это может быть полезно для отладки).

```smart header="Политика установки Referer (Referrer Policy) - не только для fetch" Политика установки Referer, описанная в спецификации Referrer Policy, существует не только для `fetch`, она более глобальная.

В частности, можно поставить политику по умолчанию для всей страницы, используя HTTP-заголовок Referrer-Policy, или на уровне ссылки <a rel="noreferrer">.


## mode

Опция `mode` - это защита от нечаянной отправки запроса на другой источник:

- **`"cors"`** -- стоит по умолчанию, позволяет делать такие запросы так, как описано в <info:fetch-crossorigin>,
- **`"same-origin"`** -- запросы на другой источник запрещены,
- **`"no-cors"`** -- разрешены только простые запросы на другой источник.

Эта опция может пригодиться, если URL-адрес для `fetch` приходит от третьей стороны, и нам нужен своего рода "глобальный выключатель" для запросов на другие источники.

## credentials

Опция `credentials` указывает, должен ли `fetch` отправлять куки и авторизационные заголовки HTTP вместе с запросом.

- **`"same-origin"`** -- стоит по умолчанию, не отправлять для запросов на другой источник,
- **`"include"`** -- отправлять всегда, но при этом необходим заголовок `Access-Control-Allow-Credentials` в ответе от сервера, чтобы JavaScript получил доступ к ответу сервера, об этом говорилось в главе <info:fetch-crossorigin>,
- **`"omit"`** -- не отправлять ни при каких обстоятельствах, даже для запросов, сделанных в пределах текущего источника.

## cache

По умолчанию `fetch` делает запросы, используя стандартное HTTP-кеширование. То есть учитываются заголовки `Expires`, `Cache-Control`, отправляется `If-Modified-Since` и так далее. Так же, как и обычные HTTP-запросы.

Настройка `cache` позволяет игнорировать HTTP-кеш или же настроить его использование:

- **`"default"`** -- `fetch` будет использовать стандартные правила и заголовки HTTP кеширования,
- **`"no-store"`** -- полностью игнорировать HTTP-кеш, этот режим становится режимом по умолчанию, если присутствуют такие заголовки как `If-Modified-Since`, `If-None-Match`, `If-Unmodified-Since`, `If-Match`, или `If-Range`,
- **`"reload"`** -- не брать результат из HTTP-кеша (даже при его присутствии), но сохранить ответ в кеше (если это дозволено заголовками ответа);
- **`"no-cache"`** -- в случае, если существует кешированный ответ - создать условный запрос, в противном же случае - обычный запрос. Сохранить ответ в HTTP-кеше,
- **`"force-cache"`** -- использовать ответ из HTTP-кеша, даже если он устаревший. Если же ответ в HTTP-кеше отсутствует, сделать обычный HTTP-запрос, действовать как обычно,
- **`"only-if-cached"`** -- использовать ответ из HTTP-кеша, даже если он устаревший. Если же ответ в HTTP-кеше отсутствует, то выдаётся ошибка. Это работает, только когда `mode` установлен в `"same-origin"`.

## redirect

Обычно `fetch` прозрачно следует HTTP-редиректам, таким как 301, 302 и так далее.

Это можно поменять при помощи опции `redirect`:

- **`"follow"`** -- стоит по умолчанию, следовать HTTP-редиректам,
- **`"error"`** -- ошибка в случае HTTP-редиректа,
- **`"manual"`** -- не следовать HTTP-редиректу, но установить адрес редиректа в `response.url`, а `response.redirected` будет иметь значение `false`, чтобы мы могли сделать перенаправление на новый адрес вручную.

## integrity

Опция `integrity` позволяет проверить, соответствует ли ответ известной заранее контрольной сумме.

Как описано в [спецификации](https://w3c.github.io/webappsec-subresource-integrity/), поддерживаемыми хеш-функциями являются SHA-256, SHA-384 и SHA-512. В зависимости от браузера, могут быть и другие.

Например, мы скачиваем файл, и мы точно знаем, что его контрольная сумма по алгоритму SHA-256 равна "abcdef" (разумеется, настоящая контрольная сумма будет длиннее).

Мы можем добавить это в настройку `integrity` вот так:

```js
fetch('http://site.com/file', {
  integrity: 'sha256-abcdef'
});

Затем fetch самостоятельно вычислит SHA-256 и сравнит его с нашей строкой. В случае несоответствия будет ошибка.

keepalive

Опция keepalive указывает на то, что запрос может "пережить" страницу, которая его отправила.

Например, мы собираем статистические данные о том, как посетитель ведёт себя на нашей странице (на что он кликает, части страницы, которые он просматривает), для анализа и улучшения интерфейса.

Когда посетитель покидает нашу страницу - мы хотим сохранить собранные данные на нашем сервере.

Для этого мы можем использовать событие unload на объекте window:

window.onunload = function() {
  fetch('/analytics', {
    method: 'POST',
    body: "statistics",
*!*
    keepalive: true
*/!*
  });
};

Обычно, когда документ выгружается, все связанные с ним сетевые запросы прерываются. Но настройка keepalive указывает браузеру выполнять запрос в фоновом режиме даже после того, как пользователь покидает страницу. Поэтому эта опция обязательна, чтобы такой запрос удался.

У неё есть ряд ограничений:

  • Мы не можем посылать мегабайты: лимит тела для запроса с keepalive - 64кб.
    • Если мы собираем больше данных, можем отправлять их регулярно, "пакетами", тогда на момент события unload их останется немного.
    • Этот лимит распространяется на все запросы с keepalive. То есть мы не можем его обойти, послав 100 запросов одновременно - каждый по 64Кбайт.
  • Мы не можем обработать ответ сервера, если документ выгружен. Поэтому в нашем примере fetch завершится успешно благодаря keepalive, но последующие функции не сработают.
    • В большинстве случаев, например, при отправке статистики, это не проблема, поскольку сервер просто принимает данные и обычно отправляет пустой ответ на такие запросы.