Ajax кроссдоменно

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, потому что запрос у нас может быть и не один, а целая куча.

Значит нам нужно решить пару не совсем простых задач:

  1. Как понять что ответ пришёл, что бы удалить тег SCRIPT?

  2. Как обработать ответ сервера и что такое 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);
			}
			
		}	
	}
}

Праметры:

  1. URL - серверного скрипта

  2. callback - имя функции обработчика данных

  3. nocache - запрет кеширования ответа

Добавить комментарий


Защитный код
Обновить






Кто на сайте
Сейчас 48 гостей онлайн