Объект XMLHttpRequest
Код javascript может взаимодействовать с каким-нибудь ресурсом в сети интернет, например с сервером. Для взаимодействия кода javascript с сервером обычно применяется такая технология как Ajax
. Ajax
представляет технологию для отправки запросов к серверу из клиентского кода JavaScript без перезагрузки страницы. Сам термин расшифровывается как Asynchronous JavaScript And XML
. То есть изначально AJAX предполагал асинхронное взаимодействие клиента и сервера посредством данных в формате XML
. Хотя сейчас XML
во многом вытеснил формат JSON
, в любом случае AJAX
революционизировал веб-среду, позволив создавать динамичные отзывчивые веб-приложения.
Для создания приложений использующих Ajax
, применяются различные способы. Но самым распространенным способом является использование объекта XMLHttpRequest
:
let xhr = new XMLHttpRequest();
После создания объекта XMLHttpRequest
можно отправлять запросы к серверу, для начала надо вызвать метод open()
для инициализации:
xhr.open(method, url, async, user, password)
Эта функция принимает пять параметров, из которых первые два являются обязательными:
method
тип запросаGET
,POST
,PUT
,DELETE
и т.д.url
адрес ресурса, к которому отправляется запросasync
логическое значение которое указывает будет ли запрос асинхронным. Если значениеtrue
(значение по умолчанию), то запрос асинхронный. Синхронный и асинхронный режим отличаются тем, что запрос в синхронном режиме пока запрос не выполнится, остальной код javascript не может выполняться. Если запрос отправляется в асинхронном режиме, то параллельно с выполнением запроса можно выполнять также и другой код javascript. И в большинстве случаев, как правило, используется именно асинхронный режимuser
имя пользователя которое применяется при его аутентификации на сервере (то есть для определения, какой именно пользователь осуществил запрос), по умолчанию равноnull
password
пароль пользователя, который применяется при его аутентификации на сервере, по умолчанию равноnull
Например, запрос типа GET
на файл исполнитель xmlhttprequest.php
:
xhr.open("GET", "xmlhttprequest.php");
После инициализации запроса методом open()
можно отправить запрос с помощью метода send()
. В body
находится тело запроса. Не у всякого запроса есть тело, например у GET
тела нет, у POST
основные данные как раз передаются через body
:
xhr.send(body)
Вызов метода abort()
прерывает выполнение запроса:
xhr.abort()
Ответы XMLHttpRequest
Объект XMLHttpRequest
имеет ряд свойств, которые позволяют проконтролировать выполнение запроса:
status
содержит статусный код ответаHTTP
, который пришел от сервера. С помощью статусного кода можно судить об успешности запроса или об ошибках, которые могли бы возникнуть при его выполнении. Например, статусный код200
указывает на то, что запрос прошел успешно. Код403
говорит о необходимости авторизации для выполнения запроса, а код404
сообщает, что ресурс не найден и так далееstatusText
Текстовое описание статуса от сервераOK
,Not Found
,Forbidden
и так далееresponseType
возвращает тип ответа, есть следующие типы:
""
пустая строка
arraybuffer
ответ представляет объектArrayBuffer
, которые содержит бинарные данные
blob
ответ представляет объектBlob
, которые содержит бинарные данные
document
ответ представляет документHTML/XML
json
ответ представляет данные в форматеjson
text
ответ представляет текстresponse
возвращает ответ сервераresponseText
возвращает ответа сервера в виде текстаresponseXML
возвращаетXML/HTML
, если ответ от сервера в форматеXML/HTML
Синхронные и асинхронные запросы
Если в методе open
установить параметр async
равным false
, то запрос будет синхронным.
Синхронные вызовы используются чрезвычайно редко, так как блокируют взаимодействие со страницей до окончания загрузки. Посетитель не может даже прокручивать её. Никакой JavaScript не может быть выполнен, пока синхронный вызов не завершён – в общем, в точности те же ограничения как alert
.
Если синхронный вызов занял слишком много времени, то браузер предложит закрыть «зависшую» страницу.
Из-за такой блокировки получается, что нельзя отослать два запроса одновременно. Кроме того, забегая вперёд, заметим, что ряд продвинутых возможностей, таких как возможность делать запросы на другой домен и указывать таймаут, в синхронном режиме не работают.
Из всего вышесказанного уже должно быть понятно, что синхронные запросы используются чрезвычайно редко, а асинхронные – почти всегда.
Для того, чтобы запрос стал асинхронным, укажем параметр async
равным true
.
let xhr = new XMLHttpRequest();
xhr.open("GET", "xmlhttprequest.php", true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) return;
if (xhr.status != 200) {
// обработать ошибку
alert(xhr.status + ": " + xhr.statusText);
} else {
// вывести результат
alert(xhr.responseText);
}
};
Если в open
указан третий аргумент true
(или если третьего аргумента нет), то запрос выполняется асинхронно. Это означает, что после вызова xhr.send()
код не «зависает», а преспокойно продолжает выполняться, а результат приходит через событие xhr.onreadystatechange
.
Событие readystatechange
Событие readystatechange
происходит несколько раз в процессе отсылки и получения ответа. При этом можно посмотреть «текущее состояние запроса» в свойстве xhr.readyState
.
В примере выше мы использовали только состояние 4
(запрос завершён), но есть и другие.
Все состояния, по спецификации:
const unsigned short UNSENT = 0; // начальное состояние
const unsigned short OPENED = 1; // вызван open
const unsigned short HEADERS_RECEIVED = 2; // получены заголовки
const unsigned short LOADING = 3; // загружается тело (получен очередной пакет данных)
const unsigned short DONE = 4; // запрос завершён
Запрос проходит их в порядке 0
-> 1
-> 2
-> 3
-> … -> 3
-> 4
, состояние 3
повторяется при каждом получении очередного пакета данных по сети.
HTTP-заголовки
XMLHttpRequest
умеет как указывать свои заголовки в запросе, так и читать присланные в ответ. Для работы с HTTP-заголовками есть 3 метода:
setRequestHeader
setRequestHeader(name, value)
, устанавливает заголовок name
запроса со значением value
:
xhr.setRequestHeader('Content-Type', 'application/json');
Нельзя установить заголовки, которые контролирует браузер, например Referer
или Host
и ряд других (полный список тут).
Особенностью XMLHttpRequest
является то, что отменить setRequestHeader
невозможно. Повторные вызовы лишь добавляют информацию к заголовку, например:
xhr.setRequestHeader('X-Auth', '123');
xhr.setRequestHeader('X-Auth', '456');
// в результате будет заголовок:
// X-Auth: 123, 456
getResponseHeader
getResponseHeader(name)
, возвращает значение заголовка ответа name
, кроме Set-Cookie
и Set-Cookie2
:
xhr.getResponseHeader('Content-Type');
getAllResponseHeaders
getAllResponseHeaders()
, возвращает все заголовки ответа, кроме Set-Cookie
и Set-Cookie2
. Заголовки возвращаются в виде единой строки. Между заголовками стоит перевод строки в два символа "\r\n"
(не зависит от ОС), значение заголовка отделено двоеточием с пробелом ": "
. Этот формат задан стандартом. Таким образом, если хочется получить объект с парами заголовок-значение, то эту строку необходимо разбить и обработать:
Cache-Control: max-age=31536000
Content-Length: 4260
Content-Type: image/png
Date: Sat, 08 Sep 2012 16:53:16 GMT
Таймаут
Максимальную продолжительность асинхронного запроса можно задать свойством timeout
:
xhr.timeout = 30000; // 30 секунд (в миллисекундах)
При превышении этого времени запрос будет оборван и сгенерировано событие ontimeout
:
xhr.ontimeout = function() {
alert( 'Извините, запрос превысил максимальное время' );
}
Полный список событий
Современная спецификация предусматривает следующие события по ходу обработки запроса:
loadstart
запрос начатprogress
браузер получил очередной пакет данных, можно прочитать текущие полученные данные вresponseText
abort
запрос был отменён вызовомxhr.abort()
error
произошла ошибкаload
запрос был успешно (без ошибок) завершёнtimeout
запрос был прекращён по таймаутуloadend
запрос был завершён (успешно или неуспешно)
Используя эти события можно более удобно отслеживать загрузку (onload
) и ошибку (onerror
), а также количество загруженных данных (onprogress
).
Ранее мы видели ещё одно событие – readystatechange
. Оно появилось гораздо раньше, ещё до появления текущего стандарта.
Пример использования асинхронно
function loadPhones() {
let xhr = new XMLHttpRequest();
xhr.open("GET", "xmlhttprequest.php", true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) return;
if (xhr.status != 200) {
// обработать ошибку
alert(xhr.status + ": " + xhr.statusText);
} else {
// вывести результат
alert(xhr.responseText);
}
};
}
Пример использования синхронно
function loadPhones() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'xmlhttprequest.php', false);
xhr.send();
if (xhr.status != 200) {
// обработать ошибку
alert('Ошибка ' + xhr.status + ': ' + xhr.statusText);
} else {
// вывести результат
alert(xhr.responseText);
}
}