Fetch API надає JavaScript для роботи з запитами та відповідями HTTP. Він також надає глобальний метод fetch() , який дозволяє легко та логічно отримувати ресурси через мережу асинхронно. Подібна функціональність раніше досягалася за допомогою XMLHttpRequest. Fetch є найкращою альтернативою, яка може бути легко використана іншими технологіями, такими як Service Workers. Fetch також забезпечує єдине логічне місце визначення інших пов'язаних з HTTP понять, такі як CORS і розширення для HTTP. Зверніть увагу, специфікація fetch відрізняється від jQuery.ajax() в основному в двох пунктах: Базовий запит на отримання даних дійсно простий у налаштуванні. Погляньте на наступний код: Тут ми забираємо JSON файл по мережі та виводимо його вміст у консоль. Найпростіший спосіб використання fetch() полягає у викликі цієї функції з одним аргументом - рядком, що містить шлях до ресурсу, який ви хочете отримати - який повертає promise, що містить відповідь (об'єкт Response). Звичайно, це просто HTTP-відповідь, а не фактичний JSON. Щоб витягти вміст тіла JSON з відповіді, ми використовуємо json() метод (визначений міксином Body, який реалізований в об'єктах Request та Response.) Fetch-запити контролюються за допомогою директиви connect-src (Content Security Policy), а не директивою ресурсів. Метод fetch() може приймати другий параметр - об'єкт init, який дозволяє вам контролювати різні налаштування: З докладним описом функції та повним списком параметрів ви можете ознайомитись на сторінці fetch() . Щоб браузери могли надсилати запит з обліковими даними (навіть для cross-origin запитів), додайте credentials: 'include' в об'єкт init , що передається в метод fetch() : Якщо ви хочете надсилати запит з обліковими даними тільки якщо URL належить одному джерелу (origin) що і скрипт, що викликає, додайте credentials: 'same-origin'. Навпаки, щоб бути впевненим, що облікові дані не передаються із запитом, використовуйте credentials: 'omit': За допомогою fetch() можна надсилати запити POST у форматі JSON. На сервер можна завантажити файл, використовуючи комбінацію HTML-елемента, FormData() та fetch(). На сервер можна завантажити кілька файлів, використовуючи комбінацію HTML-елемента, FormData() та fetch(). Фрагменти даних, отримані з відповіді, не розбиваються на рядки автоматично (принаймні з достатньою точністю) і є не рядками, а об'єктами Uint8Array . Якщо ви хочете завантажити текстовий файл і обробляти його в міру завантаження рядковим чином, то на вас самих лягає тягар відповідальності за обробку всіх згаданих моментів. Як приклад, далі представлений один із способів подібної обробки за допомогою створення рядкового ітератора (для простоти прийняті такі припущення: текст надходить у кодуванні UTF-8 і помилки отримання не обробляються). У методі fetch() promise буде відхилено (reject) з TypeError , коли трапиться помилка мережі або не буде налаштовано CORS на стороні запитуваного сервера, хоча зазвичай це означає проблеми доступу або аналогічні - для прикладу, 404 не є мережевою помилкою. Для достовірної перевірки успішності fetch() включатиме перевірку того, що promise успішний (resolved), потім перевірку того, що значення властивості Response.ok є true. Код виглядатиме приблизно так: Замість передачі шляху ресурсу, який ви хочете запросити викликом fetch(), ви можете створити об'єкт запиту, використовуючи конструктор Request() і передати його в fetch() аргументом: Конструктор Request() приймає такі самі параметри, як і метод fetch(). Ви можете навіть передати існуючий об'єкт запиту для створення його копії: Досить зручно, коли тіла запиту та відповіді використовуються один раз (прим.пер.: "are one use only"). Створення копії, як показано, дозволяє вам використовувати запит/відповідь повторно, при зміні опцій init, при бажанні. Копія повинна бути зроблена до прочитання тіла, а читання тіла у копії також помітить його прочитаним у вихідному запиті. Також є метод clone() , який створює копії. Обидва методи створення копії припинять роботу з помилкою, якщо тіло оригінального запиту або відповіді вже було прочитано, але читання тіла клонованої відповіді або запиту не призведе до маркування оригінальної запиту. Інтерфейс Headers дозволяє створити ваш власний об'єкт заголовків через конструктор Headers() . Об'єкт заголовків - проста мультикарта імен-значень: Це може бути досягнуто шляхом передачі масиву масивів чи літерального об'єкта конструктору: Вміст може бути запитаний та витягнутий: Деякі з цих операцій можуть бути використані тільки в ServiceWorkers , але вони надають зручніший API для маніпуляції заголовками. Всі методи Headers викидають TypeError, якщо ім'я заголовка не є валідним ім'ям HTTP Header. Операції мутації викинуть TypeError якщо є захист від мутації (дивіться нижче) (прим.пер.: "If there is an immutable guard"). Інакше вони перериваються мовчки. Наприклад: Хорошим варіантом використання заголовків є перевірка коректності типу контенту перед його обробкою. Наприклад: З того часу, як заголовки можуть передаватися в запиті, прийматися у відповіді і мають різні обмеження щодо того, яка інформація може і повинна бути змінена, заголовки мають властивість guard. Це не поширюється на Web, але впливає те, які операції мутації доступні об'єкта заголовків. none: за замовчуванням.request: захист об'єкта заголовків, отриманого за запитом ( Request.headers ).request-no-cors: захист об'єкта заголовків, отриманого за запитом створеного з Request.mode no-cors.response: Response.headers ).immutable: в основному використовується в ServiceWorkers; робить об'єкт заголовків read-only. Примітка: ви не можете додати або встановити request захищені Headers' заголовок Content-Length. Аналогічно, вставка Set-Cookie у заголовок відповіді неприпустима: ServiceWorkers не допускають встановлення cookies через синтезовані відповіді. Як ви бачили вище, екземпляр Response буде повернутий, коли fetch() проміс буде виконаний. Властивості об'єкта-відповіді, які найчастіше використовуються: Response.status — Цілочисленне (за промовчанням 200) містить код статусу відповіді. Response.statusText — Рядок (за умовчанням "OK"), який відповідає HTTP коду статусу. Response.ok - як сказано раніше, це коротка властивість для спрощення перевірки на те, що статус відповіді знаходиться десь між 200-299 включно. Це властивість типу Boolean. Вони також можуть бути створені за допомогою JavaScript, але реальна користь від цього є лише при використанні сервіс-воркерів, коли ви надаєте власну відповідь на запит за допомогою методу respondWith() : Конструктор Response() приймає два необов'язкові аргументи - тіло для відповіді та об'єкт init (аналогічний тому, що приймає Request() ) Примітка: Метод error() просто повертає відповідь на помилку. Аналогічно, redirect() повертає відповідь, що призводить до перенаправлення на вказану URL-адресу. Вони також належать лише до Service Workers. Запит та відповідь можуть містити дані тіла. Тіло є екземпляром будь-якого з наступних типів: Body домішка визначає такі методи для вилучення тіла (реалізовані як Request так Response ). Всі вони повертають promise, який зрештою виконується і виводить вміст. Це робить використання нетекстових даних легшим, ніж XMR. У запиті можна встановити параметри для надсилання тіла запиту: Параметри request та response (and by extension the fetch() function) по можливості повертають коректні типи даних. Параметр request також автоматично встановить Content-Type у заголовок, якщо його не було встановлено зі словника. Підтримка Fetch API може бути виявлена шляхом перевірки наявності Headers, Request, Response або fetch () в області видимості Window або Worker. Для прикладу: За допомогою функції fetch ( ) можна надсилати мережні запити на сервер як отримувати, так і надсилати дані. Метод повертає проміс з об'єктом відповіді, де знаходиться додаткова інформація (статус відповіді, заголовки) та відповідь на запит. Скопіювати посилання "Як зрозуміти" Скопійовано Браузер надає глобальний API для роботи із запитами та відповідями HTTP. Раніше для подібної роботи використовувався XMLHttpRequest, проте fetch ( ) більш гнучка та потужна альтернатива. Він зрозуміліший і простіший у використанні через те, що використовує Promise. Скопіювати посилання "Як пишеться" Скопійовано Функція fetch ( ) приймає два параметри: За промовчанням виклик fetch ( ) робить GET-запит за вказаною адресою. Базовий виклик для отримання даних можна записати таким чином: Результатом виклику fetch() буде Promise, в якому міститься спеціальний об'єкт відповіді Response. Цей об'єкт має два важливі для нас поля: У наступному прикладі використовуємо .then ( ) - Обробник результату, отриманого від асинхронної операції. Оброблювач дочекається відповіді від сервера, приймає відповідь, і в даному випадку неявно поверне відповідь, оброблену методом .json ( ). У прикладі функція then поверне інший проміс (їх можна поєднувати). Коли відрізається проміс (r.json()), який повернула функція then, буде викликаний наступний колбек у ланцюжку. За допомогою другого аргументу options можна передати налаштування запиту. Наприклад, можна змінити метод та додати тіло запиту, якщо ми хочемо не отримувати, а надсилати дані. Також у запит можна додати заголовки у вигляді об'єкта або спеціального класу Headers. Скопіювати посилання "Cookies" Скопійовано За промовчанням fetch ( ) запити не включають cookies, і тому авторизовані запити на сервері можуть не пройти. Для цього необхідно додати в налаштування поле credentials: Скопіювати посилання "Обробка помилок" Скопійовано Будь-яка відповідь на запит через fetch ( ) , наприклад, HTTP-код 400, 404 або 500, переводить Promise у стан fulfilled. Проміс перейде у стан rejected тільки якщо запит не трапився через збій мережі або щось завадило виконання fetch ( ) . Щоб обробити помилку запиту, потрібно звертати увагу на поле ok в об'єкті відповіді Response . У разі помилки запиту воно дорівнюватиме false . Скопіювати посилання "На практиці" Скопійовано Скопіювати посилання "Егор Огарков радить" Скопійовано Скопіювати посилання "Скасувати запит" Скопіювати Іноді може виникнути потреба перервати запит, наприклад, коли авторизація користувача прострочена або користувач самостійно хоче скасувати запит (скасував скачування файлу). Запит не виконається, в консолі буде помилка The user aborted a request . Якщо заглянути в інструменти розробника, там можна побачити скасований статус у запиту. Скопіювати посилання "Завантаження файлу на сервер" Скопійовано За допомогою fetch ( ) можна завантажувати файли на сервер, наприклад коли користувач хоче завантажити свій аватар у профіль. Надсилання файлів можна здійснювати за допомогою спеціального об'єкта Form Data. Покажемо на прикладі обробника відправлення форми: Скопіювати посилання "Скачування даних з результатом прогресу" Скопійовано Щоб отримувати поточний прогрес завантаження файлу або будь-яких інших даних, використовуйте властивість body об'єкта Response , який повертається до Promise після виклику fetch ( ) . Поле body є «потоком для читання» (Readable Stream). Це спеціальний об'єкт, який дає можливість отримувати інформацію частинами, у міру її надходження на клієнт. Спробуємо в такий спосіб завантажити миле відео, як білий котик дружить із величезним псом.Використання Fetch
fetch('http://example.com/movies.json') .then((response) => < return response.json(); >) .then((data) => < console.log(data); >);
Встановлення параметрів запиту
// Приклад відправки запиту POST: async function postData(url = "", data = <>) < // Default options є маркованим з * const response = await fetch (url, < method: "POST", // *GET, POST, PUT, DELETE, etc. mode: "cors", // no-cors; -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 in native JavaScript objects > postData("https://example.com/answer", < answer: 42 >).then((data) => < console.log(data); // JSON data parsed by `response.json()` call>);
Надсилання запиту з обліковими даними
fetch("https://example.com", < credentials: "include", >);
// Скрипт, що викликає, належить джерелу 'https://example.com' fetch("https://example.com", < credentials: "same-origin", >);
fetch("https://example.com", < credentials: "omit", >);
Надсилання даних у форматі 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)
Завантаження файлу на сервер
const formData = новий 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", < метод: "PUT", body: formData, >); const result = await response.json(); console.log("Успіх:", JSON.stringify(result)); > catch (error)
Завантаження кількох файлів на сервер
const formData = новий 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)
Обробка текстового файлу рядково
async function* makeTextFileLineIterator(fileURL) < const utf8Decoder = новий 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|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); >> для звільнення (let line of makeTextFileLineIterator(urlOfFile))
Перевірка успішності запиту
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)
Складання об'єкта запиту
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;
const anotherRequest = новий Request(myRequest, myInit);
Заголовки
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")); // []
const myResponse = Response.error(); try < myResponse.headers.set("Origin", "http://mybank.com"); >catch (e)
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)
Захист
const myBody = новий Blob(); addEventListener("fetch", function (event) < // ServiceWorker перехоплює fetch event.respondWith( new Response(myBody, < headers: < "Content-Type": "text/plain" >, >), ); >);
Тіло
const form = New FormData(document.getElementById("login-form")); fetch("/login", < method: "POST", body: form, >);
Функція виявлення
Дивіться також
fetch ( )
Як зрозуміти
Як пишеться
fetch('http://jsonplaceholder.typicode.com/posts')
fetch('http://jsonplaceholder.typicode.com/posts')
fetch('http://jsonplaceholder.typicode.com/posts')
.then((response) => response.json()
// Отримаємо відповідь у вигляді масиву з об'єктів:
// [, , , . ])
fetch('http://jsonplaceholder.typicode.com/posts')
.then((response)
=> response.json()
// Отримаємо відповідь у вигляді масиву з об'єктів:
// [, , , . ]
)
const newPost = title: 'foo',
body: 'bar',
userId: 1,>
fetch('https://jsonplaceholder.typicode.com/posts', method: 'POST', // Тут також можуть бути GET, PUT, DELETE
// Тіло запиту в JSON-форматі
body: JSON.stringify(newPost),
headers: // Додаємо необхідні заголовки
'Content-type': 'application/json; charset = UTF-8 ',
>,>)
.then((response) => response.json())
.then((data) => console.log(data)
// >)
const newPost =
title:
'foo',
body:
'bar',
userId:
1,
>
fetch('https://jsonplaceholder.typicode.com/posts',
метод:
'POST',
// Тут також можуть бути GET, PUT, DELETE
// Тіло запиту в JSON-форматі
body:
JSON.stringify(newPost),
headers:
// Додаємо необхідні заголовки
'Content-type':
'application/json; charset = UTF-8 ',
>,
>)
.then((response)
=> response.json())
.then((data)
=>
console.log(data)
// >
)
Cookies
fetch('https://somesite.com/admin', метод: 'GET',
// Або same-origin, якщо можна робити такі запити
// Тільки в межах цього домену
credentials: 'include',>)
fetch('https://somesite.com/admin',
метод:
'GET',
// Або same-origin, якщо можна робити такі запити
// Тільки в межах цього домену
credentials:
'include',
>)
Обробка помилок
// Запит поверне помилку "404 Not Found"fetch('https://jsonplaceholder.typicode.com/there-is-no-such-route').catch(
() =>
console.log('Error occurred!')
>)// Ніколи не виконається
// Запит поверне помилку "404 Not Found"
fetch('https://jsonplaceholder.typicode.com/there-is-no-such-route').catch(
()
=>
console.log('Error occurred!')
>
)
// Ніколи не виконається
fetch('https://jsonplaceholder.typicode.com/there-is-no-such-route')
.then((response) => // Перевіряємо успішність запиту та викидаємо помилку
if (!response.ok) throw new Error('Error occurred!')
>
return response.json()
>)
// Тепер потрапимо сюди, оскільки викинули помилку
.catch((err) => console.log(err)
>)// Error: Error occurred!
fetch('https://jsonplaceholder.typicode.com/there-is-no-such-route')
.then((response)
=>
// Перевіряємо успішність запиту та викидаємо помилку
if
(!response.ok)
throw
new
Error('Error occurred!')
>
return response.json()
>)
// Тепер потрапимо сюди, оскільки викинули помилку
.catch((err)
=>
console.log(err)
>
)
// Error: Error occurred!
На практиці
Єгор Огарков радить
Скасування запиту
const controller = новий AbortController()
function fetchData() return fetch('http://jsonplaceholder.typicode.com/posts', сигнал: controller.signal,
>)
.then((response) => response.json())
.catch((e) => console.log(e)
>
)>
fetchData()
// Якщо запит ще не виконався, він буде перерваний.// Перерваний fetch поверне Promise з помилкоюcontroller.abort()
const controller =
new
AbortController()
function
fetchData()
return
fetch('http://jsonplaceholder.typicode.com/posts',
signal: controller.signal,
>)
.then((response)
=> response.json())
.catch((e)
=>
console.log(e)
>
)
>
fetchData()
// Якщо запит ще не виконався, він буде перерваний.
// Перерваний fetch поверне Promise з помилкою controller.abort()
Завантаження файлу на сервер
form
id="form">
input
type="file"
id="avatar">
button
type="submit">Завантажитиbutton>
form>
// Знаходимо елемент із файломconst fileInput = document.getElementById('avatar')const form = document.getElementById('form')
function handleSubmit(event) event.preventDefault()
const formData = новий FormData()
// Додаємо файли з інпуту до даних
for (let i = 0; i < fileInput.files.length; i++) const file = fileInput.files[i]
formData.append('avatar', file, file.name)
>
// Надсилаємо файли на сервер
fetch('https://backend.com/api/upload', method: "POST",
body: formData,
>)>
form.addEventListener('submit', handleSubmit)
// Знаходимо елемент із файлом
const fileInput =
document.getElementById('avatar')
const form =
document.getElementById('form')
function
handleSubmit(event)
event.preventDefault()
const formData =
new
FormData()
// Додаємо файли з інпуту до даних
for
(let i =
0; i fileInput.files.length; i++)
const file = fileInput.files[i] formData.append('avatar', file, file.name)
>
// Надсилаємо файли на сервер
fetch('https://backend.com/api/upload',
метод:
"POST",
body: formData,
>)
> form.addEventListener('submit', handleSubmit)
Завантаження даних з результатом прогресу
fetch('https://i.imgur.com/C5QXZ7u.mp4').then(async (response) => let received = 0
// Отримуємо потік у змінну
const reader = response.body.getReader()
// Зчитуємо загальну довжину даних
const contentLength = parseInt(response.headers.get('Content-Length'), 10)
while (true) // Після виклику read() повертається об'єкт, у якому
// done - boolean-значення про те, чи закінчилася інформація,
// value - масив байт, які прийшли цього разу
const < done, value >= await reader.read()
якщо (зроблено) console.log('Отримано 100%')
break
>
received += Math.ceil(contentLength / value.length)
console.log(`Отримано $%`)
>>)
fetch('https://i.imgur.com/C5QXZ7u.mp4').then(async
(response)
=>
let received =
0
// Отримуємо потік у змінну
const reader = response.body.getReader()
// Зчитуємо загальну довжину даних
const contentLength =
parseInt(response.headers.get('Content-Length'),
10)
while
(true)
// Після виклику read() повертається об'єкт, у якому
// done - boolean-значення про те, чи закінчилася інформація,
// value - масив байт, які прийшли цього разу
const
done, value >
=
await reader.read()
if
(done)
console.log('Отримано 100%')
break
> received +=
Math.ceil(contentLength / value.length)
console.log(`Отримано
$received>%`)
>
>)