Что такое ajax fetch
Перейти к содержимому

Что такое ajax fetch

  • автор:

Основы работы с 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; >); >);

Даны три кнопки и див. На сервере даны три страницы. Сделайте так, чтобы каждая из кнопок подгружала в див соответствующую страницу.

Пусть на сервере есть пять страниц. Пусть первое нажатие на кнопку подгружает первую страницу, второе нажатие — вторую, и так далее, пока страницы не закончатся.

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 .

    В песочнице есть тестовый пример.

    1. На каждого пользователя должен приходиться один запрос fetch .
    2. Запросы не должны ожидать завершения друг друга. Надо, чтобы данные приходили как можно быстрее.
    3. Если какой-то запрос завершается ошибкой или оказалось, что данных о запрашиваемом пользователе нет, то функция должна возвращать 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 – дополнительные параметры, такие как метод, заголовки и т.д.

    warning_icon

    Где посмотреть 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 (); // читать тело ответа в формате JSON

    warning_icon

    Fetch работает на основе promise-ов, поэтому для работы в Internet Explorer нужно определять полифил.

    Для GET-запроса параметры options не нужны.

    Процесс получения ответа происходит в два этапа:

    1 — выполняется запрос, который возвращает объект класса Response. На этом этапе можно проверить статус HTTP-запроса и определить, выполнился ли он успешно, а также посмотреть заголовки, но пока без тела ответа.

    У Response есть свойства:

    • status – код статуса HTTP-запроса (например, 200 — код успешного выполнения),

    • ok – логическое значение, равное true если код HTTP-статуса в диапазоне 200-299.

    warning_icon

    Для 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.

    warning_icon

    Можно использовать только один метод чтения ответа. Если уже был получен ответ с помощью 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 );

    warning_icon

    Поскольку 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(‘Задача неактивна’);
    >
    >);
    .

    1. Asynchronous JavaScript and XML

    AJAX — метод отправки или запроса данных с последующим обновлением интерфейса по этим данным, без необходимости перезагрузки страницы. За счет этого уменьшается время отклика и веб-страница становится более интерактивной.

    Несмотря на то, что в названии технологии присутствует XML, использовать его вовсе не обязательно. Под AJAX подразумевают любое общение с сервером без перезагрузки страницы.

    В пример можно привести подгрузку данных:

    • На веб-странице происходит событие (страница загружается, нажимается кнопка «Показать больше», отправляется форма и т. п.).
    • На клиенте, с помощью JavaScript, реакцией на это событие выполнятся функция для работы с сервером, где создается и отправляется HTTP-запрос.
    • Сервер получает и обрабатывает HTTP-запрос, отправляя обратно в ответе данные в JSON-формате.
    • На клиенте, с помощью JavaScript, ответ от сервера обрабатывается, считываются данные и обновляется интерфейс.

    2. Fetch API

    Fetch API — предоставляет интерфейс, набор методов и свойств для отправки, получения и обработки ресурсов от сервера.

    Это XMLHttpRequest нового поколения. Он предоставляет улучшенный интерфейс для составления запросов к серверу и построен на обещаниях (promise).

    fetch(url, options) 
    • url — обязательный параметр — путь к данным которые вы хотите получить.
    • options — необязательный параметр — объект настроек запроса. Содержит служебную информацию: метод (по умолчанию GET ), заголовки, тело и т. д.
    • Возвращает промис, который содержит ответ сервера.
    fetch('https://jsonplaceholder.typicode.com/users') .then(response =>  //response handling >) .then(data =>  // data handling >) .catch(error =>  // error handling >); 

    2.1. Response

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

    В зависимости от типа получаемого контента, используется различный метод парса.

    • Response.prototype.json() — используется, когда от бекенда ожидаются данные в JSON-формате.
    • Response.prototype.text() — используется, когда от бекенда ожидаются данные в простом текстовом формате, например .csv — формат хранения табличных данных в виде текстового файла.
    • Response.prototype.blob() — используется, когда от бекенда ожидаются данные, описывающие файл, например изображение, аудио или видео.
    fetch('https://jsonplaceholder.typicode.com/users') .then(response =>  return response.json(); >) .then(data =>  // data handling >) .catch(error =>  // error handling >); 

    2.2. Headers

    Headers — позволяет выполнять различные действия в заголовках HTTP-запроса и ответа. Эти действия включают в себя извлечение, настройку, добавление и удаление заголовков (методы append , has , get , set , delete ).

    const headers = new Headers( 'Content-Type': 'application/json', 'X-Custom-Header': 'custom value', >); headers.append('Content-Type', 'text/bash'); headers.append('X-Custom-Header', 'custom value'); headers.has('Content-Type'); // true headers.get('Content-Type'); // "text/bash" headers.set('Content-Type', 'application/json'); headers.delete('X-Custom-Header'); 

    Для составления заголовков запроса можно использовать просто литерал объекта со свойствами. В таком случае методов заголовков не будет, что зачастую и не требуется.

    const headers =  'Content-Type': 'application/json', 'X-Custom-Header': 'custom value', >; 

    Запрос с использованием заголовков может выглядеть так.

    fetch('https://jsonplaceholder.typicode.com/users',  method: 'GET', headers:  Accept: 'application/json', >, >).then(response => >); 

    2.3. Ошибка при работе с асинхронным кодом

    Разберем распространенную ошибку начинающих разработчиков, попытку использовать данные вне метода then , используем для этого предыдущий пример.

    3. Вкладка Network

    На вкладке Network отображаются все выполняющиеся на странице HTTP-запросы. Выбрав XHR (можно фильтровать по типу запроса), останутся только запросы к бекенду, и после нажатия кнопки, через некоторое время, отобразится запрос. Выбрав его, можно посмотреть служебную информацию и тело ответа на вкладках Headers , Preview , Response и Timing .

    Вкладка Network

    4. Работа с публичным RESTful API

    Каждый API уникален, их тысячи, невозможно заучить код для работы с одним сервисом и использовать его для общения с другим. С другой стороны, RESTful API построены по принципам REST-архитектуры, а это значит, что можно понять принцип и методы работы, после чего, все что нужно сделать — это ознакомиться с документацией того сервиса, который необходимо использовать.

    Для примера был выбран API ПриватБанка. Из документации берем URL для запроса информации о текущем курсе обмена валют.

    https://api.privatbank.ua/p24api/pubinfo?exchange&json&coursid=11 
    • https://api.privatbank.ua/p24api — endpoint, base URL, точка входа на API.
    • /pubinfo — ресурс, к которому мы обращаемся
    • ? — говорит о том, что дальше идут параметры запроса
    • & — используется для указания смыслового И , связывает параметры запроса

    Параметры ответа из документации:

    • ccy — код валюты
    • base_ccy — код национальной валюты
    • buy — курс покупки
    • sale — курс продажи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *