XMLHttpRequest
XMLHttpRequest — API-запит вебклієнта (браузера) до вебсервера за протоколом HTTP у фоновому режимі, для мов програмування JavaScript, JScript, VBScript і подібних. Використовується для синхронного або асинхронного обміну інформацією в довільному текстовому форматі (наприклад, XML, JSON, HTML). Дозволяє здійснювати HTTP-запити до віддаленого сервера без потреби перезавантажувати сторінку. Застосування XMLHttpRequest справляє враження «миттєвої» відповіді сервера, у порівнянні з класичними методом перезавантаження всієї сторінки для оновлення представленої на ній інформації.
XMLHttpRequest є невід'ємною частиною технології AJAX і використовується багатьма сайтами для створення динамічних вебзастосунків, що швидко реагують на запити користувача. Наприклад XMLHTTP використовується такими сайтами як Gmail, Google Suggest, MSN Virtual Earth та іншими. XMLHTTP працює лише з файлами, розташованими на тому ж домені, з якої завантажено сторінку. Як і у випадку JavaScript, це зроблено з метою забезпечення безпеки користувача (як захист від атаки, що має назву «міжсайтові сценарії», англ. cross-site scripting).
Історія
ред.Вперше був реалізований компанією Microsoft, з'явившись в Internet Explorer 5.0 у вигляді об'єкта ActiveX, доступного через JavaScript, JScript, VBScript — скриптові мови, що підтримуються браузером. Програмісти проекту Mozilla потім розробили сумісну версію, під назвою XMLHttpRequest[1], в Mozilla 1.0. Надалі ця можливість також була реалізована компаніями Apple починаючи з Safari 1.2, спорідненим браузером Konqueror, компанією Opera Software починаючи з Opera 8.01, і ймовірно іншими.
Оскільки оригінальний XMLHttpRequest в IE5 та IE6 є об'єктом ActiveX, його неможливо розширити, додавши нові властивості і методи, що іноді є незручним обмеженням. Це обмеження було знято в реалізації Mozilla — XMLHttpRequest є повноцінним об'єктом JavaScript. Починаючи з IE7 Microsoft теж почав дотримуватися рекомендованого w3c визначення запиту.
Методи класу XMLHttpRequest
ред.Метод | Опис |
---|---|
abort() | скасовує поточний запит |
getAllResponseHeaders() | повертає повний список HTTP-заголовків у вигляді рядка |
getResponseHeader (headerName) | повертає значення вказаного заголовка |
open (method, URL, async, userName, password) | визначає метод, URL і інші необов'язкові параметри запиту; параметр async визначає, чи відбувається робота в асинхронному режимі |
send (content) | відправляє запит на сервер |
setRequestHeader (label, value) | додає HTTP-заголовок до запиту |
overrideMimeType (mimeType) | дозволяє вказати MIME-тип документа, якщо сервер його не передав або передав неправильно. Увага: метод відсутній в Internet Explorer |
Властивості класу XMLHttpRequest
ред.Властивість | Опис |
---|---|
onreadystatechange | обробник події, яка відбувається при кожній зміні стану об'єкта (необхідний для асинхронного режиму) |
readyState | повертає поточний стан об'єкта (0 — не ініціалізовано, 1 — відкрито, 2 — відправлення даних, 3 — отримання даних і 4 — дані завантажено) |
responseText | текст відповіді на запит |
responseXML | текст відповіді на запит в вигляді XML, котрий пізніше може бути розібраний методами DOM |
status | повертає HTTP-стан у вигляді числа (404 — «Not Found, Не найдено», 200 — «OK» тощо) |
statusText | повертає стан у вигляді рядка («Not Found», «OK» тощо) |
Приклад використання
ред.План роботи з об'єктом XMLHttpRequest можна представити так:
- Створення об'єкта XMLHttpRequest
- Встановлення для нього обробника події onreadystatechange
- Відкриття з'єднання з вказівкою типу запиту, URL і інших параметрів.
- Безпосередньо відправлення запиту.
Створення екземпляра класу XMLHttpRequest
ред.Перший пункт: створення екземпляра класу XMLHttpRequest. Конструкція створення об'єкта відрізняється в залежності від версії браузера: у IE 5 та IE 6 вона реалізована через ActiveXObject, а в решті браузерах (IE 7, Mozilla, Opera, Netscape і Safari) — як вбудований об'єкт типу XMLHttpRequest.
Отже, виклик для ранніх версій Internet Explorer:
var req = new ActiveXObject("Microsoft.XMLHTTP");
У ранніх версіях Internet Explorer (до IE7) рекомендується використовувати:
var req = new ActiveXObject("Msxml2.XMLHTTP");
і для решти:
var req = new XMLHttpRequest();
Тобто, для забезпечення кросс-браузерності нашого коду, потрібно лише перевіряти наявність об'єктів window.XMLHttpRequest і window.ActiveXObject, і застосовувати присутній. Як універсальне рішення пропонується використання наступної функції:
function createRequestObject()
{
if (window.XMLHttpRequest) {
try {
return new XMLHttpRequest();
} catch (e){}
} else if (window.ActiveXObject) {
try {
return new ActiveXObject('Msxml2.XMLHTTP');
} catch (e){}
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch (e){}
}
return null;
}
Установлення обробника події
ред.Наступним кроком є створення обробника подій і відкриття з'єднання. Ці виклики виглядають просто і однаково:
req.onreadystatechange = processReqChange;
req.open(<"GET"|"POST"|...>, <url>, <asyncFlag>);
Відкриття з'єднання і відправлення
ред.Після визначення всіх параметрів запиту його залишається тільки відправити. Робиться це функцією send(). Якщо необхідно передати на сервер POST-дані, їх треба підставити як параметр для цієї функції. POST-дані повинні бути згорнуті в URL-закодований рядок (кодування UTF-8). Іншими словами цей рядок матиме вигляд, який ми звикли бачити в командному рядку браузера, при передачі даних командою GET. При відправленні запиту методом GET — для версії без ACTIVEX необхідно вказати параметр null, в решті випадків можна не указувати ніяких параметрів, але не буде помилкою, якщо для GET завжди буде вказаний параметр null:
req.send(null);
Після цього починає працювати згаданий вище обробник подій. Він — фактично основна частина нашої програми. У обробнику зазвичай відбувається перехоплення всіх можливих кодів стану запиту і виклик відповідних дій, а також перехоплення можливих помилок. Власне, ось приклад частини коду з цими двома функціями:
var req;
function loadXMLDoc(url)
{
req = null;
if (window.XMLHttpRequest) {
try {
req = new XMLHttpRequest();
} catch (e){}
} else if (window.ActiveXObject) {
try {
req = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e){
try {
req = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e){}
}
}
if (req) {
req.onreadystatechange = processReqChange;
req.open("GET", url, true);
req.send(null);
}
}
function processReqChange()
{
// Тільки в стані "complete"
if (req.readyState == 4) {
// для стану "OK"
if (req.status == 200) {
// Якщо 200 - робимо потрібні дії (404 - не знайдено)
} else {
alert("Не вдалось одержати дані:\n" +
req.statusText);
}
}
}
Підсумковий код
ред.Отже, початковий код JavaScript-частини:
var req;
var reqTimeout;
function loadXMLDoc(url) {
req = null;
if (window.XMLHttpRequest) {
try {
req = new XMLHttpRequest();
} catch (e){}
} else if (window.ActiveXObject) {
try {
req = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e){
try {
req = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e){}
}
}
if (req) {
req.onreadystatechange = processReqChange;
req.open("GET", url, true);
req.send(null);
reqTimeout = setTimeout("req.abort();", 5000);
} else {
alert("Браузер не підтримує AJAX");
}
}
function processReqChange() {
document.form1.state.value = stat(req.readyState);
if (req.readyState == 4) {
clearTimeout(reqTimeout);
document.form1.statusnum.value = req.status;
document.form1.status.value = req.statusText;
// only if "OK"
if (req.status == 200) {
document.form1.response.value=req.responseText;
} else {
alert("Не вдалося отримати дані:\n" + req.statusText);
}
}
}
function stat(n)
{
switch (n) {
case 0:
return "не ініціалізовано";
break;
case 1:
return "Завантаження...";
break;
case 2:
return "Завантажено";
break;
case 3:
return "В процесі...";
break;
case 4:
return "Виконано";
break;
default:
return "Невідомий стан";
}
}
function requestdata(params)
{
loadXMLDoc('examples/httpreq.php'+params);
}
Тепер — HTML-форма:
<form name=form1>
<table width=100% style="font-size: 100%">
<tr><td width=30% valign=top>
Стан запиту
<td width=70%>
<input size=25 disabled type=text name=state value="">
<tr><td valign=top>Код стану
<td><input disabled size=2 type=text name=statusnum value="">
<input disabled size=19 type=text name=status value="">
<tr><td valign=top>Дані від сервера
<td><textarea rows=6 name=response></textarea>
<tr><td>Рядок GET-запиту<td>
<input type=text name=getparams value="?">
<input type=button onclick="requestdata(getparams.value);" value="GET">
</table>
</form>
І наостанок, PHP файл:
<?php
header("Content-type: text/plain; charset=windows-1251");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Expires: -1");
echo "Hello world!\n\n";
if (isset($a))
{
for ($i=1; $i < 10000; $i++)
{
echo 'Це тестовий рядок. ';
if (($i % 1000) == 0) flush();
}
}
if (count($_GET) > 0)
{
echo "\n\nПередано GET'ом\n"; print_r($_GET);
}
?>
Кодування
ред.Всі параметри GET/POST, що йдуть на сервер, окрім випадку multipart/form-data, кодуються по різному в різних браузерах. Зокрема, Firefox користується стандартним кодом URL, Opera вдається до кодування в UTF-8, IE7 передає кирилицю не кодуючи, як є. Тому треба бути уважним, інформація про спосіб кодування присутня в заголовках запиту. Наприклад, в PHP їх потрібно за потреби перекодувати функцією iconv. Єдино, можна бути певним, що латиниця не перекодовується в будь-якому випадку, і якщо є можливість залишитися в рамках латиниці, це позбавить програміста від додаткових клопотів.
Відповідь сервера браузер сприймає в тому кодуванні, яке вказане в заголовку відповіді Content-Type. Тобто, знову ж таки, в PHP, щоб браузер сприйняв відповідь в Windows-1251, потрібно послати заголовок типу:
header(Content-Type: text/plain; charset=windows-1251);
Або ж, це має зробити сервер.
Відомі проблеми
ред.Проблема з кешуванням в Microsoft Internet Explorer
ред.Internet Explorer кешує GET-запити. Ті автори, які незнайомі з кешуванням HTTP, сподіваються, що GET-запити не кешуються, або що кеш може бути обійдений, як у разі натиснення кнопки оновлення. У деяких ситуаціях уникнення кешування дійсно є помилкою. Одним з рішень є використання методу POST, який ніколи не кешується; проте він призначений для інших операцій. Іншим рішенням є використання методу запиту GET, що включає унікальний рядок запиту з кожним викликом, як показано на прикладі нижче.
req.open("GET", "xmlprovider.php?hash=" + Math.random());
або установки заголовка Expires на минулу дату у вашому скрипті, який генерує вміст XML. У PHP це буде так:
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // disable IE caching
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
У сервлетах Java це буде так:
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setDateHeader("Expires", 0);
Інакше можна примусити об'єкт XMLHttpRequest завжди витягати новий вміст, не використовуючи кеш.
req.open("GET", "xmlprovider.php");
req.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
req.send(null);
Важливо відмітити, що всі ці методики повинні використовуватися у разі, коли кешування заважає. В основному ж краще отримати переваги в швидкості при кешуванні, можливо комбінуючи зі спеціально вказаними датами модифікації або іншими доречними заголовками на сервері так, щоб максимально використовувати кешування без отримання неправильних результатів.
Повторне використання об'єкта XmlHttpRequest
ред.В Internet Explorer, якщо open() викликаний після установки onreadystatechange, може бути проблема з повторним використанням цього XmlHttpRequest. Щоб використовувати наново XmlHttpRequest, спочатку викликайте метод open(), а потім — призначайте onreadystatechange. Це потрібно тому, що IE неявно очищає об'єкт XmlHttpRequest в методі open(), якщо його стан «completed».
Викликати abort() для перенаправлення запиту на іншій URL не потрібно, навіть якщо поточний запит ще не завершився.
Витоки пам'яті
ред.В Internet Explorer об'єкт XmlHttpRequest належить середовищу DOM/COM, а Javascript-функція — середовищу Javascript. Виклик req.onreadystatechange = function() { … } неявний круговий зв'язок: req посилається на функцію через onreadystatechange, а функція, через область видимості — бачить (посилається на) req.
Неможливість виявити і обірвати такий зв'язок в багатьох (до IE 6,7 редакцій червня 2007?) версіях Internet Explorer приводить до того, що XmlHttpRequest разом з відповіддю сервера, функція-обробник, і все замикання міцно осідають в пам'яті до перезавантаження браузера. Щоб цього уникнути, ряд фреймворків (YUI, dojo…) взагалі не ставлять onreadystatechange, а натомість через setTimeout перевіряють його readyState кожні 10 мілісекунд. Це розриває кругову зв'язку req <-> onreadystatechange, і витік пам'яті не загрожує навіть в найбільш глючних браузерах.
Обмеження безпеки
ред.Кросс-доменний XMLHttpRequest
ред.Для обмеження XmlHttpRequest використовується філософія «Same Origin Policy» — «Правило одного джерела». Воно дуже просте — кожен сайт працює в своїй пісочниці. Запит можна робити тільки на адреси з тим же протоколом, доменом, портом, що і поточна сторінка. Тобто, із сторінки на адресі http://site.com не можна зробити XmlHttpRequest на адресу https://web.archive.org/web/20190617134849/http://www.site.com/, http://site.com:81[недоступне посилання з червня 2019] або https://web.archive.org/web/20030621190843/http://www.othersite.com/.
Це створює проблему, якщо хочеться узяти вміст з іншого сайту. Як правило, в цьому випадку замість XmlHttpRequest використовуються інші засоби, наприклад, завантаження через динамічно створюваний тег <script>. Але, здебільшого, XmlHttpRequest є зручнішим.
Проксі
ред.Найпростіший спосіб обійти це обмеження — проксування. Припустимо, ми хочемо зробити запит з http://site.com [Архівовано 17 червня 2019 у Wayback Machine.] на https://web.archive.org/web/20150508130049/http://remote.com/get.html. Замість вказівки remote.com у методі open(), там ставиться URL виду http://site.com/proxy/remote.com/get.html[недоступне посилання з червня 2019], а сервер на site.com вже обробляє цей запит, як треба.
Якщо remote.com знаходиться на іншому сервері, то серверу site.com доведеться проксувати відвідувачеві як запит, так і відповідь. При цьому, зрозуміло, site.com не отримає куки remote.com, тому з цієї точки зору для користувача все безпечно.
Використання наддомену
ред.Часто кросбраузерні запити — це спосіб обійти обмеження в 2 одночасних з'єднання до одного домену-порту. Спосіб використовувати два різних сервера в спілкуванні з відвідувачем. Крос-доменні запити між наддоменами https://web.archive.org/web/20110102121034/http://a.site.com/, http://b.site.com[недоступне посилання з червня 2019] на http://site.com [Архівовано 17 червня 2019 у Wayback Machine.] допустимі, через властивість document.domain, яке треба встановити в site.com
// на сторінці а.site.com
…
document.domain="site.com";
…
// все, тепер можу робити XmlHttpRequest на site.com
req.open("POST", "http://site.com/giveme.php")
Будь-які запити допустимі між сайтами, що знаходяться в довіреній (trusted) зоні Internet Explorer. Отже, внутрішній корпоративний портал може бути у всіх в цій зоні, і робити запити до будь-яких сайтів.
Ще один хитрий підхід називається XHRIframeProxy, і дозволяє робити XmlHttpRequest до будь-яких доменів за допомогою хитрого iframe-хака.
В плагінах Google Chrome
ред.Пишучи аддон до браузера Google Chrome можна дозволити робити запити на довільні сервери, записавши їхні адреси в manifest.json
[2]
{
"name": "My extension",
...
"permissions": [
"http://www.google.com/"
],
...
}
Примітки
ред.- ↑ Mozilla намагалася зберегти максимальну сумісність із оригіналом, були вилучені лише пропрієтарні назви Microsoft та ActiveX
- ↑ Архівована копія. Архів оригіналу за 8 лютого 2010. Процитовано 27 липня 2010.
{{cite web}}
: Обслуговування CS1: Сторінки з текстом «archived copy» як значення параметру title (посилання)
Див. також
ред.Посилання
ред.- Об'єкт XMLHttpRequest [Архівовано 5 лютого 2012 у WebCite] — робочий нарис W3C
- яваскрипт.укр/XMLHttpRequest [Архівовано 13 квітня 2021 у Wayback Machine.] — про XMLHttpRequest українською мовою.
- Apple Safari 1.2 [Архівовано 12 січня 2010 у Wayback Machine.]
- Microsoft IXMLHTTPRequest
- Mozilla XML Extras [Архівовано 30 листопада 2005 у Wayback Machine.]
- Mozilla XMLHttpRequest object HowTo [Архівовано 24 лютого 2006 у Wayback Machine.]
Це незавершена стаття про Інтернет. Ви можете допомогти проєкту, виправивши або дописавши її. |