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 - запрет кеширования ответа
Информация копипастерам
Внимание! Копирование контента с сайта, возможно только с разрешения администратора. Т.е. Меня! Я скорее всего разрешу Вам это сделать, в обмен на живую ссылку, на статью оригинал.
Ajax кроссдоменно