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 можна представити так:

  1. Створення об'єкта XMLHttpRequest
  2. Встановлення для нього обробника події onreadystatechange
  3. Відкриття з'єднання з вказівкою типу запиту, URL і інших параметрів.
  4. Безпосередньо відправлення запиту.

Створення екземпляра класу 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%5Bнедоступне+посилання+з+червня+2019%5D або 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%5Bнедоступне+посилання+з+червня+2019%5D, а сервер на 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%5Bнедоступне+посилання+з+червня+2019%5D на 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/"
  ],
  ...
}

Примітки

Див. також

Посилання

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.