From 074785cea106179cb3305637055ab0a009ca74f2 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:42:52 -0500 Subject: initial commit --- .../fetch_api/cross-global_fetch_usage/index.html | 33 ++ files/ru/web/api/fetch_api/index.html | 102 +++++ files/ru/web/api/fetch_api/using_fetch/index.html | 488 +++++++++++++++++++++ 3 files changed, 623 insertions(+) create mode 100644 files/ru/web/api/fetch_api/cross-global_fetch_usage/index.html create mode 100644 files/ru/web/api/fetch_api/index.html create mode 100644 files/ru/web/api/fetch_api/using_fetch/index.html (limited to 'files/ru/web/api/fetch_api') diff --git a/files/ru/web/api/fetch_api/cross-global_fetch_usage/index.html b/files/ru/web/api/fetch_api/cross-global_fetch_usage/index.html new file mode 100644 index 0000000000..ec05253d7f --- /dev/null +++ b/files/ru/web/api/fetch_api/cross-global_fetch_usage/index.html @@ -0,0 +1,33 @@ +--- +title: Cross-global fetch usage +slug: Web/API/Fetch_API/Cross-global_fetch_usage +translation_of: Web/API/Fetch_API/Cross-global_fetch_usage +--- +

Эта статья объясняет крайний случай, который случается с fetch (и потенциально с другими API, предоставляющими такой же способ получения данных). Когда cross-origin fetch, включающий относительный URL, инициируется из {{htmlelement("iframe")}}, относительный URL может использовать текущий глобальный location вместо того что задается в iframe.

+ +

Крайний случай

+ +

Многие сайты никогда не столкнутся с таким поведением. Чтобы увидеть его:

+ + + +

Проблема

+ +

В прошлом мы разрешали относительный URL адрес вместо текущего глобального, для примера:

+ +
let absolute = new URL(relative, window.location.href)
+ +

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

+ +

Решение

+ +

В Firefox 60 и далее, Mozilla сопоставляет относительный URL с глобальным, которой принадлежит используемой функции fetch() (смотри {{bug(1432272)}}). Таким образом в случае описанном выше, он разрешается в зависимости от расположения iframe:

+ +
let absolute = new URL(relative, frame.contentWindow.location.href)
+ +

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

diff --git a/files/ru/web/api/fetch_api/index.html b/files/ru/web/api/fetch_api/index.html new file mode 100644 index 0000000000..cb3917dcf8 --- /dev/null +++ b/files/ru/web/api/fetch_api/index.html @@ -0,0 +1,102 @@ +--- +title: Fetch API +slug: Web/API/Fetch_API +tags: + - API + - Fetch + - Response + - XMLHttpRequest + - request + - Главная + - Справка + - Экспериментальная +translation_of: Web/API/Fetch_API +--- +

{{DefaultAPISidebar("Fetch API")}}

+ +

Fetch API предоставляет интерфейс для получения ресурсов (в том числе по сети). Он покажется знакомым любому, кто использовал {{DOMxRef("XMLHttpRequest")}}, но новый API является более мощным и гибким набором функций.

+ +

Oпределения и использование

+ +

Fetch обеспечивает обобщенное определение объектов {{DOMxRef("Request")}} и {{DOMxRef("Response")}} (и других вещей, связанных с сетевыми запросами). Это позволит им использоваться везде, где необходимо в будущем, в том числе и для service workers, Cache API и других подобных технологий, которые обрабатывают или изменяют запросы (requests) и ответы (responses), а так же в любых других случаях, которые могут потребовать от вас генерировать свой собственный ответ программно.

+ +

Это также предоставляет определение в отношение таких понятий, как CORS и семантика заголовков HTTP origin, заменяя их обособленные определения где бы то ни было.

+ +

Чтобы создать запрос и получить данные, используется метод {{DOMxRef("GlobalFetch.fetch")}}. Он реализован во множестве интерфейсов, в том числе в {{DOMxRef("Window")}} и {{DOMxRef("WorkerGlobalScope")}}. Это позволяет использовать его практически в любом контексте для получения данных.

+ +

Метод fetch() принимает один обязательный аргумент —  путь к данным, которые вы хотите получить. Он возвращает promise, который разрешается в ({{DOMxRef("Response")}}) независимо от того, был ли запрос удачным. Вы можете также передать во втором аргументе необязательный объект с указанием опций (см. {{DOMxRef("Request")}}.)

+ +

Как только {{DOMxRef("Response")}} выполнится успешно, становятся доступными несколько методов для определения тела контента и, как его содержимое олжно быть обработано (см. {{DOMxRef("Body")}}.)

+ +

Вы можете создавать запрос и ответ непосредственно, используя конструкторы {{DOMxRef("Request.Request","Request()")}} и {{DOMxRef("Response.Response","Response()")}}, но маловероятно, что в этом есть необходимость. Напротив, более вероятно, что они будут созданы как результат работы другого API (например, {{DOMxRef("FetchEvent.respondWith")}} в service workers.)

+ +

Отличия от jQuery

+ +

Спецификации fetch() отличаются от jQuery.ajax() тремя основными способами:

+ + + +
Заметка: узнайте больше об использовании Fetch API на Using Fetch и изучите концепции на Fetch basic concepts.
+ +

Прерывание выборки

+ +

Браузеры начали добавлять экспериментальную поддержку для {{domxref("AbortController")}} и {{domxref("AbortSignal")}} интерфейсов (aka The Abort API), которые позволяют прерывать операции, такие как Fetch и XHR, если они еще не завершены. Подробности смотрите на страницах интерфейсов.

+ +

Fetch интерфейсы

+ +
+
{{DOMxRef("GlobalFetch")}}
+
Содержит метод fetch(), используемый для получения ресурсов.
+
{{DOMxRef("Headers")}}
+
Представляет заголовки запроса/ответа, позволяет запрашивать данные и выполнять различные действия в зависимости от результата.
+
{{DOMxRef("Request")}}
+
Запрашивает ресурс.
+
{{DOMxRef("Response")}}
+
Представляет ответ на запрос.
+
+ +

Fetch примесь

+ +
+
{{DOMxRef("Body")}}
+
Предоставляет методы, относящиеся к телу запроса/ответа, позволяя вам определять content-type и то, как ответ должен быть обработан.
+
+ +

Спецификации

+ + + + + + + + + + + + + + +
СпецификацияСтатусКомментарий
{{SpecName('Fetch')}}{{Spec2('Fetch')}}Изначальное определение
+ +

Браузерная совместимость

+ +

{{Compat("api.WindowOrWorkerGlobalScope.fetch")}}

+ +

Смотрите также

+ + diff --git a/files/ru/web/api/fetch_api/using_fetch/index.html b/files/ru/web/api/fetch_api/using_fetch/index.html new file mode 100644 index 0000000000..f123f02792 --- /dev/null +++ b/files/ru/web/api/fetch_api/using_fetch/index.html @@ -0,0 +1,488 @@ +--- +title: Использование Fetch +slug: Web/API/Fetch_API/Using_Fetch +translation_of: Web/API/Fetch_API/Using_Fetch +--- +

Fetch API предоставляет интерфейс JavaScript для работы с запросами и ответами HTTP. Он также предоставляет глобальный метод {{domxref("GlobalFetch.fetch","fetch()")}}, который позволяет легко и логично получать ресурсы по сети асинхронно.

+ +

Подобная функциональность ранее достигалась с помощью {{domxref("XMLHttpRequest")}}. Fetch представляет собой лучшую альтернативу, которая может быть легко использована другими технологиями, такими как {{domxref("ServiceWorker_API", "Service Workers")}}. Fetch также обеспечивает единое логическое место для определения других связанных с HTTP понятий, такие как CORS и расширения для HTTP.

+ +

Обратите внимание, fetch спецификация отличается от jQuery.ajax() в основном в двух пунктах:

+ + + +
+

25 августа 2017 г. в спецификации изменилось значение по умолчанию свойства credentials на same-origin. Firefox применяет это изменение с версии 61.0b13.

+
+ +

Базовый запрос на получение данных действительно прост в настройке. Взгляните на следующий код:

+ +
fetch('http://example.com/movies.json')
+  .then((response) => {
+    return response.json();
+  })
+  .then((data) => {
+    console.log(data);
+  });
+ +

