Полное руководство по XMLHttpRequest
Хватит искать и прочитай это
Прежде всего, давайте разберемся, о чем мы будем говорить.
Что это
Изобретен Microsoft в начале 90-х годов и сокращенно называется XHR, XMLHttpRequest. – это набор API-интерфейсов, которые могут использоваться языками сценариев веб-браузеров, такими как JavaScript, для передачи данных в и из Веб-сервер, использующий HTTP.
XHR можно использовать с протоколами, отличными от HTTP, и данные могут быть в форме не только XML, но и JSON, HTML или >простой текст.
Что вы можете сделать с XHR
Все современные браузеры имеют встроенный объект XMLHttpRequest для запроса данных с сервера. С объектом XHR вы можете:
- Обновить веб-страницу без перезагрузки страницы
- Запросить данные с сервера — после загрузки страницы.
- Получить данные с сервера — после загрузки страницы
- Отправить данные на сервер — в фоновом режиме
Что мы собираемся делать
Мы собираемся использовать поддельный онлайн REST API для тестирования и создания прототипов, например reqres (еще один хороший — JSONPlaceholder), он отлично подходит для учебных пособий, тестирования и примеров кода. В этом посте вы узнаете:
- как получать данные с помощью запросов GET и как отправлять с помощью POST
- Что такое callback и что такое promise (кратко)
- Улучшить наш код, чтобы сделать его более универсальным
- Обработка ошибок запроса
- Визуализируйте полученные данные
Получение данных
Нам нужно создать новый объект xhr, создав экземпляр XMLHttpRequest:
const xhr = new XMLHttpRequest();
это встроенная функция конструктора в вашем браузере, поэтому вам не нужно добавлять какую-либо специальную библиотеку или пакет.
На следующем шаге нам нужно подготовить HttpRequest к отправке, используя функцию, которая принимает два аргумента:
1) используемый HTTP-метод
2) URL-адрес, на который вы хотите отправить отправить этот запрос
xhr.open('GET', 'https:reqres.in/api/users')
Теперь мы можем отправить предварительно настроенный запрос:
xhr.send();
Теоретически это должно быть всем, что нам нужно сделать, фактически запрос отправляется на URL-адрес с использованием метода GET, и мы можем получить ответ, но мы еще не там. Нам нужно использовать ответ.
Небольшая преамбула
Есть два основных способа прослушивания события onload в нашем запросе xhr, а именно:
/*1*/ xhr.addEventListener("load", reqListener);
/*2*/ xhr.onload = () => { };
Мы собираемся использовать второй, так как он имеет более широкую поддержку браузеров.
Таким образом, функция onload сработает, когда мы получим ответ, чтобы получить данные, мы могли бы зарегистрировать ответ следующим образом:
xhr.onload = () => { console.log(xhr.response); };
в этом случае увидит в журнале консоли кучу данных, напечатанных в виде строки, но на самом деле это ответ JSON. Чтобы преобразовать этот ответ в объект Javascript, чтобы манипулировать им, мы можем его проанализировать:
xhr.onload = () => { const data = JSON.parse(xhr.response); console.log(data); };
Другой способ проанализировать ответ JSON — установить для атрибута responseType значение «json»:
xhr.responseType = 'json'; xhr.onload = () => { const data = xhr.response; console.log(data); };
Наш код должен выглядеть так:
const xhr = new XMLHttpRequest(); xhr.open('GET','https://reqres.in/api/users'); xhr.responseType = 'json'; xhr.onload = () => { const data = xhr.response; console.log(data); }; xhr.send();
Отправка данных
Допустим, мы хотим создать нового пользователя, нам нужно будет отправить данные, чтобы указать имя и работу нового пользователя.
Единственное, что изменится, — это вызов xhr.open(), так как нам нужно изменить метод запроса на POST, установить URL-адрес для запроса и добавить данные для пользователя, которого мы хотим создать:
xhr.open('POST','https://reqres.in/api/users',{ "name":"morpheus", "job":"leader" });
reqres.in ответит на наш запрос со статусом 2xx и отправит нам дополнительную информацию об успешном создании пользователя с таким объектом ответа:
createdAt: "2020-02-03T17:10:31.110Z" id: "644"
Что такое обратный вызов
Проще говоря: обратный вызов — это функция, которая должна быть выполнена после завершения выполнения другой функции — отсюда и название «обратный вызов».
— Брэндон Морелли в этой прекрасной статье об обратных вызовах.
Что такое обещание
(Промисы изначально работают только в современных браузерах)
XHR на основе промисов может помочь вам избежать глубоко вложенных обратных вызовов и разрешить цепочку методов с использованием .then()
Вот как может выглядеть традиционная модель обратного вызова:
first(a,function(b){ second(b,function(c){ third(c,function(d){ fourth(d); }); }); });
И как это может выглядеть при использовании подхода, основанного на промисах.
first(a) .then(function(b){ return second(b); }) .then(function(c){ return third(c); }) .then(function(d){ return fourth(d); }) .catch(function (error){ // error at any point of the chain });
довольно яснее, а?
Давайте улучшим код
Хорошо, теперь, когда мы поняли основы, пришло время сделать вещи более профессиональными, создав функцию для получения/отправки данных через HttpRequests:
const sendHttpRequest = (method, url) => { const xhr = new XMLHttpRequest(); xhr.open(method,url); xhr.responseType = 'json'; xhr.onload = () => { const data = xhr.response; console.log(data); }; xhr.send(); };
Еще не там. Давайте добавим обещания.
const sendHttpRequest = (method, url) => { //Adding promise const promise = new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open(method,url); xhr.responseType = 'json'; xhr.onload = () => { resolve(xhr.response); }; xhr.send(); }); //returning the promise return promise }; //Using .then() to retrieve data const getData = () => { sendHttpRequest('GET','https://reqres.in/api/users') .then(responseData => { console.log(responseData); }); };
Мы также можем отправлять POST-запросы, но необходимо внести некоторые изменения в sendHttpRequest:
//adding data parameter const sendHttpRequest = (method, url, data) => { const promise = new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open(method,url); xhr.responseType = 'json'; //signals that we are appending json data in POST if (data){ xhr.setRequestHeader('Content-Type','application/json'); } xhr.onload = () => { resolve(xhr.response); }; //appending json data xhr.send(JSON.stringify(data)); }); return promise }; //Sending POST request const sendData = () => { sendHttpRequest('POST','https://reqres.in/api/users',{ name: "morpheus", job: "leader" }).then(responseData => { console.log(responseData); }) };
Отлично, теперь мы можем вызывать getData() и sendData(), чтобы протестировать их:
Обработка ошибок
Все идет нормально. Но как насчет ошибок?
Прежде всего, прежде чем обрабатывать ошибки xhr, нам нужно понять небольшую разницу: есть два основных типа ошибок:
- Ошибки ответа
Не удалось ответить из-за сетевого подключения или неверного URL-адреса. - Ошибки состояния
На самом деле мы можем получить ответ от сервера, но со статусом ошибки (например, 400); это потому, что сервер не распознал наш запрос (например, мы пропустили параметр в запросе POST).
Давайте изменим наш код для обработки этих ошибок:
const sendHttpRequest = (method, url, data) => { const promise = new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open(method,url); xhr.responseType = 'json'; if (data){ xhr.setRequestHeader('Content-Type','application/json'); } xhr.onload = () => { //rejecting promise on status error if(xhr.status >= 400) { reject(xhr.response); } else { resolve(xhr.response); } }; //rejecting promise on response errors xhr.onerror = () => { reject('Something went wrong!'); }; xhr.send(JSON.stringify(data)); }); return promise }; const sendData = () => { sendHttpRequest('POST','https://reqres.in/api/users',{ name: "morpheus", job: "leader" }).then(responseData => { console.log(responseData); //catching and printing errors }).catch(err => { console.log(err); }); };
Теперь, если мы отключим интернет-соединение и попытаемся вызвать sendData(), мы получим в журнале something went wrong!
, то же самое, если мы попытаемся достичь недопустимого URL-адреса.
В зависимости от запроса POST, если мы попытаемся опустить поле в отправляемых данных:
const sendData = () => { sendHttpRequest('POST', 'https://reqres.in/api/register', { email: '[email protected]' // password: 'pistol' }).then(responseData => { console.log(responseData); }).catch(err => { console.log(err); }); };
мы получим ошибку статуса error: "Missing email or username"
Визуализируйте полученные данные
Вы можете просто визуализировать данные, используя функцию, которая добавляет элементы DOM.
// Visualize in HTML the data from GET requests const visualize = (data) => { // Referring to an existing element in HTML code // (in this case <div id="data-from-xhr"></div>) let div = document.getElementById('data-from-xhr') // Clear div element div.innerHTML = '' // For every data element: data.map(userData => { // Create DOM elements let user = document.createElement('div') let avatar = document.createElement('img') let email = document.createElement('p') let fullName = document.createElement('p') // Fill DOM elements with passed data avatar.src=userData.avatar email.innerText = userData.email fullName.innerText = userData.first_name + userData.last_name // Create a user node each one with avatar, email and fullName user.appendChild(avatar) user.appendChild(email) user.appendChild(fullName) // Append the user node to div in HTML div.appendChild(user) }) };
с некоторыми css это должно выглядеть так:
Немного конфет для вас
Состояния запроса:
мы можем отслеживать состояние запроса, используя событие onreadystatechange
:
xhr.onreadystatechange = function () {
if(xhr.readyState == 1) {
console.log('Request started.');
}
if(xhr.readyState == 2) {
console.log('Headers received.');
}
if (xhr.readyState == 3) {
console.log('Data loading..!');
}
if (xhr.readyState == 4) {
console.log('Request ended.');
}
}
;
Прервать запрос
прервать запрос XHR в любое время, вызвав метод abort()
:
xhr.abort(); // cancel request
Событие выполнения
Запускается периодически во время загрузки ответов и может использоваться для отчета о ходе выполнения тяжелых сетевых запросов.
xhr.onprogress = (event) => {
// event.loaded returns how many bytes are downloaded
// event.total returns the total number of bytes
// event.total only if server sends `Content-Length` header
console.log(`Downloaded ${event.loaded} of ${event.total}`);
}
Хотите реальный пример кода?
Вот мой репозиторий на github о XHR.
использованная литература
youtube.com: Отправка HTTP-запросов Javascript с помощью XMLHttpRequest
codeburst.io: JavaScript: что такое обратный вызов?
gomakethings.com : XHR на основе обещаний
attacomsian.com: Выполнение HTTP-запросов с использованием XMLHttpRequest (XHR)
w3schools. com: XML HttpRequest
developer.mozilla.org: XMLHttpRequest
wikipedia.org: XMLHttpRequest