Fetch
Fetch API
предоставляет упрощенный и в тоже время гибкий и мощный инструмент для обращения к сетевым ресурсам по сравнению со стандартным XMLHttpRequest
. Ключевым элементом этого Fetch API
является функция fetch()
. Она реализована в различных контекстах. В частности, в браузере она реализована интерфейсом Windows
.
Функция fetch
имеет следующий синтаксис:
let fetchPromise = fetch(url, options)
url
URL для отправки запросаoptions
дополнительные параметры - метод, заголовки и так далее
Без options
это простой GET-запрос, скачивающий содержимое по адресу url
. Функция fetch()
возвращает объект Promise
, который получает ответ после завершения запроса к сетевому ресурсу. promise
выполняется с объектом встроенного класса Response в качестве результата, как только сервер пришлёт заголовки ответа.
Отправка запроса и чтение ответа
Если не указывать метод, то Fetch API по умолчанию делает GET-запрос:
let response = fetch(url);
index.html<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hmarketing.ru</title>
</head>
<body>
<div></div>
<button>Нажми</button>
<script>
let div = document.querySelector("div");
let button = document.querySelector("button");
button.addEventListener("click", function () {
// запрос к файлу ajax.html
let response = fetch("/ajax.html");
response
// в первом then находится информация о запросе и в каком формате мы получим ответ
.then((response) => {
if (response.ok) {
// возвращаем тело ответа
return response.text();
}
})
// в втором then находится ответ на запрос
.then((text) => {
// выведем данные
div.innerHTML = text;
})
// в catch мы попадём когда fetch() вообще не может выполнить запрос, т.е. нет такого сайта или произошла потеря сетевого соединения
.catch((error) => {
console.log(error);
});
});
</script>
</body>
</html>
При успешном выполнении запроса, мы получим объект Response
в первом then
. У Response
есть ряд полезных свойств для проверки состояния ответа:
body
содержимое ответа в виде объектаReadableStream
bodyUsed
хранит булевое значение, которое указывает, было ли содержимое ответа уже использовано.headers
набор заголовков ответа в виде объектаHeaders
ok
хранит булевое значение, которое указывает, завершился ли запрос успешно (то есть если статусной код ответа находится в диапазоне 200-299)redirected
хранит булевое значение, которое указывает, является ли ответ результатом переадресацииstatus
хранит статусный код ответаstatusText
хранит сообщение статуса, которое соответствует статусному кодуtype
хранит тип ответаurl
хранит адрес URL. Если в процессе запроса происходит ряд переадресаций, то хранит конечный адрес URL после всех переадресаций
Для получения тела ответа, у объекта Response
в первом then
имеются следующие методы:
response.text()
читает ответ и возвращает как обычный текст,response.json()
декодирует ответ в формате JSONresponse.formData()
возвращает ответ как объектFormData
response.blob()
возвращает объект какBlob
(бинарные данные с типом)response.arrayBuffer()
– возвращает ответ какArrayBuffer
(низкоуровневое представление бинарных данных)response.body
это объект ReadableStream, с помощью которого можно считывать тело запроса по частям
Отправка запроса и чтение ответа через async-await
Ключевое слово await
заставит интерпретатор JavaScript ждать до тех пор, пока промис справа от await
не выполнится. После чего оно вернёт его результат, и выполнение кода продолжится.
index.html<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hmarketing.ru</title>
</head>
<body>
<div></div>
<button>Нажми</button>
<script>
let div = document.querySelector("div");
let button = document.querySelector("button");
// функция, возвращающая промис
function promis(ms) {
return new Promise((resolve) =>
setTimeout(() => {
resolve(console.log(2));
}, ms)
);
}
button.addEventListener("click", async function () {
console.log(1);
try {
// запрос к файлу ajax.html
let response = await fetch("/ajax.html");
// получаем информацию о запросе и в каком формате мы получим ответ
if (response.ok) {
// получаем тело ответа
let data = await response.text();
// ждем выполнения промиса
await await promis(1000);
// выведем данные
div.innerHTML = data;
}
} catch (error) {
console.log(error);
}
console.log(3);
});
</script>
</body>
</html>
Заголовки ответа
Заголовки ответа хранятся в похожем на Map
объекте response.headers
.
Это не совсем Map
, но мы можем использовать такие же методы, как с Map
, чтобы получить заголовок по его имени:
index.html<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hmarketing.ru</title>
</head>
<body>
<div></div>
<button>Нажми</button>
<script>
let div = document.querySelector("div");
let button = document.querySelector("button");
button.addEventListener("click", function () {
// запрос к файлу ajax.html
let response = fetch("/ajax.html");
response
// в первом then находится информация о запросе и в каком формате мы получим ответ
.then((response) => {
if (response.ok) {
// выводим в консоль заголовок
console.log(response.headers.get("Content-Type"));
// возвращаем тело ответа
return response.text();
}
})
// в втором then находится ответ на запрос
.then((text) => {
// выведем данные
div.innerHTML = text;
})
// в catch мы попадём когда fetch() вообще не может выполнить запрос, т.е. нет такого сайта или произошла потеря сетевого соединения
.catch((error) => {
console.log(error);
});
});
</script>
</body>
</html>
Можем перебрать заголовки в цикле:
index.html<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hmarketing.ru</title>
</head>
<body>
<div></div>
<button>Нажми</button>
<script>
let div = document.querySelector("div");
let button = document.querySelector("button");
button.addEventListener("click", function () {
// запрос к файлу ajax.html
let response = fetch("/ajax.html");
response
// в первом then находится информация о запросе и в каком формате мы получим ответ
.then((response) => {
if (response.ok) {
// выводим в консоль заголовки
for (let [key, value] of response.headers) {
console.log(`${key} = ${value}`);
}
// возвращаем тело ответа
return response.text();
}
})
// в втором then находится ответ на запрос
.then((text) => {
// выведем данные
div.innerHTML = text;
})
// в catch мы попадём когда fetch() вообще не может выполнить запрос, т.е. нет такого сайта или произошла потеря сетевого соединения
.catch((error) => {
console.log(error);
});
});
</script>
</body>
</html>
Для получения данных из заголовков мы можем воспользоваться один из следующих методов интерфейса Headers
:
entries()
возвращает итератор, который позволяет пройтись по всем заголовкамforEach()
осуществляет перебор заголовковget()
возвращает значение определенного заголовкаhas()
возвращаетtrue
, если установлен определенный заголовокkeys()
получает все названия установленных заголовковvalues()
получает все значения установленных заголовков
Заголовки запроса
Для установки заголовка запроса в fetch
мы можем использовать опцию headers
. Она содержит объект с исходящими заголовками, например:
index.html<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hmarketing.ru</title>
</head>
<body>
<div></div>
<button>Нажми</button>
<script>
let div = document.querySelector("div");
let button = document.querySelector("button");
button.addEventListener("click", function () {
// запрос к файлу ajax.html
let response = fetch("/ajax.html", {
// устанавливаем заголовок
headers: {
Test: "Test",
},
});
response
// в первом then находится информация о запросе и в каком формате мы получим ответ
.then((response) => {
if (response.ok) {
// возвращаем тело ответа
return response.text();
}
})
// в втором then находится ответ на запрос
.then((text) => {
// выведем данные
div.innerHTML = text;
})
// в catch мы попадём когда fetch() вообще не может выполнить запрос, т.е. нет такого сайта или произошла потеря сетевого соединения
.catch((error) => {
console.log(error);
});
});
</script>
</body>
</html>
Есть список запрещённых 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-*
Отправка данных в запросе
Для отправки данных в запросе в функции fetch()
применяется в рамках второго параметра применяется опция body
. Стоит учитывать, что в запросах с методом GET
и HEAD
для запроса нельзя установить эту опцию. Эти данные могут представлять типы:
Blob
BufferSource
FormData
URLSearchParams
USVString
ReadableStream
Определим на странице index.html код для отправки данных на сервер. Для отправки применяется метод POST
. В качестве отправляемых данных выступает простая строка Test
:
index.html<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hmarketing.ru</title>
</head>
<body>
<div></div>
<button>Нажми</button>
<script>
let div = document.querySelector("div");
let button = document.querySelector("button");
let login = "test";
let pass = 123123;
button.addEventListener("click", function () {
// запрос к файлу ajax.html
let response = fetch("/ajax.html", {
// устанавливаем заголовок
headers: {
Test: "Test",
},
// устанавливаем метод
method: "POST",
// передаем тело запроса
body: `'login=' + ${login} + '&pass=' + ${pass}`,
});
response
// в первом then находится информация о запросе и в каком формате мы получим ответ
.then((response) => {
if (response.ok) {
// возвращаем тело ответа
return response.text();
}
})
// в втором then находится ответ на запрос
.then((text) => {
// выведем данные
div.innerHTML = text;
})
// в catch мы попадём когда fetch() вообще не может выполнить запрос, т.е. нет такого сайта или произошла потеря сетевого соединения
.catch((error) => {
console.log(error);
});
});
</script>
</body>
</html>
Часто не очень удобно формировать вручную свойство body
. В него можно передать объекты класса URLSearchParams
, в которых содержатся переменные и их значения:
index.html<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hmarketing.ru</title>
</head>
<body>
<div></div>
<button>Нажми</button>
<script>
let div = document.querySelector("div");
let button = document.querySelector("button");
let login = "test";
let pass = 123123;
button.addEventListener("click", function () {
// конструктор который передает POST параметры в body
let params = new URLSearchParams();
params.set("login", login);
params.set("pass", pass);
// запрос к файлу ajax.html
let response = fetch("/ajax.html", {
// устанавливаем заголовок
headers: {
Test: "Test",
},
// устанавливаем метод
method: "POST",
// передаем тело запроса
body: params,
});
response
// в первом then находится информация о запросе и в каком формате мы получим ответ
.then((response) => {
if (response.ok) {
// возвращаем тело ответа
return response.text();
}
})
// в втором then находится ответ на запрос
.then((text) => {
// выведем данные
div.innerHTML = text;
})
// в catch мы попадём когда fetch() вообще не может выполнить запрос, т.е. нет такого сайта или произошла потеря сетевого соединения
.catch((error) => {
console.log(error);
});
});
</script>
</body>
</html>
Для кодирования данных метод FormData
использует формат "multipart/form-data"
. Это означает то, что он позволяет подготовить для отправки по AJAX не только текстовые данные, но и файлы (input с type, равным file):
index.html<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hmarketing.ru</title>
</head>
<body>
<form method="post">
<input type="text" name="user_name" />
<input type="tel" name="user_phone" />
<button>Нажми</button>
</form>
<div></div>
<script>
let div = document.querySelector("div");
let button = document.querySelector("button");
let form = document.querySelector("form");
button.addEventListener("click", function (event) {
// отменяем стандартное действие
event.preventDefault();
// конструктор который создает объект формы
let params = new FormData(form);
// запрос к файлу ajax.html
let response = fetch("/ajax.html", {
// устанавливаем заголовок
headers: {
Test: "Test",
},
// устанавливаем метод
method: "POST",
// передаем тело запроса
body: params,
});
response
// в первом then находится информация о запросе и в каком формате мы получим ответ
.then((response) => {
if (response.ok) {
// возвращаем тело ответа
return response.text();
}
})
// в втором then находится ответ на запрос
.then((text) => {
// выведем данные
div.innerHTML = text;
})
// в catch мы попадём когда fetch() вообще не может выполнить запрос, т.е. нет такого сайта или произошла потеря сетевого соединения
.catch((error) => {
console.log(error);
});
});
</script>
</body>
</html>
Передача куки
По умолчанию куки не передаются в AJAX запросах. Это означает, что не будет работать сессия сервера. Обычно передача кук нам все-таки нужна. Ее можно включить настройкой credentials
у которой есть три параметра:
credentials: 'include'
заставит передавать куки в AJAX запросе даже если запрос cross-origincredentials: 'same-origin'
заставит передавать куки, но только на тот сайт, на котором запускается скриптcredentials: 'omit'
запрещает передавать куки
index.html<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hmarketing.ru</title>
</head>
<body>
<div></div>
<button>Нажми</button>
<script>
let div = document.querySelector("div");
let button = document.querySelector("button");
button.addEventListener("click", function () {
// запрос к файлу ajax.html
let response = fetch("/ajax.html", {
// запрещаем передавать куки
credentials: "omit",
});
response
// в первом then находится информация о запросе и в каком формате мы получим ответ
.then((response) => {
if (response.ok) {
// возвращаем тело ответа
return response.text();
}
})
// в втором then находится ответ на запрос
.then((text) => {
// выведем данные
div.innerHTML = text;
})
// в catch мы попадём когда fetch() вообще не может выполнить запрос, т.е. нет такого сайта или произошла потеря сетевого соединения
.catch((error) => {
console.log(error);
});
});
</script>
</body>
</html>