XMLHttpRequestXMLHttpRequest — 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
Властивості класу XMLHttpRequest
Приклад використанняПлан роботи з об'єктом XMLHttpRequest можна представити так:
Створення екземпляра класу 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 ExplorerInternet 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 можна дозволити робити запити на довільні сервери, записавши їхні адреси в {
"name": "My extension",
...
"permissions": [
"http://www.google.com/"
],
...
}
Примітки
Див. такожПосилання
|
Portal di Ensiklopedia Dunia