Ajax кроссдоменно
Всем привет. Тема сегодняшней заметки: ajax кроссдоменно, без Jquery. По соображениям безопасности, объект XMLHttpRequest имеет неприятное ограничение - нельзя посылать ajax запросы на домены, отличные от домена, на котором выполняется скрипт, формирующий запрос. То есть нельзя послать ajax запрос с домена domain1.ru на domain2.ru. В будущем, кстати это ограничение собираются убрать. Почему? Потому что люди уже давно научились обходить эти ограничения и фактически это ограничение не столько спасает, сколько мешает нормальным разработчикам. Но на данный момент ситуация требует "почесать репу".
Но если вам неохота, то в конце статьи приведен листинг функции, позволяющий посылать запросы ajax кроссдоменно
Вообще технологии ajax уже хренова куча лет, но вот такую попупулярность она получила сравнительно недавно. Что такое ajax - я рассказывать в этой заметке не буду, она о другом. Итак, если ограничение на обращение к другому домену накладывает сам объект XMLHttpRequest - значит, нужно тупо обойтись без него! Нет это не шутка, и более менее знающие люди меня уже понимают
Когда в браузере загружается обычная страница, даже без JavaScript - она уже посылает на сервер целую кучу фоновых запросов, которые выполняются асинхронно загрузке самой странице... да - да я имею ввиду запросы для файлов css, запросы атрибутов src - тегов: img, iframe, object, embed, link... и ещё кучу подобных. Идея ясна? Кстати атрибут src - тега скрипт - это тоже такой же фоновый, асинхронный запрос. И заметьте, что даже если файлы картинок, стилей или скриптов, размещены на других доменах - никакие кроссбраузерные ограничения на их загрузку не влияют.
Из этого вывод:
ajax кроссдоменный GET-запрос можно послать даже с помощью картинки! Сложность в другом - как обработать ответ.
Ajax кроссдоменно
Давайте рассмотрим сдедующий алгоритм. Что бы послать запрос ajax кроссдоменно, а потом иметь возможность обработать полученные данные, мы динамически, с помощью того же Javascript создадим тег SCRIPT. Так же динамически назначим ему нужный адрес, укажем callback функцию, которая обработает полученные данные, а когда запрос загрузится - удалим к хренам, созданный нами тег SCRIPT, потому что запрос у нас может быть и не один, а целая куча.
Значит нам нужно решить пару не совсем простых задач:
Как понять что ответ пришёл, что бы удалить тег SCRIPT?
Как обработать ответ сервера и что такое callback функция?
Первая решается следующим образом: тег SCRIPT, как и все нормальные вещи, которые подгружаются помимо основной страницы имеет событие onload, но в IE это не прокатит. В IE воспользуемся событием onreadystatechange ( да, в IE оно есть и у элемента SCRIPT) и свойством readyState. И поняв, когда загрузка произошла - удаляем подопытного. В общем, смотрим листинг ниже:
// Netscape, Opera if(navigator.appName !== "Microsoft Internet Explorer") { // Используем событие load: scriptTag.onload = function() { scriptTag.parentNode.removeChild(scriptTag); } } else { // Microsoft Internet Explorer scriptTag.onreadystatechange = function() { // Используем свойство readyState: if(scriptTag.readyState === 'loaded'){ scriptTag.parentNode.removeChild(scriptTag); } } }
Далее, как же нам обработать то, что загрузится? Есть такой вариант: в строке запроса атрибута src - указываем имя некой функции, которая должна выполниться, когда данные станут доступны, например "getServerTime" :
http://yourdomain.ru/servertime.php?callback=getServerTime
А в серверном скрипте мы сможем получить её имя из $_GET['callback']. Давайте напишем следующий код для серверной части нашего скрипта:
Серверная сторона
<?php header('Content-type: text/javascript; charset=utf-8'); if(isset($_GET['callback'])) { echo $_GET['callback'].'({data : "'.date("Y-m-d H:i:s", time()).'"});'; } ?>
Этот код просто возвратит строку типа: getServerTime("2011-10-02 19:52:04"); При условии, конечно, что была передана GET - переменная callback со значением getServerTime. То есть мы сделали так, что серверный скрипт, как бы поместил результат своей работы (в нашем случае текущую дату и время) вместо аргумента, указанной функции.
Имейте ввиду IE не любит косяков с MIME - типами! Если указанный тип данных не будет соответствовать фактическому, в IE возможны глюки.
Что нам даст эта строка в уже загруженном теге SCRIPT ? - Ошибку! Конечно, если у нас не определена в Javscript - е функция getServerTime - имя которой мы указали, как callback. В общем нам осталось просто опредилить эту функцию. В нашем случае она будет просто выводить свой единственный аргумент как содержимое некоего html - элемента:
function getServerTime(time) { document.getElementById("timed").innerHTML = "Серверное время: " + time.data + "
"; return true; }
А ещё, то что мы с вами только что сделали - нынче называют модным словом JSONP
Конечно это всё равно не полная свобода - мы ограничены типом запросов: GET но всё же это лучше чем ничего. Но даже это ограничение преодолимо, но об этом я, возможно расскажу в будущем. И на конец полный листинг клиентской стороны :
Клиентская сторона
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Документ без названия</title> <script type="text/javascript"> // ... Определение функции getJsonp... </script> <script type="text/javascript"> function getServerTime(time) { document.getElementById("timed").innerHTML = "<h3>Серверное время: " + time.data + "</h3>"; return true; } window.onload = function() { getJsonp("хттп://yourdomain.ru/servertime.php", "getServerTime", true); } </script> </head> <body> <div id="timed"></div> </body> </html>
Выше, из за особенностей подсветки синтаксиса кода - я не указал реализацию функции для создания кроссдоменных ajax запрсов. Вот её полный листинг:
Функция реализующая ajax кроссдоменно
/* * getJsonp - создаёт кроссдоменный асинхр. запрос * param@ url:string - адрес серверного файла * param@ callback:string - имя функции-обработчика ответа. */ function getJsonp(/*string*/url,/*string*/callback, /*bool*/nocache) { var scriptTag = document.createElement("SCRIPT"); scriptTag.src = url + "?callback=" + callback; if(typeof nocache !== undefined ) { scriptTag.src += "&nocache=" + (new Date()).getTime(); } document.body.appendChild(scriptTag); // Netscape, Opera if(navigator.appName !== "Microsoft Internet Explorer") { scriptTag.onload = function() { scriptTag.parentNode.removeChild(scriptTag); } } else { // Microsoft Internet Explorer scriptTag.onreadystatechange = function() { if(scriptTag.readyState === 'loaded'){ scriptTag.parentNode.removeChild(scriptTag); } } } }
Праметры:
URL - серверного скрипта
callback - имя функции обработчика данных
nocache - запрет кеширования ответа
Информация копипастерам
Внимание! Копирование контента с сайта, возможно только с разрешения администратора. Т.е. Меня! Я скорее всего разрешу Вам это сделать, в обмен на живую ссылку, на статью оригинал.