Здесь мы забираем JSON файл по сети и выводим его содержимое в консоль. Самый простой способ использования fetch() заключается в вызове этой функии с одним аргументом — строкой, содержащей путь к ресурсу, который вы хотите получить — которая возвращает promise, содержащее ответ (объект {{domxref("Response")}}).

+ +

Конечно, это просто HTTP-ответ, а не фактический JSON. Чтобы извлечь содержимое тела JSON из ответа, мы используем {{domxref("Body.json","json()")}} метод  (определён миксином {{domxref("Body")}}, который реализован в объектах {{domxref("Request")}} и {{domxref("Response")}}.)

+ +
+

Примечание: Миксин Body имеет подобные методы для извлечения других типов контента; см. раздел {{anch("Тело")}}.

+
+ +

Fetch-запросы контролируются посредством директивы connect-src (Content Security Policy), а не директивой извлекаемых ресурсов.

+ +

Установка параметров запроса

+ +

Метод fetch() может принимать второй параметр - обьект init, который позволяет вам контролировать различные настройки:

+ +
// Пример отправки POST запроса:
+async function postData(url = '', data = {}) {
+  // Default options are marked with *
+  const response = await fetch(url, {
+    method: 'POST', // *GET, POST, PUT, DELETE, etc.
+    mode: 'cors', // no-cors, *cors, same-origin
+    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
+    credentials: 'same-origin', // include, *same-origin, omit
+    headers: {
+      'Content-Type': 'application/json'
+      // 'Content-Type': 'application/x-www-form-urlencoded',
+    },
+    redirect: 'follow', // manual, *follow, error
+    referrerPolicy: 'no-referrer', // no-referrer, *client
+    body: JSON.stringify(data) // body data type must match "Content-Type" header
+  });
+  return await response.json(); // parses JSON response into native JavaScript objects
+}
+
+postData('https://example.com/answer', { answer: 42 })
+  .then((data) => {
+    console.log(data); // JSON data parsed by `response.json()` call
+  });
+ +

С подробным описанием функции и полным списком параметров вы можете ознакомиться на странице {{domxref("GlobalFetch.fetch","fetch()")}}.

+ +

Отправка запроса с учётными данными

+ +

Чтобы браузеры могли отправлять запрос с учётными данными (даже для cross-origin запросов), добавьте credentials: 'include' в объект init, передаваемый вами в метод fetch():

+ +
fetch('https://example.com', {
+  credentials: 'include'
+})
+ +

Если вы хотите отправлять запрос с учетными данными только если URL принадлежит одному источнику (origin) что и вызывающий его скрипт, добавьте credentials: 'same-origin'.

+ +
// Вызывающий скрипт принадлежит источнику 'https://example.com'
+
+fetch('https://example.com', {
+  credentials: 'same-origin'
+})
+ +

Напротив, чтобы быть уверенным, что учётные данные не передаются с запросом, используйте credentials: 'omit':

+ +
fetch('https://example.com', {
+  credentials: 'omit'
+})
+ +

Отправка данных в формате JSON

+ +

При помощи {{domxref("GlobalFetch.fetch","fetch()")}} можно отправлять POST-запросы в формате JSON.

+ +
const url = 'https://example.com/profile';
+const data = { username: 'example' };
+
+try {
+  const response = await fetch(url, {
+    method: 'POST', // или 'PUT'
+    body: JSON.stringify(data), // данные могут быть 'строкой' или {объектом}!
+    headers: {
+      'Content-Type': 'application/json'
+    }
+  });
+  const json = await response.json();
+  console.log('Успех:', JSON.stringify(json));
+} catch (error) {
+  console.error('Ошибка:', error);
+}
+ +

Загрузка файла на сервер

+ +

На сервер можно загрузить файл, используя комбинацию HTML-элемента <input type="file" />, {{domxref("FormData.FormData","FormData()")}} и {{domxref("WindowOrWorkerGlobalScope/fetch","fetch()")}}.

+ +
const formData = new FormData();
+const fileField = document.querySelector('input[type="file"]');
+
+formData.append('username', 'abc123');
+formData.append('avatar', fileField.files[0]);
+
+try {
+  const response = await fetch('https://example.com/profile/avatar', {
+    method: 'PUT',
+    body: formData
+  });
+  const result = await response.json();
+  console.log('Успех:', JSON.stringify(result));
+} catch (error) {
+  console.error('Ошибка:', error);
+}
+ +

