Fetch
JavaScript может отправлять сетевые запросы на сервер и подгружать новую информацию по мере необходимости.
Например, мы можем использовать сетевой запрос, чтобы:
- Отправить заказ,
- Загрузить информацию о пользователе,
- Запросить последние обновления с сервера,
- …и т.п.
Для сетевых запросов из JavaScript есть широко известный термин «AJAX» (аббревиатура от Asynchronous JavaScript And XML). XML мы использовать не обязаны, просто термин старый, поэтому в нём есть это слово. Возможно, вы его уже где-то слышали.
Есть несколько способов делать сетевые запросы и получать информацию с сервера.
Метод fetch() — современный и очень мощный, поэтому начнём с него. Он не поддерживается старыми (можно использовать полифил), но поддерживается всеми современными браузерами.
let promise = fetch(url, [options])
- url – URL для отправки запроса.
- options – дополнительные параметры: метод, заголовки и так далее.
Без options это простой GET-запрос, скачивающий содержимое по адресу url .
Браузер сразу же начинает запрос и возвращает промис, который внешний код использует для получения результата.
Процесс получения ответа обычно происходит в два этапа.
Во-первых, promise выполняется с объектом встроенного класса Response в качестве результата, как только сервер пришлёт заголовки ответа.
На этом этапе мы можем проверить статус HTTP-запроса и определить, выполнился ли он успешно, а также посмотреть заголовки, но пока без тела ответа.
Промис завершается с ошибкой, если fetch не смог выполнить HTTP-запрос, например при ошибке сети или если нет такого сайта. HTTP-статусы 404 и 500 не являются ошибкой.
Мы можем увидеть HTTP-статус в свойствах ответа:
- status – код статуса HTTP-запроса, например 200.
- ok – логическое значение: будет true , если код HTTP-статуса в диапазоне 200-299.
let response = await fetch(url); if (response.ok) < // если HTTP-статус в диапазоне 200-299 // получаем тело ответа (см. про этот метод ниже) let json = await response.json(); >else
Во-вторых, для получения тела ответа нам нужно использовать дополнительный вызов метода.
Response предоставляет несколько методов, основанных на промисах, для доступа к телу ответа в различных форматах:
- response.text() – читает ответ и возвращает как обычный текст,
- response.json() – декодирует ответ в формате JSON,
- response.formData() – возвращает ответ как объект FormData (разберём его в следующей главе),
- response.blob() – возвращает объект как Blob (бинарные данные с типом),
- response.arrayBuffer() – возвращает ответ как ArrayBuffer (низкоуровневое представление бинарных данных),
- помимо этого, response.body – это объект ReadableStream, с помощью которого можно считывать тело запроса по частям. Мы рассмотрим и такой пример несколько позже.
Например, получим JSON-объект с последними коммитами из репозитория на GitHub:
let url = 'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'; let response = await fetch(url); let commits = await response.json(); // читаем ответ в формате JSON alert(commits[0].author.login);
То же самое без await , с использованием промисов:
fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits') .then(response => response.json()) .then(commits => alert(commits[0].author.login));
Для получения ответа в виде текста используем await response.text() вместо .json() :
let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); let text = await response.text(); // прочитать тело ответа как текст alert(text.slice(0, 80) + '. ');
В качестве примера работы с бинарными данными, давайте запросим и выведем на экран логотип спецификации «fetch» (см. главу Blob, чтобы узнать про операции с Blob ):
let response = await fetch('/article/fetch/logo-fetch.svg'); let blob = await response.blob(); // скачиваем как Blob-объект // создаём let img = document.createElement('img'); img.style = 'position:fixed;top:10px;left:10px;width:100px'; document.body.append(img); // выводим на экран img.src = URL.createObjectURL(blob); setTimeout(() => < // прячем через три секунды img.remove(); URL.revokeObjectURL(img.src); >, 3000);
Мы можем выбрать только один метод чтения ответа.
Если мы уже получили ответ с response.text() , тогда response.json() не сработает, так как данные уже были обработаны.
let text = await response.text(); // тело ответа обработано let parsed = await response.json(); // ошибка (данные уже были обработаны)
Заголовки ответа
Заголовки ответа хранятся в похожем на Map объекте response.headers .
Это не совсем Map , но мы можем использовать такие же методы, как с Map , чтобы получить заголовок по его имени или перебрать заголовки в цикле:
let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); // получить один заголовок alert(response.headers.get('Content-Type')); // application/json; charset=utf-8 // перебрать все заголовки for (let [key, value] of response.headers) < alert(`$= $`); >
Заголовки запроса
Для установки заголовка запроса в fetch мы можем использовать опцию headers . Она содержит объект с исходящими заголовками, например:
let response = fetch(protectedUrl, < headers: < Authentication: 'secret' >>);
Есть список запрещённых HTTP-заголовков, которые мы не можем установить:
- Accept-Charset , Accept-Encoding
- Access-Control-Request-Headers
- Access-Control-Request-Method
- Connection
- Content-Length
- Cookie , Cookie2
- Date
- DNT
- Expect
- Host
- Keep-Alive
- Origin
- Referer
- TE
- Trailer
- Transfer-Encoding
- Upgrade
- Via
- Proxy-*
- Sec-*
Эти заголовки обеспечивают достоверность данных и корректную работу протокола HTTP, поэтому они контролируются исключительно браузером.
POST-запросы
Для отправки POST -запроса или запроса с другим методом, нам необходимо использовать fetch параметры:
- method – HTTP метод, например POST ,
- body – тело запроса, одно из списка:
- строка (например, в формате JSON),
- объект FormData для отправки данных как form/multipart ,
- Blob / BufferSource для отправки бинарных данных,
- URLSearchParams для отправки данных в кодировке x-www-form-urlencoded , используется редко.
Чаще всего используется JSON.
Например, этот код отправляет объект user как JSON:
let user = < name: 'John', surname: 'Smith' >; let response = await fetch('/article/fetch/post/user', < method: 'POST', headers: < 'Content-Type': 'application/json;charset=utf-8' >, body: JSON.stringify(user) >); let result = await response.json(); alert(result.message);
Заметим, что так как тело запроса body – строка, то заголовок Content-Type по умолчанию будет text/plain;charset=UTF-8 .
Но, так как мы посылаем JSON, то используем параметр headers для отправки вместо этого application/json , правильный Content-Type для JSON.
Отправка изображения
Мы можем отправить бинарные данные при помощи fetch , используя объекты Blob или BufferSource .
В этом примере есть элемент , на котором мы можем рисовать движением мыши. При нажатии на кнопку «Отправить» изображение отправляется на сервер:
Заметим, что здесь нам не нужно вручную устанавливать заголовок Content-Type , потому что объект Blob имеет встроенный тип ( image/png , заданный в toBlob ). При отправке объектов Blob он автоматически становится значением Content-Type .
Функция submit() может быть переписана без async/await , например, так:
function submit() < canvasElem.toBlob(function(blob) < fetch('/article/fetch/post/image', < method: 'POST', body: blob >) .then(response => response.json()) .then(result => alert(JSON.stringify(result, null, 2))) >, 'image/png'); >
Итого
Типичный запрос с помощью fetch состоит из двух операторов await :
let response = await fetch(url, options); // завершается с заголовками ответа let result = await response.json(); // читать тело ответа в формате JSON
fetch(url, options) .then(response => response.json()) .then(result => /* обрабатываем результат */)
- response.status – HTTP-код ответа,
- response.ok – true , если статус ответа в диапазоне 200-299.
- response.headers – похожий на Map объект с HTTP-заголовками.
Методы для получения тела ответа:
- response.text() – возвращает ответ как обычный текст,
- response.json() – декодирует ответ в формате JSON,
- response.formData() – возвращает ответ как объект FormData (кодировка form/multipart, см. следующую главу),
- response.blob() – возвращает объект как Blob (бинарные данные с типом),
- response.arrayBuffer() – возвращает ответ как ArrayBuffer (низкоуровневые бинарные данные),
Опции fetch , которые мы изучили на данный момент:
- method – HTTP-метод,
- headers – объект с запрашиваемыми заголовками (не все заголовки разрешены),
- body – данные для отправки (тело запроса) в виде текста, FormData , BufferSource , Blob или UrlSearchParams .
В следующих главах мы рассмотрим больше параметров и вариантов использования fetch .
Задачи
Получите данные о пользователях GitHub
Создайте асинхронную функцию getUsers(names) , которая получает на вход массив логинов пользователей GitHub, запрашивает у GitHub информацию о них и возвращает массив объектов-пользователей.
Информация о пользователе GitHub с логином USERNAME доступна по ссылке: https://api.github.com/users/USERNAME .
В песочнице есть тестовый пример.
- На каждого пользователя должен приходиться один запрос fetch .
- Запросы не должны ожидать завершения друг друга. Надо, чтобы данные приходили как можно быстрее.
- Если какой-то запрос завершается ошибкой или оказалось, что данных о запрашиваемом пользователе нет, то функция должна возвращать null в массиве результатов.
Чтобы получить сведения о пользователе, нам нужно вызвать fetch(‘https://api.github.com/users/USERNAME’) .
Если ответ приходит cо статусом 200 , то вызываем метод .json() , чтобы прочитать JS-объект.
А если запрос завершается ошибкой или код статуса в ответе отличен от 200, то мы просто возвращаем null в массиве результатов.
async function getUsers(names) < let jobs = []; for(let name of names) < let job = fetch(`https://api.github.com/users/$`).then( successResponse => < if (successResponse.status != 200) < return null; >else < return successResponse.json(); >>, failResponse => < return null; >); jobs.push(job); > let results = await Promise.all(jobs); return results; >
Пожалуйста, обратите внимание: вызов .then прикреплён к fetch , чтобы, когда ответ получен, сразу начинать считывание данных с помощью .json() , не дожидаясь завершения других запросов.
Если бы мы использовали await Promise.all(names.map(name => fetch(. ))) и вызывали бы .json() на результатах запросов, то пришлось бы ждать, пока завершатся все из них. Вызывая .json() сразу после каждого fetch , мы добились того, что считывание присланных по каждому запросу данных происходит независимо от других запросов.
Это пример того, как относительно низкоуровневое Promise API может быть полезным, даже если мы в основном используем async/await в коде.
Руководство администратора
где url – URL для отправки запроса, options – дополнительные параметры, такие как метод, заголовки и т.д.
Где посмотреть URL
Типичный запрос с помощью fetch:
fetch ( url )
. then (( response ) => <
return response . json ()
>)
. then (( data ) => <
console .log( data ); // обрабатываем результат
>);или другой вариант:
let response = await fetch ( url , options); // завершается с заголовками ответа
let result = await response . json (); // читать тело ответа в формате JSONFetch работает на основе promise-ов, поэтому для работы в Internet Explorer нужно определять полифил.
Для GET-запроса параметры options не нужны.
Процесс получения ответа происходит в два этапа:
1 — выполняется запрос, который возвращает объект класса Response. На этом этапе можно проверить статус HTTP-запроса и определить, выполнился ли он успешно, а также посмотреть заголовки, но пока без тела ответа.
У Response есть свойства:
• status – код статуса HTTP-запроса (например, 200 — код успешного выполнения),
• ok – логическое значение, равное true если код HTTP-статуса в диапазоне 200-299.
Для fetch такие HTTP-статусы как 404 или 500 не являются ошибкой — запрос будет выполнен нормально, но со значением false в свойстве ok. Запрос будет отклонен только при сбое сети или если что-то помешало запросу выполниться
let response = await fetch ( url );
if ( response . ok ) <
// получаем тело ответа
let json = await response . json ();
> else <
alert(«Ошибка HTTP: » + response .status);
>2 — для получения тела ответа нужно использовать дополнительный вызов метода.
Response предоставляет несколько методов, основанных на промисах, для чтения ответа в различных форматах. Наиболее популярные:
• response.text() – читает ответ и возвращает как обычный текст,
• response.json() – декодирует ответ в формате JSON.
Можно использовать только один метод чтения ответа. Если уже был получен ответ с помощью response.text(), тогда response.json() не сработает, так как данные уже были обработаны.
let url = ‘. ‘;
let response = await fetch ( url );
let result = await response . json (); // читаем ответ в формате JSON
console .log( result );То же самое без await, с использованием промисов:
fetch (‘. ‘)
. then ( response => response . json ())
. then ( result => console .log( result ));Для получения ответа в виде текста используем response.text() вместо .json():
let response = await fetch (‘. ‘);
let text = await response .text(); // прочитать тело ответа как текст
console .log(text);Для отправки POST-запроса необходимо использовать параметры options :
• method – HTTP метод, например POST (также поддерживаются GET, PUT, DELETE и пр.)
• body – тело запроса (например, строка в формате JSON),
• headers — (опционально) заголовок запроса.
Например, этот код отправляет объект user как JSON:
let user = <
name: ‘John’,
surname : ‘Smith’
>;
let response = await fetch (url, <
method: ‘POST’,
headers : <
‘Content-Type’: ‘application/json;charset=utf-8’
>,
body: JSON . stringify ( user )
>);
let result = await response . json ();
alert( result . message );Поскольку body это текст, заголовок Content-Type по умолчанию — text/plain;charset=UTF-8. Но так как мы отправляем JSON, то в headers вместо этого задаем application/json, правильный Content-Type для JSON.
.
fetch (‘/app/v1.0/api/mobile/tasks/’ + taskid )
. then ( response => response . json ())
. then ( function (value) <
let info = JSON .parse(value);
if ( info . IsClosed == 0) <
console .log(‘Задача активна’)
> else <
console .log(‘Задача неактивна’);
>
>);
.Использование Fetch
Fetch API предоставляет интерфейс JavaScript для работы с запросами и ответами HTTP. Он также предоставляет глобальный метод fetch() (en-US), который позволяет легко и логично получать ресурсы по сети асинхронно.
Подобная функциональность ранее достигалась с помощью XMLHttpRequest . Fetch представляет собой лучшую альтернативу, которая может быть легко использована другими технологиями, такими как Service Workers (en-US). Fetch также обеспечивает единое логическое место для определения других связанных с HTTP понятий, такие как CORS и расширения для HTTP.
Обратите внимание, fetch спецификация отличается от jQuery.ajax() в основном в двух пунктах:
- Promise возвращаемый вызовом fetch() не перейдёт в состояние «отклонено» из-за ответа HTTP, который считается ошибкой, даже если ответ HTTP 404 или 500. Вместо этого, он будет выполнен нормально (с значением false в статусе ok ) и будет отклонён только при сбое сети или если что-то помешало запросу выполниться.
- По умолчанию, fetch не будет отправлять или получать cookie файлы с сервера, в результате чего запросы будут осуществляться без проверки подлинности, что приведёт к неаутентифицированным запросам, если сайт полагается на проверку пользовательской сессии (для отправки cookie файлов в аргументе init options должно быть задано значение свойства credentials отличное от значения по умолчанию omit ).
Примечание: 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, содержащее ответ (объект Response ).
Конечно, это просто HTTP-ответ, а не фактический JSON. Чтобы извлечь содержимое тела JSON из ответа, мы используем json() (en-US) метод (определён миксином Body , который реализован в объектах Request и Response .)
Примечание: Миксин Body имеет подобные методы для извлечения других типов контента; см. раздел Тело.
Fetch-запросы контролируются посредством директивы connect-src (Content Security Policy (en-US) ), а не директивой извлекаемых ресурсов.
Установка параметров запроса
Метод 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 >);
С подробным описанием функции и полным списком параметров вы можете ознакомиться на странице fetch() (en-US).
Отправка запроса с учётными данными
Чтобы браузеры могли отправлять запрос с учётными данными (даже для 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
При помощи fetch() (en-US) можно отправлять 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-элемента , FormData() и 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-элемента , FormData() и 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); >
Обработка текстового файла построчно
Фрагменты данных, получаемые из ответа, не разбиваются на строки автоматически (по крайней мере с достаточной точностью) и представляют собой не строки, а объекты Uint8Array (en-US). Если вы хотите загрузить текстовый файл и обрабатывать его по мере загрузки построчно, то на вас самих ложится груз ответственности за обработку всех упомянутых моментов. Как пример, далее представлен один из способов подобной обработки с помощью создания построчного итератора (для простоты приняты следующие допущения: текст приходит в кодировке 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); >
Проверка успешности запроса
В методе fetch() (en-US) promise будет отклонён (reject) с TypeError , когда случится ошибка сети или не будет сконфигурирован CORS на стороне запрашиваемого сервера, хотя обычно это означает проблемы доступа или аналогичные — для примера, 404 не является сетевой ошибкой. Для достоверной проверки успешности fetch() будет включать проверку того, что promise успешен (resolved), затем проверку того, что значение свойства Response.ok (en-US) является 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(), вы можете создать объект запроса, используя конструктор Request() (en-US), и передать его в 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, при желании. Копия должна быть сделана до прочтения тела, а чтение тела в копии также пометит его прочитанным в исходном запросе.
Примечание: Также есть метод clone() (en-US), создающий копии. Оба метода создания копии прекратят работу с ошибкой если тело оригинального запроса или ответа уже было прочитано, но чтение тела клонированного ответа или запроса не приведёт к маркировке оригинального.
Заголовки
Интерфейс Headers (en-US) позволяет вам создать ваш собственный объект заголовков через конструктор Headers() (en-US). Объект заголовков — простая мультикарта имён-значений:
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", >);
Содержимое может быть запрошено и извлечено:
.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")); // [ ]
Некоторые из этих операций могут быть использованы только в ServiceWorkers (en-US), но они предоставляют более удобный 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, но влияет на то, какие операции мутации доступны для объекта заголовков.
none: по умолчанию.request: защита объекта заголовков, полученного по запросу ( Request.headers (en-US)).request-no-cors: защита объекта заголовков, полученного по запросу созданного с Request.mode no-cors.response: защита Headers полученных от ответа ( Response.headers (en-US)).immutable: в основном, используется в ServiceWorkers; делает объект заголовков read-only.
Примечание: вы не можете добавить или установить request защищаемые Headers’ заголовок Content-Length. Аналогично, вставка Set-Cookie в заголовок ответа недопустимо: ServiceWorkers не допускают установки cookies через синтезированные ответы.
Как вы видели выше, экземпляр Response будет возвращён когда fetch() промис будет исполнен.
Свойства объекта-ответа которые чаще всего используются:
Response.status (en-US) — Целочисленное (по умолчанию 200) содержит код статуса ответа. Response.statusText (en-US) — Строка (по умолчанию»OK»), которая соответствует HTTP коду статуса. Response.ok (en-US) — как сказано ранее, это короткое свойство для упрощения проверки на то что статус ответа находится где-то между 200-299 включительно. Это свойство типа Boolean (en-US).
Они так же могут быть созданы с помощью JavaScript, но реальная польза от этого есть только при использовании сервис-воркеров (en-US), когда вы предоставляете собственный ответ на запрос с помощью метода respondWith() (en-US):
const myBody = new Blob(); addEventListener("fetch", function (event) // ServiceWorker перехватывает fetch event.respondWith( new Response(myBody, headers: "Content-Type": "text/plain" >, >), ); >);
Конструктор Response() принимает два необязательных аргумента — тело для ответа и объект init (аналогичный тому, который принимает Request() (en-US))
Примечание: Метод error() (en-US) просто возвращает ответ об ошибке. Аналогично, redirect() (en-US) возвращает ответ, приводящий к перенаправлению на указанный URL. Они также относятся только к Service Workers.
Тело
Запрос и ответ могут содержать данные тела. Тело является экземпляром любого из следующих типов:
Body примесь определяет следующие методы для извлечения тела (реализованы как для Request так и для 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 может быть обнаружена путём проверки наличия Headers (en-US), Request , Response или fetch() (en-US) в области видимости Window или Worker . Для примера:
if (self.fetch) // запустить мой fetch запрос здесь > else // Сделать что-то с XMLHttpRequest? >
Полифил
Для того, чтобы использовать Fetch в неподдерживаемых браузерах, существует Fetch Polyfill , который воссоздаёт функциональность для не поддерживающих браузеров.
СпецификацииSpecification Status Comment Fetch Живой стандарт Initial definitionСовместимость браузера
Смотрите такжеServiceWorker APIHTTP access control (CORS)HTTPFetch polyfillFetch examples on Github`
Found a content problem with this page?
- Edit the page on GitHub.
- Report the content issue.
- View the source on GitHub.
This page was last modified on 8 нояб. 2023 г. by MDN contributors.
Your blueprint for a better internet.
Основы работы с fetch в AJAX
Давайте теперь посмотрим, как подгрузить часть страницы с помощью AJAX. Пусть на нашей странице index.html будет расположен див и кнопка (здесь показано содержимое body ):
Пусть у нас также есть страница ajax.html , на которой расположены три абзаца (без тегов страницы, просто абзацы):
Давайте сделаем так, чтобы по клику кнопку на странице index.html в див подгрузилось содержимое страницы ajax.html .
Для этого для начала получим в переменные ссылки на наши элементы:
let div = document.querySelector(‘div’); let button = document.querySelector(‘button’);
Давайте навесим на кнопку обработчик клика:
button.addEventListener(‘click’, function() < // тут будем выполнять AJAX запрос >);
Давайте теперь выполним AJAX запрос. Для этого используется функция fetch , параметром получающая адрес страницы, содержимое которой мы хотим получить. Укажем адрес страницы ajax.html на нашем сервере:
button.addEventListener(‘click’, function() < let result = fetch('/ajax.html'); >);
Своим результатом fetch вернет промис. Дело в том, что запрос страницы через AJAX — это асинхронная операция, ведь пройдет некоторое время, через которое страница нам ответит.
Давайте получим результат промиса:
button.addEventListener(‘click’, function() < let promise = fetch('/ajax.html'); promise.then(function(response) < // ответ сервера лежит в переменной response >); >);
Упростим, избавившись от лишней переменной:
button.addEventListener(‘click’, function() < fetch('/ajax.html').then(function(response) < >); >);
Упростим, воспользовавшись стрелочной функцией:
button.addEventListener(‘click’, function() < fetch('/ajax.html').then(response =>< >); >);
Представим в более читаемом виде:
button.addEventListener(‘click’, function() < fetch('/ajax.html').then( response => < >); >);
В переменной response содержится достаточно сложный объект класса Response :
button.addEventListener(‘click’, function() < fetch('/ajax.html').then(response =>< console.log(response); // объект класса Response >); >);
Глубоко этот объект мы будем изучать в следующих уроках, а пока нам нужно самое простое — получить текст запрошенной страницы. Для этого в этом объекте существует метод text . Этот метод, однако, возвращает не текст страницы, а промис с ее текстом:
button.addEventListener(‘click’, function() < fetch('/ajax.html').then( response => < console.log(response.text()); // выведет Promise >); >);
Чтобы добраться до текста страницы, необходимо промис, полученный из response.text , обработать еще раз:
button.addEventListener(‘click’, function() < fetch('/ajax.html').then( response => < return response.text(); >).then( text => < console.log(text); // текст страницы >); >);
Итак, мы наконец получили текст запрошенной страницы и можем, например, записать его в наш див:
button.addEventListener(‘click’, function() < fetch('/ajax.html').then( response => < return response.text(); >).then( text => < div.innerHTML = text; >); >);
Даны три кнопки и див. На сервере даны три страницы. Сделайте так, чтобы каждая из кнопок подгружала в див соответствующую страницу.
Пусть на сервере есть пять страниц. Пусть первое нажатие на кнопку подгружает первую страницу, второе нажатие — вторую, и так далее, пока страницы не закончатся.