Использование XMLHttpRequest
Базовая Широко доступная *
Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна во всех браузерах с июля 2015 года.
* Некоторые части этой функции могут иметь различный уровень поддержки.
В этом руководстве мы рассмотрим, как использовать XMLHttpRequest для отправки HTTP запросов, чтобы обмениваться данными между веб-сайтом и сервером.
Включены примеры как распространённых, так и менее распространённых случаев использования XMLHttpRequest.
Для отправки HTTP запроса:
- Создать объект
XMLHttpRequest - Открыть URL
- Отправить запрос.
После завершения транзакции объект XMLHttpRequest будет содержать полезную информацию, такую как тело ответа и код состояния HTTP результата.
function reqListener() {
console.log(this.responseText);
}
const req = new XMLHttpRequest();
req.addEventListener("load", reqListener);
req.open("GET", "http://www.example.org/example.txt");
req.send();
Типы запросов
Запрос, выполненный через XMLHttpRequest , может извлечь данные двумя способами: асинхронно или синхронно. Тип запроса определяется необязательным аргументом async (третьим аргументом), который устанавливается в методе XMLHttpRequest.open(). Если этот аргумент true или не указан, обработка XMLHttpRequest выполняется асинхронно, в противном случае — синхронно. Подробное обсуждение и демонстрации этих двух типов запросов можно найти на странице синхронных и асинхронных запросов. Синхронные запросы нельзя использовать вне веб-рабочих процессов, так как они блокируют основное интерфейс.
Примечание: Конструктор XMLHttpRequest не ограничен только XML-документами. Он начинается с "XML", потому что когда он был создан, основной формат, который изначально использовался для асинхронного обмена данными, был XML.
Обработка ответов
Существует несколько типов атрибутов ответа, определённых для конструктора XMLHttpRequest(). Они сообщают клиенту, выполняющему XMLHttpRequest важную информацию о состоянии ответа. Некоторые случаи, когда требуется работа с типами ответов, отличными от текста, могут включать некоторую манипуляцию и анализ, описанные в следующих разделах.
Анализ и изменение свойства responseXML
Если вы используете XMLHttpRequest для получения содержимого удалённого XML-документа, свойство responseXML будет объектом DOM, содержащим распарсенный XML-документ. Это может оказаться сложным для обработки и анализа. Существует четыре основных способа анализа этого XML-документа:
- Использование XPath для адресации (или указания) его частей.
- Ручное парсирование и сериализация XML в строки или объекты.
- Использование
XMLSerializerдля сериализации деревьев DOM в строки или файлы. -
RegExpможет быть использован, если вам заранее известно содержимое XML-документа. Вы можете захотеть удалить символы перевода строки, если используетеRegExpдля сканирования с учётом перевода строк. Однако этот метод является "методом последней инстанции", так как при небольшом изменении XML-кода он, скорее всего, потерпит неудачу.
Примечание: XMLHttpRequest теперь может интерпретировать HTML, используя свойство responseXML. Прочитайте статью о HTML в XMLHttpRequest, чтобы узнать, как это сделать.
Обработка свойства responseText содержащего HTML-документ
Если вы используете XMLHttpRequest для получения содержимого удалённой HTML-веб-страницы, свойство responseText — это строка, содержащая исходный HTML. Это может оказаться сложным для обработки и анализа. Существует три основных способа анализа и парсинга этой исходной HTML-строки:
- Используйте свойство
XMLHttpRequest.responseXML, как описано в статье HTML в XMLHttpRequest. - Вставьте содержимое в тело фрагмента документа document fragment через
fragment.body.innerHTMLи пройдитесь по DOM фрагмента. -
RegExpможет быть использован, если вам заранее известно содержимое HTML-responseText. Вы можете захотеть удалить символы перевода строки, если используетеRegExpдля сканирования с учётом перевода строк. Однако этот метод является "методом последней инстанции", так как при небольшом изменении HTML-кода он, скорее всего, потерпит неудачу.
Обработка бинарных данных
Хотя XMLHttpRequest чаще всего используется для отправки и получения текстовых данных, он может использоваться для отправки и получения бинарного содержимого. Существует несколько хорошо проверенных методов принуждения ответа XMLHttpRequest к отправке бинарных данных. Они включают использование метода overrideMimeType() объекта XMLHttpRequest и являются работоспособным решением.
const req = new XMLHttpRequest();
req.open("GET", url);
// retrieve data unprocessed as a binary string
req.overrideMimeType("text/plain; charset=x-user-defined");
/* … */
Однако доступны более современные методы, так как атрибут responseType теперь поддерживает ряд дополнительных типов содержимого, что делает отправку и получение бинарных данных намного проще.
Например, рассмотрите этот фрагмент кода, который использует свойство responseType объекта "arraybuffer" для получения удалённого содержимого в объект ArrayBuffer, который хранит исходные бинарные данные.
const req = new XMLHttpRequest();
req.onload = (e) => {
const arraybuffer = req.response; // not responseText
/* … */
};
req.open("GET", url);
req.responseType = "arraybuffer";
req.send();
Для получения дополнительных примеров обратитесь к странице Отправка и приём бинарных данных.
Мониторинг прогресса
XMLHttpRequest предоставляет возможность прослушивания различных событий, которые могут произойти во время обработки запроса. Это включает периодические уведомления о прогрессе, уведомления об ошибках и так далее.
Поддержка мониторинга событий DOM progress для XMLHttpRequest переводов соответствует спецификации для событий прогресса: эти события реализуют интерфейс ProgressEvent. Фактические события, которые вы можете отслеживать, чтобы определить состояние текущей передачи:
progress-
Изменилось количество полученных данных.
load-
Передача завершена; все данные теперь в
response
const req = new XMLHttpRequest();
req.addEventListener("progress", updateProgress);
req.addEventListener("load", transferComplete);
req.addEventListener("error", transferFailed);
req.addEventListener("abort", transferCanceled);
req.open();
// …
// progress on transfers from the server to the client (downloads)
function updateProgress(event) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
// …
} else {
// Unable to compute progress information since the total size is unknown
}
}
function transferComplete(evt) {
console.log("The transfer is complete.");
}
function transferFailed(evt) {
console.log("An error occurred while transferring the file.");
}
function transferCanceled(evt) {
console.log("The transfer has been canceled by the user.");
}
Мы добавляем обработчики событий для различных событий, которые отправляются при выполнении передачи данных с помощью XMLHttpRequest.
Примечание: Вам необходимо добавить обработчики событий до вызова open() для запроса. В противном случае события progress не будут срабатывать.
Обработчик события progress, определённый функцией updateProgress() в данном примере, получает общее количество байт для передачи и количество переданных байтов в полях total и loaded события. Однако, если поле lengthComputable ложно, общий размер неизвестен и будет равен нулю.
События прогресса существуют как для загрузки, так и для отправки. События загрузки срабатывают на самом объекте XMLHttpRequest , как показано в примере выше. События отправки срабатывают на объекте XMLHttpRequest.upload , как показано ниже:
const req = new XMLHttpRequest();
req.upload.addEventListener("progress", updateProgress);
req.upload.addEventListener("load", transferComplete);
req.upload.addEventListener("error", transferFailed);
req.upload.addEventListener("abort", transferCanceled);
req.open();
Примечание: События прогресса недоступны для протокола file:.
События progress появляются для каждого блока полученных данных, включая последний блок в тех случаях, когда последний пакет получен, а соединение закрыто до срабатывания события progress. В этом случае событие progress автоматически срабатывает при возникновении события load для этого пакета. Это позволяет вам надёжно отслеживать прогресс, следя только за событием "progress".
Можно также определить все три условия завершения загрузки (abort, load, или error ) с помощью события loadend:
req.addEventListener("loadend", loadEnd);
function loadEnd(e) {
console.log(
"The transfer finished (although we don't know if it succeeded or not).",
);
}
Обратите внимание, что из информации, полученной событием loadend , невозможно определить, какое условие привело к завершению операции; однако вы можете использовать это для обработки задач, которые необходимо выполнить во всех сценариях завершения передачи.
Получение даты последнего изменения
function getHeaderTime() {
console.log(this.getResponseHeader("Last-Modified")); // A valid GMTString date or null
}
const req = new XMLHttpRequest();
req.open(
"HEAD", // use HEAD when you only need the headers
"your-page.html",
);
req.onload = getHeaderTime;
req.send();
Выполнение действий при изменении даты последнего изменения
Создадим две функции:
function getHeaderTime() {
const lastVisit = parseFloat(
window.localStorage.getItem(`lm_${this.filepath}`),
);
const lastModified = Date.parse(this.getResponseHeader("Last-Modified"));
if (isNaN(lastVisit) || lastModified > lastVisit) {
window.localStorage.setItem(`lm_${this.filepath}`, Date.now());
isFinite(lastVisit) && this.callback(lastModified, lastVisit);
}
}
function ifHasChanged(URL, callback) {
const req = new XMLHttpRequest();
req.open("HEAD" /* use HEAD - we only need the headers! */, URL);
req.callback = callback;
req.filepath = URL;
req.onload = getHeaderTime;
req.send();
}
И для тестирования:
// Let's test the file "your-page.html"
ifHasChanged("your-page.html", function (modified, visit) {
console.log(
`The page '${this.filepath}' has been changed on ${new Date(
nModified,
).toLocaleString()}!`,
);
});
Если вы хотите узнать, изменилась ли текущая страница, обратитесь к статье о document.lastModified.
Межсайтовый XMLHttpRequest
Современные браузеры поддерживают межсайтовые запросы, реализуя стандарт Cross-Origin Resource Sharing (CORS). Пока сервер настроен на разрешение запросов с источника вашего веб-приложения, XMLHttpRequest будет работать. В противном случае будет выброшено исключение INVALID_ACCESS_ERR.
Обход кэша
Подход, совместимый с различными браузерами, для обхода кэша заключается в добавлении отметки времени к URL, обязательно используя «?» или «&», как нужно. Например:
http://example.com/bar.html -> http://example.com/bar.html?12345 http://example.com/bar.html?foobar=baz -> http://example.com/bar.html?foobar=baz&12345
Так как локальный кэш индексируется по URL, каждый запрос становится уникальным, тем самым обходя кэш.
Вы можете автоматически изменять URL с помощью следующего кода:
const req = new XMLHttpRequest();
req.open("GET", url + (/\?/.test(url) ? "&" : "?") + new Date().getTime());
req.send(null);
Безопасность
Рекомендуемый способ включения межсайтового сценария – использование заголовка Access-Control-Allow-Origin HTTP в ответе на XMLHttpRequest.
Остановленные XMLHttpRequests
Если вы получаете в XMLHttpRequest значения status=0 и statusText=null, это означает, что запрос не был разрешен к выполнению. Он был UNSENT. Вероятной причиной является изменение XMLHttpRequest источника (при создании XMLHttpRequest) после того, как XMLHttpRequest был open(). Такая ситуация может возникнуть, например, когда XMLHttpRequest запускается при событии onunload для окна, ожидаемый XMLHttpRequest создаётся, когда закрываемое окно ещё существует, и, наконец, отправляет запрос (иными словами, open()) после того, как это окно потеряло фокус, а другой получил фокус. Наиболее эффективным способом избежать этой проблемы является установка обработчика события DOMActivate в новом окне, который устанавливается после того, как для закрываемого окна сработает событие unload.
Спецификации
| Спецификация |
|---|
| XMLHttpRequest # interface-xmlhttprequest |
Совместимость с браузерами
| Настольный | Мобильный | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox for Android | Opera Android | Safari на IOS | Samsung Internet | WebView Android | |
XMLHttpRequest |
1 | 12 | 1 | ≤12.1 | 3 | 18 | 4 | ≤12.1 | 1 | 1.0 | 4.4 |
Using_XMLHttpRequest |
1 | 12 | 1 | 8 | 1.2 | 18 | 4 | 10.1 | 1 | 1.0 | ≤37 |
abort |
1 | 12 | 1 | ≤12.1 | 1.2 | 18 | 4 | ≤12.1 | 1 | 1.0 | 4.4 |
abort_event |
1 | 12 | 3.5 | ≤12.1 | 1.3 | 18 | 4 | ≤12.1 | 1 | 1.0 | ≤37 |
authorization_removed_cross_origin |
Нет | Нет | 111 | Нет | 16.1 | Нет | 111 | Нет | 16.1 | Нет | Нет |
error_event |
1 | 12 | 1 | ≤12.1 | 1.3 | 18 | 4 | ≤12.1 | 1 | 1.0 | ≤37 |
getAllResponseHeaders |
1 | 12 | 1Начиная с Firefox 49, пустые заголовки возвращаются как пустые строки, если параметрnetwork.http.keep_empty_response_headers_as_empty_string установлен в true, по умолчанию false. До Firefox 49 пустые заголовки игнорировались. Начиная с Firefox 50, параметр по умолчанию true. |
≤12.1 | 1.2 | 18 | 4Начиная с Firefox for Android 49, пустые заголовки возвращаются как пустые строки, если параметрnetwork.http.keep_empty_response_headers_as_empty_string установлен в true, по умолчанию false. До Firefox for Android 49 пустые заголовки игнорировались. Начиная с Firefox for Android 50, параметр по умолчанию true. |
≤12.1 | 1 | 1.0 | 4.4 |
getResponseHeader |
1 | 12 | 1Начиная с Firefox 49, пустые заголовки возвращаются как пустые строки, если параметрnetwork.http.keep_empty_response_headers_as_empty_string установлен в true, по умолчанию false. До Firefox 49 пустые заголовки игнорировались. Начиная с Firefox 50, параметр по умолчанию true. |
8 | 1.2 | 18 | 4Начиная с Firefox for Android 49, пустые заголовки возвращаются как пустые строки, если параметрnetwork.http.keep_empty_response_headers_as_empty_string установлен в true, по умолчанию false. До Firefox for Android 49 пустые заголовки игнорировались. Начиная с Firefox for Android 50, параметр по умолчанию true. |
10.1 | 1 | 1.0 | 4.4 |
load_event |
1 | 12 | 1 | ≤12.1 | 1.3 | 18 | 4 | ≤12.1 | 1 | 1.0 | ≤37 |
loadend_event |
18 | 12 | 5 | ≤12.1 | 4 | 18 | 5 | ≤12.1 | 3 | 1.0 | 4.4 |
loadstart_event |
1 | 12 | 3.5 | ≤12.1 | 1.3 | 18 | 4 | ≤12.1 | 1 | 1.0 | 4.4 |
open |
1 | 12 | 1Начиная с Firefox 30, синхронные запросы в основном потоке устарели из-за их негативного влияния на производительность и пользовательский опыт. Поэтому параметрasync может быть не false за исключением Worker. |
8 | 1.2 | 18 | 4Начиная с Firefox for Android 30, синхронные запросы в основном потоке устарели из-за их негативного влияния на производительность и пользовательский опыт. Поэтому параметрasync может быть не false за исключением Worker. |
10.1 | 1 | 1.0 | 4.4 |
overrideMimeType |
1 | 12 | 1 | ≤12.1 | 1.2 | 18 | 4 | ≤12.1 | 1 | 1.0 | 4.4 |
progress_event |
1 | 12 | 1 | ≤12.1 | 3 | 18 | 4 | ≤12.1 | 1 | 1.0 | ≤37 |
readyState |
1 | 12 | 1 | 8 | 1.2 | 18 | 4 | 10.1 | 1 | 1.0 | 4.4 |
readystatechange_event |
1 | 12 | 1 | 9 | 1.2 | 18 | 4 | 10.1 | 1 | 1.0 | 4.4 |
response |
9 | 12 | 6 | 11.6 | 5.1 | 18 | 6 | 12 | 5 | 1.0 | 3 |
responseText |
1 | 12 | 1 | 8 | 1.2 | 18 | 4 | 10.1 | 1 | 1.0 | 4.4 |
responseType |
31 | 12 | 6 | 1812–15 | 5.1 | 31 | 50 | 1812–14 | 5 | 2.0 | 4.4.3 |
responseURL |
37 | 14 | 32 | 24 | 8 | 37 | 32 | 24 | 8 | 3.0 | 37 |
responseXML |
1 | 12 | 1Перед Firefox 51, при ошибке парсинга полученных данных добавлялся узел<parsererror> в начало Document, а затем возвращалось значение Document в любом состоянии. Это не соответствовало спецификации. Начиная с Firefox 51, в этом случае теперь правильно возвращается значение null в соответствии со спецификацией. |
≤12.1 | 3 | 18 | 4Перед Firefox for Android 51, при ошибке парсинга полученных данных добавлялся узел<parsererror> в начало Document, а затем возвращалось значение Document в любом состоянии. Это не соответствовало спецификации. Начиная с Firefox for Android 51, в этом случае теперь правильно возвращается значение null в соответствии со спецификацией. |
≤12.1 | 1 | 1.0 | ≤37 |
|---|---|---|---|---|---|---|---|---|---|---|---|
send |
1 | 12 | 1 | 8 | 1.2 | 18 | 4 | 10.1 | 1 | 1.0 | 4.4 |
setAttributionReporting |
125 | 125 | Нет | 111 | Нет | 125 | Нет | 83 | Нет | 27.0 | 125 |
setPrivateToken |
117 | 117 | Нет | 103 | Нет | 117 | Нет | 78 | Нет | 24.0 | 117 |
setRequestHeader |
1 | 12 | 1 | 8 | 1.2 | 18 | 4 | 10.1 | 1 | 1.0 | 4.4 |
status |
1 | 12 | 1 | 8 | 1.2 | 18 | 4 | 10.1 | 1 | 1.0 | 4.4 |
statusText |
1 | 12 | 1 | ≤12.1 | 1.2 | 18 | 4 | ≤12.1 | 1 | 1.0 | 4.4 |
timeout |
29 | 12 | 12 | 1712–16 | 7 | 29 | 14 | 1812–16 | 7 | 2.0 | 4.4 |
timeout_event |
29 | 12 | 12 | 16 | 7 | 29 | 14 | 16 | 7 | 1.0 | 4.4 |
upload |
2 | 12 | 3.5 | ≤12.1 | 4 | 18 | 4 | ≤12.1 | 3 | 1.0 | 4.4 |
withCredentials |
3 | 12 | 3.5Начиная с Firefox 11, использование атрибутаwithCredentials при выполнении синхронных запросов больше не поддерживается. Попытка сделать это вызывает исключение NS_ERROR_DOM_INVALID_ACCESS_ERR. |
12 | 4 | 18 | 4Начиная с Firefox for Android 14, использование атрибутаwithCredentials при выполнении синхронных запросов больше не поддерживается. Попытка сделать это вызывает исключение NS_ERROR_DOM_INVALID_ACCESS_ERR. |
12 | 3.2 | 1.0 | ≤37 |
worker_support |
4 | 12 | 3.5 | 10.6 | 4 | 18 | 4 | 11 | 5 | 1.0 | 4 |
См. также
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_XMLHttpRequest