Загрузка нескольких файлов на сервер

+ +

На сервер можно загрузить несколько файлов, используя комбинацию HTML-элемента <input type="file" multiple />, {{domxref("FormData.FormData","FormData()")}} и {{domxref("WindowOrWorkerGlobalScope/fetch","fetch()")}}.

+ +
const formData = new FormData();
+const photos = document.querySelector('input[type="file"][multiple]');
+
+formData.append('title', 'Мой отпуск в Вегасе');
+for (let i = 0; i < photos.files.length; i++) {
+  formData.append('photos', photos.files[i]);
+}
+
+try {
+  const response = await fetch('https://example.com/posts', {
+    method: 'POST',
+    body: formData
+  });
+  const result = await response.json();
+  console.log('Успех:', JSON.stringify(result));
+} catch (error) {
+  console.error('Ошибка:', error);
+}
+ +

Обработка текстового файла построчно

+ +

Фрагменты данных, получаемые из ответа, не разбиваются на строки автоматически (по крайней мере с достаточной точностью) и представляют собой не строки, а объекты {{jsxref("Uint8Array","Uint8Array")}}. Если вы хотите загрузить текстовый файл и обрабатывать его по мере загрузки построчно, то на вас самих ложится груз ответственности за обработку всех упомянутых моментов. Как пример, далее представлен один из способов подобной обработки с помощью создания построчного итератора (для простоты приняты следующие допущения: текст приходит в кодировке UTF-8 и ошибки получения не обрабатываются).

+ +
async function* makeTextFileLineIterator(fileURL) {
+  const utf8Decoder = new TextDecoder("utf-8");
+  let response = await fetch(fileURL);
+  let reader = response.body.getReader();
+  let {value: chunk, done: readerDone} = await reader.read();
+  chunk = chunk ? utf8Decoder.decode(chunk) : "";
+
+  let re = /\n|\r|\r\n/gm;
+  let startIndex = 0;
+  let result;
+
+  for (;;) {
+    let result = re.exec(chunk);
+    if (!result) {
+      if (readerDone) {
+        break;
+      }
+      let remainder = chunk.substr(startIndex);
+      ({value: chunk, done: readerDone} = await reader.read());
+      chunk = remainder + (chunk ? utf8Decoder.decode(chunk) : "");
+      startIndex = re.lastIndex = 0;
+      continue;
+    }
+    yield chunk.substring(startIndex, result.index);
+    startIndex = re.lastIndex;
+  }
+  if (startIndex < chunk.length) {
+    //последняя строка не имеет символа перевода строки в конце
+    yield chunk.substr(startIndex);
+  }
+}
+
+for await (let line of makeTextFileLineIterator(urlOfFile)) {
+  processLine(line);
+}
+ +

Проверка успешности запроса

+ +

В методе {{domxref("GlobalFetch.fetch","fetch()")}} promise будет отклонён (reject) с {{jsxref("TypeError")}}, когда случится ошибка сети или не будет сконфигурирован CORS на стороне запрашиваемого сервера, хотя обычно это означает проблемы доступа или аналогичные — для примера, 404 не является сетевой ошибкой. Для достоверной проверки успешности fetch() будет включать проверку того, что promise успешен (resolved), затем проверку того, что  значение свойства {{domxref("Response.ok")}} является true. Код будет выглядеть примерно так:

+ +
try {
+  const response = await fetch('flowers.jpg');
+  if (!response.ok) {
+    throw new Error('Ответ сети был не ok.');
+  }
+  const myBlob = await response.blob();
+  const objectURL = URL.createObjectURL(myBlob);
+  myImage.src = objectURL;
+} catch (error) {
+  console.log('Возникла проблема с вашим fetch запросом: ', error.message);
+}
+ +

Составление своего объекта запроса

+ +

Вместо передачи пути ресурса, который вы хотите запросить вызовом fetch(), вы можете создать объект запроса, используя конструктор {{domxref("Request.Request","Request()")}}, и передать его в fetch() аргументом:

+ +
const myHeaders = new Headers();
+
+const myInit = {
+  method: 'GET',
+  headers: myHeaders,
+  mode: 'cors',
+  cache: 'default'
+};
+
+const myRequest = new Request('flowers.jpg', myInit);
+const response = await fetch(myRequest);
+const myBlob = await response.blob();
+const objectURL = URL.createObjectURL(myBlob);
+myImage.src = objectURL;
+ +

Конструктор Request() принимает точно такие же параметры, как и метод fetch(). Вы даже можете передать существующий объект запроса для создания его копии:

+ +
const anotherRequest = new Request(myRequest, myInit);
+ +

Довольно удобно, когда тела запроса и ответа используются единожды (прим.пер.: "are one use only"). Создание копии как показано позволяет вам использовать запрос/ответ повторно, при изменении опций init, при желании. Копия должна быть сделана до прочтения тела, а чтение тела в копии также пометит его прочитанным в исходном запросе.

+ +
+

Примечание: Также есть метод {{domxref("Request.clone","clone()")}}, создающий копии. Оба метода создания копии прекратят работу с ошибкой если тело оригинального запроса или ответа уже было прочитано, но чтение тела клонированного ответа или запроса не приведёт к маркировке оригинального.

+
+ +

Заголовки

+ +

Интерфейс {{domxref("Headers")}} позволяет вам создать ваш собственный объект заголовков через конструктор {{domxref("Headers.Headers","Headers()")}}. Объект заголовков - простая мультикарта имён-значений:

+ +
const content = 'Hello World';
+const myHeaders = new Headers();
+myHeaders.append('Content-Type', 'text/plain');
+myHeaders.append('Content-Length', content.length.toString());
+myHeaders.append('X-Custom-Header', 'ProcessThisImmediately');
+ +

То же может быть достигнуто путём передачи массива массивов или литерального объекта конструктору:

+ +
const myHeaders = new Headers({
+  'Content-Type': 'text/plain',
+  'Content-Length': content.length.toString(),
+  'X-Custom-Header': 'ProcessThisImmediately'
+});
+ +

Содержимое может быть запрошено и извлечено:

+ +
console.log(myHeaders.has("Content-Type")); // true
+console.log(myHeaders.has("Set-Cookie")); // false
+myHeaders.set("Content-Type", "text/html");
+myHeaders.append("X-Custom-Header", "AnotherValue");
+
+console.log(myHeaders.get("Content-Length")); // 11
+console.log(myHeaders.get("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]
+
+myHeaders.delete("X-Custom-Header");
+console.log(myHeaders.get("X-Custom-Header")); // [ ]
+ +

Некоторые из этих операций могут быть использованы только в {{domxref("ServiceWorker_API","ServiceWorkers")}}, но они предоставляют более удобный API для манипуляции заголовками.

+ +

Все методы Headers выбрасывают TypeError, если имя используемого заголовка не является валидным именем HTTP Header. Операции мутации выбросят TypeError если есть защита от мутации (смотрите ниже) (прим.пер.: "if there is an immutable guard"). В противном случае они прерываются молча. Например:

+ +
const myResponse = Response.error();
+try {
+  myResponse.headers.set('Origin', 'http://mybank.com');
+} catch (e) {
+  console.log('Не могу притвориться банком!');
+}
+ +

Хорошим вариантом использования заголовков  является проверка корректности типа контента перед его обработкой. Например:

+ +
try {
+  const response = await fetch(myRequest);
+  const contentType = response.headers.get('content-type');
+  if (!contentType || !contentType.includes('application/json')) {
+    throw new TypeError("Ой, мы не получили JSON!");
+  }
+  const json = await response.json();
+  /* Дальнейшая обработка JSON */
+} catch (error) {
+  console.log(error);
+}
+ +

Защита

+ +

С тех пор как заголовки могут передаваться в запросе, приниматься в ответе и имеют различные ограничения в отношении того, какая информация может и должна быть изменена, заголовки имеют свойство guard. Это не распространяется на Web, но влияет на то, какие операции мутации доступны для объекта заголовков.

+ +

Возможные значения:

+ + + +
+

Примечание: Вы не можете добавить или установить request защищаемые Headers’ заголовок Content-Length. Аналогично, вставка Set-Cookie в заголовок ответа недопустимо: ServiceWorkers не допускают установки cookies через синтезированные ответы.

+
+ +

Объекты ответа

+ +

Как вы видели выше, экземпляр {{domxref("Response")}} будет возвращен когда fetch() промис будет исполнен.

+ +

Свойства объекта-ответа которые чаще всего используются:

+ + + +

Они так же могут быть созданы с помощью JavaScript, но реальная польза от этого есть только при использовании  {{domxref("ServiceWorker_API", "сервисворкеров")}}, когда вы предоставляете собственный ответ на запрос с помощью метода {{domxref("FetchEvent.respondWith","respondWith()")}}:

+ +
const myBody = new Blob();
+
+addEventListener('fetch', function(event) {
+  // ServiceWorker перехватывает fetch
+  event.respondWith(
+    new Response(myBody, {
+      headers: { 'Content-Type': 'text/plain' }
+    })
+  );
+});
+ +

Конструктор {{domxref("Response.Response","Response()")}} принимает два необязательных аргумента — тело для ответа и объект init (аналогичный тому, который принимает {{domxref("Request.Request","Request()")}})

+ + + +
+

Примечание: Метод {{domxref("Response.error","error()")}} просто возвращает ответ об ошибке. Аналогично, {{domxref("Response.redirect","redirect()")}} возвращает ответ, приводящий к перенаправлению на указанный URL. Они также относятся только к Service Workers.

+
+ +

Тело

+ +

Запрос и ответ могут содержать данные тела. Тело является экземпляром любого из следующих типов:

+ + + +

{{domxref("Body")}} примесь определяет следующие методы для извлечения тела (реализованны как для {{domxref("Request")}} так и для {{domxref("Response")}}). Все они возвращают promise, который в конечном итоге исполняется и выводит содержимое.

+ + + +

Это делает использование нетекстовых данных более легким, чем при XMR.

+ +

В запросе можно установить параметры для отправки тела запроса:

+ +
const form = new FormData(document.getElementById('login-form'));
+fetch('/login', {
+  method: 'POST',
+  body: form
+});
+ +

Параметры request и response (and by extension the fetch() function), по возможности возвращают корректные типы данных. Параметр request также автоматически установит Content-Type в заголовок, если он не был установлен из словаря.

+ +

Функция обнаружения

+ +

Поддержка Fetch API может быть обнаружена путем проверки наличия {{domxref("Headers")}}, {{domxref("Request")}}, {{domxref("Response")}} или {{domxref("GlobalFetch.fetch","fetch()")}} в области видимости {{domxref("Window")}} или {{domxref("Worker")}}. Для примера:

+ +
if (self.fetch) {
+    // запустить мой fetch запрос здесь
+} else {
+    // Сделать что-то с XMLHttpRequest?
+}
+ +

Полифилл

+ +

Для того, чтобы использовать Fetch в неподдерживаемых браузерах, существует  Fetch Polyfill , который воссоздает функциональность для не поддерживающих браузеров.

+ +

Спецификации

+ + + + + + + + + + + + + + +
СпецификацииСтатусКомментарий
{{SpecName('Fetch')}}{{Spec2('Fetch')}}Первоначальное описание
+ +

Совместимость браузера

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + + + +
FeatureChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Базовая поддержка{{CompatChrome(42)}}14{{CompatGeckoDesktop(39)}}
+ {{CompatGeckoDesktop(34)}}[1]
+ {{CompatGeckoDesktop(52)}}[2]
{{CompatNo}}29
+ 28[1]
10.1
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroid WebviewChrome for AndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari MobileChrome for Android
Базовая поддержка{{CompatChrome(42)}}{{CompatChrome(42)}}{{CompatVersionUnknown}}{{CompatNo}}{{CompatUnknown}}10.1{{CompatUnknown}}
+
+ +

[1] Этот API  is implemented behind a preference.

+ +

[2] До Firefox 52, get() возращал только  первое значение в указанном заголовке, а getAll() возращал все значения. Начиная с 52, get() теперь возращает все значения и getAll() был удален.

+ +

См. также

+ + -- cgit v1.2.3-54-g00ecf