Ajax - что это? Асинхронный JavaScript и XML: преимущества и недостатки. Как работает AJAX

AJAX - технология разработки веб-интерфейсов, которая дает возможность браузеру взаимодействовать с веб-сервером без видимой для пользователя перезагрузки страницы. Т.е. при обновлении каких-то данных перезагружается только часть страницы.

Технология AJAX базируется на использовании объекта XMLHttpRequest(), который позволяет отправлять и получать информацию в различных форматах включая XML и HTML. Реализация технологии состоит из клиентской и серверной частей. Клиентская часть выполняется в браузере пользователя и пишется на JavaScript, а серверная выполняется на веб-сервере и пишется на любом языке веб-программирования: php, asp, perl и др.

Клиентский JavaScript-код - это основной код, выполняющий Ajax-приложение и обеспечивающий взаимодействие с сервером. Клиентская часть выполняет сбор информации для отправки запроса серверу, устанавливает соединение с сервером, посылает запрос, получает ответ и обрабатывает ответ сервера. Рассмотрим реализацию клиентской части подробней.

1. Создание объекта запроса XMLHttpRequest .
В Internet Explorer и других браузерах создание объекта XMLHttpRequest отличается. Во всех браузерах кроме Internet Explorer этот объект создается очень просто: xmlHttp = new XMLHttpRequest(); Браузер Internet Explorer для создания объекта запроса использует анализатор MSXML. Кроме того существует две разных версии MSXML. Универсальный метод создания объекта запроса XMLHttpRequest для всех браузеров выглядит следующим образом:


var xmlHttp=null;
try
{


}
catch (e)
{

try
{
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
}

Создание объекта запроса происходит в строках xmlHttp = new XMLHttpRequest(); , xmlHttp=new ActiveXObject("Msxml2.XMLHTTP"); и xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); . Остальные строки обрабатывают ошибки.

2. Выполнение запроса .
Для выполнения запроса необходимо открыть соединение с сервером, установить функцию, которая выполниться после ответа сервера и передать запрос.

Чтобы указать функцию которая будет обрабатывать ответ, необходимо свойству onreadystatechange присвоить имя JavaScript функции: xmlHttp.onreadystatechange = MyFunc; . Причем имя функции указывается без скобок.

Чтобы открыть соединение с сервером необходимо вызвать функцию open() . Эта функция имеет три обязательных параметра:

  • метод запроса HTTP - обычно используют "GET" или "POST" , но можно использовать любой другой метод в соответствии с HTTP стандартами ;
  • url запроса - адрес скрипта на сервере, который обрабатывает запрос;
  • асинхронность запроса - TRUE или FALSE, если TRUE, то запрос асинхронный и пользователь сможет продолжать работу со страницей.

Для отправки запроса необходимо вызвать метод send() , параметром которого могут быть любые данные, которые вы хотите отправить на сервер. Данные должны быть сформированы в строку запроса param1=1¶m2=2¶m3=3 .

Если данные отправляются методом POST, то необходимо изменить MIME-тип запроса: xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); .

Выполнение запроса с использованием метода GET :

/*Устанавливаем соединение*/
xmlHttp.open("GET", "example.php?param1=1¶m2=2", true);
/*Указываем функцию*/

/*Отправляем запрос*/
xmlHttp.send(null);

И с использование метода POST :

/*Устанавливаем соединение*/
xmlHttp.open("POST", "example.php", true);
/*Меняем MIME-тип*/
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
/*Указываем функцию*/
xmlHttp.onreadystatechange = MyFunc;
/*Отправляем запрос*/
xmlHttp.send("param1=1¶m2=2");

3. Обработка ответа сервера .
Отправляя запрос, мы указали имя функции, обрабатывающей ответ сервера: xmlHttp.onreadystatechange = MyFunc; . Для начала, эта функция должна проверять статус запроса. Это можно сделать с помощью свойства readyState . Если xmlHttp.readyState = 4 , то ответ от сервера получен и можно приступать к его обработке. Далее необходимо проверить статус HTTP-ответа с помощью свойства status . Если xmlHttp.status = 200 значит все в порядке и можно продолжить обработку данных. Получить доступ к данным можно с помощью свойств responseText - в виде текста, либо responseXML - в виде объекта XMLDocument.

Полный пример работы Ajax-приложения:


/*переменная для хранения объекта запроса*/
var xmlHttp=null;
/*создание объекта запроса*/
function createRequest()
{
try{
//создаем объект запроса для Firefox, Opera, Safari
xmlHttp = new XMLHttpRequest();
} catch(e){
//создаем объект запроса для Internet Explorer
try{
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e){
try{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e){
xmlHttp = null;
}
}
}

If(xmlHttp == null)alert("Браузер не поддерживает AJAX!");
}

/*отправка запроса*/
function sendRequest()
{
/*получаем объект запроса*/
createRequest();
/*Устанавливаем соединение*/
xmlHttp.open("GET", "ajax.php?param1=1¶m2=2", true);
/*Указываем функцию*/
xmlHttp.onreadystatechange = MyFunc;
/*Отправляем запрос*/
xmlHttp.send(null);
}

/*обрабатываем ответ*/
function MyFunc()
{
if(xmlHttp.readyState == 4) {
if (xmlHttp.status == 200) {
alert(xmlHttp.responseText);
} else {
alert("Ошибка обработки запроса!");
}
}
}

Серверная часть может быть написана на чем угодно. Возвращаемое значение xmlHttp.responseText формируется стандартным выводом. Пример серверной части на php:

Попробуйте пример в действии:

И в дополнение некоторые справочные материалы по объекту XMLHttpRequest :
Методы класса XMLHttpRequest


Свойства класса XMLHttpRequest

Мы познакомились с прямыми методами jQuery для работы с Ajax (такими как get(), post() и load()). В этой статье описан низкоуровневый программный интерфейс jQuery Ajax API .

Казалось бы, термин низкоуровневый указывает на то, что вы получаете доступ к скрытым возможностям механизма запросов, но это не совсем так. Описываемые здесь методы менее удобны по сравнению с рассмотренными ранее, однако ценой небольших дополнительных усилий запрос можно сконфигурировать так, чтобы он в точности соответствовал вашим потребностям, чего не всегда удается добиться с помощью прямых или вспомогательных методов.

Простые Ajax-запросы

Создавать запросы с помощью низкоуровневого API не намного сложнее, чем с помощью прямых или вспомогательных методов. Разница состоит в том, что такой подход позволяет контролировать многие другие аспекты запроса и получать о выполняющемся запросе гораздо больше информации. Центральное место в низкоуровневом API занимает метод ajax() , простой пример использования которого приведен ниже (здесь используется исходный файл и файл mydata.json, описанные в предыдущей статье):

$(function() { $.ajax("mydata.json", { success: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); } }); });

Аргументами метода ajax() являются запрашиваемый URL и объект отображения данных, свойства которого определяют набор пар "ключ-значение", каждая из которых определяет некий параметр запроса. Здесь передаваемый методу ajax() объект содержит только один параметр, success, задающий функцию, которая будет вызываться в случае успешного выполнения запроса.

В данном примере мы запрашиваем у сервера файл mydata.json и используем его вместе с шаблоном данных для создания элементов и вставки их в документ, как это делалось в предыдущей статье с помощью прямых методов. По умолчанию метод ajax() создает HTTP-запрос GET, т.е. данный пример эквивалентен использованию методов get() и getJSON().

Объект jqXHR

Метод ajax() возвращает объект jqXHR, который можно использовать для получения подробной информации о запросе и с которым можно взаимодействовать. Объект jqXHR представляет собой оболочку объекта XMLHttpRequest , составляющую фундамент браузерной поддержки Ajax.

При выполнении большинства операций Ajax объект jqXHR можно просто игнорировать, что я и рекомендую делать. Этот объект используется в тех случаях, когда необходимо получить более полную информацию об ответе сервера, чем та, которую удается получить иными способами. Кроме того, его можно использовать для настройки параметров Ajax-запроса, но это проще сделать, используя настройки, доступные для метода ajax(). Свойства и методы объекта jqXHR описаны в таблице ниже:

Свойства и методы объекта jqXHR Свойство/метод Описание
readyState Возвращает индикатор хода выполнения запроса на протяжении всего его жизненного цикла, принимающий значения от 0 (запрос не отправлен) до 4 (запрос завершен)
status Возвращает код состояния HTTP, отправленный сервером
statusText Возвращает текстовое описание кода состояния
responseXML Возвращает ответ в виде XML (если он является XML-документом)
responseText Возвращает ответ в виде строки
setRequest(имя, значение) Возвращает заголовок запроса (это можно сделать проще с помощью параметра headers)
getAllResponseHeaders() Возвращает в виде строки все заголовки, содержащиеся в ответе
getResponseHeaders(имя) Возвращает значение указанного заголовка ответа
abort() Прерывает запрос

Объект jqXHR встречается в нескольких местах кода. Сначала он используется для сохранения результата, возвращаемого методом ajax(), как показано в примере ниже:

$(function() { var jqxhr = $.ajax("mydata.json", { success: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); } }); var timerID = setInterval(function() { console.log("Статус: " + jqxhr.status + " " + jqxhr.statusText); if (jqxhr.readyState == 4) { console.log("Запрос выполнен: " + jqxhr.responseText); clearInterval(timerID); } }, 100); });

В этом примере мы сохраняем результат, возвращаемый методом ajax(), а затем используем метод setInterval() для вывода информации о запросе каждые 100 мс. Использование результата, возвращаемого методом ajax(), не изменяет того факта, что запрос выполняется асинхронно, поэтому при работе с объектом jqXHR необходимо соблюдать меры предосторожности. Для проверки состояния запроса мы используем свойство readyState (завершению запроса соответствует значение 4) и выводим ответ сервера на консоль.

Для данного сценария консольный вывод выглядит так (в вашем браузере он может выглядеть несколько иначе):

Я использую объект jqXHR лишь в редких случаях и не делаю этого вообще, если он представляет собой результат, возвращаемый методом ajax(). Библиотека jQuery автоматически запускает Ajax-запрос при вызове метода ajax(), и поэтому я не считаю возможность настройки параметров запроса сколько-нибудь полезной. Если я хочу работать с объектом jqXHR (как правило, для получения дополнительной информации об ответе сервера), то обычно делаю это через параметры обработчика событий, о которых мы поговорим далее. Они предоставляют мне информацию о состоянии запроса, что избавляет от необходимости выяснять его.

Задание URL-адреса запроса

Одним из наиболее важных доступных параметров является параметр url , позволяющий указать URL-адрес для запроса. Можно использовать этот параметр как альтернативу передаче URL-адреса в качестве аргумента метода ajax(), как показано в примере ниже:

$(function() { $.ajax({ url: "mydata.json", success: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); } }); });

Создание POST-запроса

Для задания требуемого типа запроса, который необходимо выполнить, используется параметр type . По умолчанию выполняются GET-запросы, как в предыдущем примере. Пример использования метода ajax() для создания POST-запроса и отправки данных формы на сервер приведен ниже:

$(function() { $.ajax({ url: "mydata.json", success: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); } }); // Обработчик клика по кнопке "Заказать" $("button").click(function(e) { $.ajax({ url: "phphandler.php", type: "post", data: $("form").serialize(), success: processServerResponse, dataType: "json" }); e.preventDefault() }); function processServerResponse(data) { // Изначально скрываем всю продукцию var inputElems = $("div.dcell").hide(); for (var prop in data) { // Отображаем только ту продукцию, заказ которой больше 0 // (в ответе от сервера содержится только такая продукция) var filtered = inputElems.has("input") .appendTo("#row1").show(); } // Скрываем базовые элементы формы $("#buttonDiv, #totalDiv").remove(); // Отображаем новые элементы из шаблона totalTmpl $("#totalTmpl").tmpl(data).appendTo("body"); } }); Всего заказов: ${total} Заказать .png"/> ${name}:

Здесь я не буду подробно описывать этот пример, т.к. мы его рассмотрели подробно в предыдущей статье (только с использованием метода post()). Отмечу только, что здесь дополнительно к type мы использовали еще несколько параметров. Для указания цели POST-запроса используется описанный ранее параметр url. Пересылаемые данные указываются с помощью параметра data, значение которого устанавливается с помощью метода serialize(), описанного в предыдущей статье. Тип данных, получаемых от сервера указывается в параметре dataType.

Работа с событиями Ajax

Несколько параметров позволяют указывать функции для обработки событий, которые могут запускаться на протяжении жизненного цикла Ajax-запроса. Именно таким способом вы будете указывать функции обратного вызова, играющие столь важную роль в Ajax-запросах. С одной из них вы уже познакомились при рассмотрении параметра success в предыдущем примере. Список параметров, связанных с событиями, вместе с их краткими описаниями приведен в таблице ниже:

Обработка успешных запросов

В примерах выше, при использовании параметра success, в вызове функции были опущены два аргумента - сообщение, описывающее результат запроса, и объект jqXHR. Пример использования функции, которая принимает эти аргументы, приведен ниже:

$(function() { $.ajax({ url: "mydata.json", success: function(data, status, jqxhr ) { console.log("Статус: " + status); console.log("jqXHR статус: " + jqxhr.status + " " + jqxhr.statusText); console.log(jqxhr.getAllResponseHeaders()); var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); } }); });

Аргумент status - это строка, описывающая исход запроса. Функция обратного вызова, которую мы задаем, используя параметр success, выполняется лишь для успешных запросов, и поэтому значением данного аргумента обычно является success. Исключением является случай, когда вы используете параметр ifModified, описанный далее.

Функции обратного вызова для всех событий Ajax следуют одному и тому же образцу, но наибольшую пользу этот аргумент приносит в случае ряда других событий.

Последний аргумент - это объект jqXHR. Вы не должны выяснять состояние запроса, прежде чем начать работу с этим объектом, поскольку знаете, что функция выполняется лишь тогда, когда запрос завершается успешно. В этом примере объект jqXHR используется для получения информации о состоянии запроса и заголовках, которые сервер включил в ответ, а также для вывода этой информации на консоль.

В данном случае результат имеет следующий вид (в зависимости от того, какой сервер вы используете, у вас может быть другой набор заголовков):

Обработка ошибок

Параметр error используется для указания функции, которая должна вызываться при неудачном завершении запроса. Соответствующий пример приведен ниже:

Error {color: red; border: medium solid red; padding: 4px; margin: auto; width: 200px; text-align: center}

$(function() { $.ajax({ url: "NoSuchFile.json", success: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); }, error: function(jqxhr, status, errorMsg) { $("") .text("Статус: " + status + " Ошибка: " + errorMsg) .insertAfter("h1"); } }); });

Здесь запрашивается отсутствующий на сервере файл NoSuchFile.json, и поэтому запрос заведомо не сможет быть выполнен, в результате чего будет вызвана функция, заданная с помощью параметра error. Аргументами этой функции являются объект jqXHR, а также сообщение о состоянии ошибки и сообщение об ошибке, полученное в ответе сервера. Внутри этой функции в документ добавляется элемент div, отображающий значения аргументов status и errorMsg, как показано на рисунке:

Настройка параметров запросов перед их отправкой

Параметр beforeSend позволяет задать функцию, которая будет вызываться перед отправкой запросов. Это позволяет сконфигурировать запрос в последнюю минуту, добавляя или заменяя параметры, переданные методу ajax() (что может быть особенно полезным, если для множества запросов используется один и тот же объект, содержащий необходимые значения параметров). Пример использования такого подхода представлен ниже:

$(function() { $.ajax({ success: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); }, error: function(jqxhr, status, errorMsg) { $("") .text("Статус: " + status + " Ошибка: " + errorMsg) .insertAfter("h1"); }, beforeSend: function(jqxhr, settings) { settings.url = "mydata.json"; } }); });

Аргументами указанной функции являются объект jqXHR (который может пригодиться для настройки заголовков запроса или отмены запроса, прежде чем он будет отправлен) и объект, содержащий параметры, переданные методу ajax(). В данном примере URL-адрес для Ajax-запроса задается с помощью параметра beforeSend.

Задание нескольких обработчиков событий

В предыдущих примерах мы реагировали на наступление событий, связанных с Ajax-запросами, вызовом одной функции, но в параметрах success, error, complete и beforeSend можно задавать массив функций, каждая из которых будет выполняться при запуске соответствующего события. Простой пример этого приведен ниже:

$(function() { $.ajax({ success: , beforeSend: function(jqxhr, settings) { settings.url = "mydata.json"; } }); function processData(data, status, jqxhr) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); } function reportStatus(data, status, jqxhr) { console.log("Статус: " + status + " Код результата: " + jqxhr.status); } });

В этом примере для параметра success задан массив, состоящий из двух функций, одна из которых использует данные для добавления элементов в документ, а вторая выводит информацию на консоль.

Настройка контекста для событий

Параметр context позволяет указать элемент, который будет назначен переменной this, когда будет вызван обработчик события. Это может быть использовано для обращения к целевым элементам в документе без необходимости их выбора в функции-обработчике. Соответствующий пример приведен ниже:

$(function() { $.ajax({ success: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); }, beforeSend: function(jqxhr, settings) { settings.url = "mydata.json"; }, context: $("h1"), complete: function(jqxhr, status) { var color = status == "success" ? "green" : "red"; this.css("border", "thick solid " + color); } }); });

Здесь параметр context устанавливается на объект jQuery, содержащий элементы h1 документа. В функции, определяемой параметром complete, мы выделяем рамкой выбранные элементы (в данном случае - элемент, поскольку в документе есть только один элемент h1) путем вызова метода css() для объекта jQuery (на который ссылаемся через this). Цвет рамки определяется на основании состояния запроса.

С помощью параметра context можно установить в качестве контекста любой объект, и ответственность за выполнение только допустимых для этого объекта операций лежит на вас. Например, если вы задаете в качестве контекста элемент HTMLElement, то до того, как вызывать для него какие-либо методы jQuery, вы должны передать этот объект функции $().

Настройка базовых параметров Ajax-запросов

Существует группа параметров, с помощью которых можно выполнить базовую настройку Ajax-запроса (некоторые из них, url и type, мы рассмотрели выше). Из всех доступных параметров они представляют наименьший интерес, и их имена в основном говорят сами за себя. Параметры, о которых идет речь, приведены в таблице ниже:

Базовые конфигурационные параметры Ajax-запроса Параметр Описание
accepts Устанавливает для запроса значение заголовка Accept, который указывает MIME-типы, поддерживаемые браузером. По умолчанию это значение определяется параметром dataType
cache Значение false указывает на то, что содержимое запроса не должно кэшироваться сервером. По умолчанию кешируются все типы данных, кроме script и jsonp
contentType Устанавливает для запроса значение заголовка content-туре
dataType Указывает, какие типы данных ожидаются от сервера. Если используется этот параметр, то jQuery будет игнорировать информацию, предоставляемую сервером о типе запроса
headers Задает дополнительные заголовки и значения, которые должны включаться в запрос
jsonp Задает строку, которую следует использовать вместо функции обратного вызова при выполнении запросов JSONP (кроссдоменные запросы). Этот параметр требует согласования с сервером
jsonpCallback Задает имя функции обратного вызова, которое должно использоваться вместо автоматически сгенерированного случайного имени, используемого jQuery по умолчанию
password Задает пароль, который должен использоваться в запросе при прохождении процедуры аутентификации
scriptCharset Указывает jQuery, какой набор символов используется при кодировании запрашиваемого JavaScript-содержимого
timeout Задает длительность тайм-аута (в миллисекундах) для запроса
userName Задает имя пользователя, которое должно использоваться в запросе при прохождении процедуры аутентификации
Задание тайм-аутов и заголовков

О том, что выполняются Ajax-запросы, пользователи часто даже не догадываются, и поэтому указание допустимой длительности тайм-аута - неплохая идея, поскольку это избавит пользователей от томительного ожидания завершения какого-то неведомого для них процесса. Пример задания тайм-аута для запроса приведен ниже:

$(function() { $.ajax({ timeout: 5000, headers: { "X-HTTP-Method-Override": "PUT" }, success: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); }, error: function(jqxhr, status, errorMsg) { console.log("Error: " + status); } }); });

В этом примере параметр timeout устанавливает максимальную длительность тайм-аута, равную 5 сек. Если запрос за это время не будет выполнен, то вызовется функция, заданная с помощью параметра error, и будет выведен код ошибки, определяемый параметром status.

Таймер запускается сразу же после передачи запроса браузеру, и большинство браузеров налагают ограничения на количество одновременно выполняющихся запросов. Это означает, что существует риск того, что к моменту истечения тайм-аута запрос даже не будет запущен. Чтобы избежать этого, необходимо располагать сведениями об ограничениях браузера, а также об объеме и ожидаемой длительности любых других выполняющихся Ajax-запросов.

Дополнительно в этом примере ниже используется параметр headers, с помощью которого в запрос добавляется заголовок. Для указания заголовков используется объект отображения данных. Используемый здесь заголовок может быть полезным для создания веб-приложений, поддерживающих архитектурный стиль REST, если только сервер правильно его распознает.

Использование дополнительных конфигурационных параметров

В следующих разделах описаны наиболее полезные и заслуживающие внимания дополнительные параметры, применимые к Ajax-запросам. Обычно они редко используются, но в случаях, когда в них возникает потребность, они оказываются незаменимыми. Эти параметры позволяют осуществлять точную настройку взаимодействия jQuery с Ajax.

Создание синхронных запросов

Управление режимом выполнения запросов осуществляется с помощью параметра async . Значение true, используемое для этого параметра по умолчанию, означает, что запрос будет выполняться в асинхронном режиме, тогда как значению false соответствует синхронный режим.

При синхронном выполнении запроса метод ajax() ведет себя, как обычная функция, и браузер переходит к выполнению других инструкций сценария лишь после того, как закончится выполнение запроса.

Игнорирование данных, оставшихся неизменными

С помощью параметра ifModified можно обеспечить получение данных лишь в том случае, если с момента последнего запроса они были изменены. Такое поведение определяется заголовком Last-Modified. Благодаря этому удается избежать бесполезной пересылки данных, которая не даст пользователю никакой новой информации по сравнению с той, которой он уже располагает. По умолчанию параметр ifModified имеет значение false, указывающее jQuery на необходимость игнорирования заголовка Last-Modified и предоставления данных в любом случае.

Пример использования этого параметра приведен ниже:

$(function() { $("button").click(function(e) { $.ajax("mydata.json", { ifModified: true, success: function(data, status) { if (status == "success") { $("#row1, #row2").children().remove(); var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); } else if (status == "notmodified") { $("img").css("border", "thick solid green"); } } }); e.preventDefault(); }) });

В этом примере значение параметра ifModified устанавливается равным true. Функция success вызывается всегда, но если с того момента, когда содержимое запрашивалось в последний раз, оно не изменилось, то аргумент data будет иметь значение undefined, а аргумент status - значение notmodified .

В данном случае выполняемые действия определяются значением аргумента status. Если значением этого аргумента является success, то аргумент data используется для добавления элементов в документ. Если же аргумент status имеет значение notmodified, то мы используем метод css() для выделения рамкой элементов, которые уже имеются в документе.

В ответ на событие click, связанное с кнопкой, вызывается метод ajax(). Это дает возможность многократно повторять один и тот же запрос, чтобы продемонстрировать влияние параметра ifModified, как показано на рисунке:

Каким бы полезным ни был этот параметр, я рекомендую использовать его с осторожностью. Если отправка запроса является следствием действий пользователя (например, щелчка на кнопке), то существует вероятность того, что пользователь щелкнул на кнопке, поскольку предыдущий запрос не был выполнен так, как ожидалось.

Представьте, что вы запрашиваете данные, но в методе, указанном в параметре success, содержится ошибка, которая препятствует правильному обновлению содержимого документа. Тогда вашим следующим действием будет попытка щелкнуть на кнопке еще раз, чтобы добиться ожидаемого результата. Непродуманно используя параметр ifModified, можно проигнорировать действия пользователя, вынуждая его предпринимать более серьезные действия для устранения проблемы.

Обработка кода ответа

Параметр statusCode позволяет выбирать варианты дальнейших действий в зависимости от кода ответов на HTTP-запросы. Его можно использовать либо вместо параметров success и error, либо в дополнение к ним. Пример самостоятельного использования параметра statusCode приведен ниже:

$(function() { $.ajax({ url: "mydata.json", statusCode: { 200: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); }, 404: function(jqxhr, status, errorMsg) { $("") .text("Статус: " + status + " Ошибка: " + errorMsg) .insertAfter("h1"); } } }); });

Здесь параметр statusCode задан в виде объекта, устанавливающего связь между кодами ответов на HTTP-запросы и соответствующими им функциями, которые должны быть выполнены на сервере. Какие именно аргументы передаются функциям, зависит от того, отражает ли код ответа успешное выполнение запроса или ошибку.

Если код (например, 200) соответствует успешному запросу, то аргументы совпадают с теми, которые передавались бы функции, определяемой параметром success. В противном случае (например, при коде ответа 404, означающем, что запрашиваемый файл не найден) аргументы совпадают с теми, которые передавались бы функции, определяемой параметром error.

Как видите, это средство не дает непосредственной информации о кодах ответа. Я часто пользуюсь им в процессе отладки взаимодействия браузера с сервером, обычно для того, чтобы выяснить, почему jQuery ведет себя не так, как мне хотелось бы. При этом я использую параметр statusCode в дополнение к параметрам success и error и вывожу информацию на консоль. Если эти параметры используются совместно, то сначала будут выполнены функции success и error, а затем уже - функции, определяемые параметром statusCode.

Предварительная очистка ответных данных

С помощью параметра dataFilter можно задать функцию, которая будет вызвана для предварительной очистки данных, возвращаемых сервером. Это средство незаменимо в тех случаях, когда пересылаемые сервером данные не совсем вас устраивают либо из-за того, что отформатированы неподходящим образом, либо из-за того, что среди них есть данные, которые вы не хотите обрабатывать.

Это средство мне очень помогает при работе с серверами Microsoft ASP.NET, присоединяющими лишние данные к данным JSON. Удаление таких данных с помощью параметра dataFilter требует лишь минимальных усилий. Пример использования параметра dataFilter приведен ниже:

$(function() { $.ajax({ url: "mydata.json", success: function(data) { var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); }, dataType: "json", dataFilter: function(data, dataType) { if (dataType == "json") { var filteredData = $.parseJSON(data); filteredData.shift(); return JSON.stringify(filteredData.reverse()); } else { return data; } } }); });

Функции передаются данные, полученные с сервера, и значение параметра dataType. Если параметр dataType не используется, то второму аргументу присваивается значение undefined. Ваша задача заключается в том, чтобы вернуть отфильтрованные данные. В этом примере предмет нашего внимания - данные в формате JSON:

Var filteredData = $.parseJSON(data); filteredData.shift(); return JSON.stringify(filteredData.reverse()); ...

Для повышения иллюстративности примера в нем выполняются некоторые дополнительные операции. Во-первых, данные JSON преобразуются в массив JavaScript с помощью метода jQuery parseJSON. Затем из массива удаляется первый элемент с помощью метода shift(), а порядок следования остальных его элементов обращается с помощью метода reverse().

Все, что требуется от функции, - вернуть строку, и поэтому мы вызываем метод JSON.stringify(), зная, что jQuery преобразует данные в объект JavaScript, прежде чем вызвать функцию success. В данном примере была продемонстрирована возможность удаления элемента из массива, однако, в зависимости от ситуации, мы могли бы выполнить любой другой вид обработки.

Конечный результат представлен на рисунке:

Управление преобразованием данных

Рассмотрение одной из самых любимых своих настроек я приберег напоследок. Должно быть, вы обратили внимание, что при получении определенных типов данных jQuery автоматически выполняет некоторые удобные преобразования. Например, получая данные JSON, jQuery предоставляет функцию success, использующую объект JavaScript, а не исходную необработанную строку JSON.

Для управления подобными преобразованиями используется параметр converters . Значением этого параметра является объект, устанавливающий соответствие между типами данных и функциями, используемыми для их обработки. В примере ниже показано, как использовать этот параметр для автоматического преобразования HTML-данных в объект jQuery:

$(function() { $.ajax({ url: "flowers.html", // В этом примере мы загружаем HTML-разметку, а не данные в формате JSON success: function(data, status, jqxhr) { var elems = data.filter("div").addClass("dcell"); elems.slice(0, 3).appendTo("#row1"); elems.slice(3).appendTo("#row2"); }, converters: { "text html": function(data) { return $(data); } } }); });

В этом примере регистрируется функция для типа данных text html. Обратите внимание на пробел между компонентами указываемого MIME-типа (в отличие от формы записи text/html). Функция принимает данные, полученные от сервера, и возвращает преобразованные данные. В этом случае преобразование данных заключается в передаче HTML-фрагмента, содержащегося в файле flowers.html, функции $() и возврате результата. Отсюда следует, что к объекту, передаваемому в качестве аргумента data функции success, применимы обычные методы jQuery.

Работая с преобразователями данных, можно слишком увлечься. Я всегда стараюсь избегать соблазна делать с помощью этих функций больше, чем следует. Например, иногда меня так и тянет применить к данным JSON шаблон и передать обратно полученные в результате HTML-элементы. И хотя этот прием очень удобен, он сослужит плохую службу, если кто-то другой будет пытаться расширить ваш код или, например, вам самому впоследствии потребуется организовать интенсивную обработку данных для получения их в исходной форме.

Настройка и фильтрация Ajax-запросов

После того как вы познакомились с методом ajax() и доступными для работы с ним параметрами, мы можем рассмотреть несколько дополнительных методов, предоставляемых jQuery для упрощения настройки запросов.

Определение параметров, используемых по умолчанию

Метод ajaxSetup() позволяет установить значения параметров, которые будут применяться по умолчанию во всех Ajax-запросах, тем самым освобождая вас от необходимости настраивать параметры при каждом запросе. Пример использования этого метода приведен ниже:

") .text("Статус: " + status + " Ошибка: " + errorMsg) .insertAfter("h1"); }, converters: { "text html": function(data) { return $(data); } } }); $.ajax({ url: "flowers.html", success: function(data, status, jqxhr) { var elems = data.filter("div").addClass("dcell"); elems.slice(0, 3).appendTo("#row1"); elems.slice(3).appendTo("#row2"); } }); });

Метод ajaxSetup() вызывается с помощью функции jQuery $ аналогично тому, как это делалось в случае вызова метода ajax(). Аргументом метода ajaxSetup() является объект, содержащий значения параметров, которые вы хотите использовать по умолчанию для всех Ajax-запросов. В этом примере мы устанавливаем значения по умолчанию для параметров timeout, global, error и converters.

После того как был вызван метод ajaxSetup(), нам остается определить значения лишь те значения параметров, которые мы хотим изменить, или те, которые не предоставляются по умолчанию. Это обеспечивает значительную экономию времени в тех случаях, когда приходится делать множество запросов с одинаковыми значениями параметров.

Фильтрация запросов

Метод ajaxSetup() определяет базовые значения конфигурационных параметров, применимые ко всем запросам Ajax. Возможности динамической настройки параметров для отдельных Ajax-запросов обеспечиваются методом ajaxPrefilter() . Пример использования этого метода приведен ниже:

$(function() { $.ajaxSetup({ timeout: 15000, global: false, error: function(jqxhr, status, errorMsg) { $("") .text("Статус: " + status + " Ошибка: " + errorMsg) .insertAfter("h1"); }, converters: { "text html": function(data) { return $(data); } } }); $.ajaxPrefilter("json html", function(settings, originalSettings, jqxhr) { if (originalSettings.dataType == "html") { settings.timeout = 2000; } else { jqxhr.abort(); } }) $.ajax({ url: "flowers.html", dataType: "html", success: function(data, status, jqxhr) { var elems = data.filter("div").addClass("dcell"); elems.slice(0, 3).appendTo("#row1"); elems.slice(3).appendTo("#row2"); } }); });

Указанная вами функция будет выполняться для каждого нового Ajax-запроса. Аргументами, передаваемыми функции, являются параметры запроса (включая любые значения по умолчанию, установленные вами с помощью метода ajaxSetup()), а также исходные параметры, переданные методу ajax() (исключая любые значения по умолчанию) и объекту jqXHR запроса.

Мы вносим изменения в объект, передаваемый в качестве первого аргумента, как показано в примере. В данном сценарии, если среди параметров, передаваемых методу ajax(), присутствует параметр dataType, то длительность тайм-аута устанавливается равной двум секундам. Чтобы предотвратить отправку всех остальных запросов, для объекта jqXHR вызывается метод abort().

Сейчас в сети Интернет наблюдается очень активное развитие (и даже использование) новых технологий. Одна из таких технологий — AJAX.

Что такое AJAX?

AJAX — это аббревиатура, которая означает Asynchronous Javascript and XML. На самом деле, AJAX не является новой технологией, так как и Javascript, и XML существуют уже довольно продолжительное время, а AJAX — это синтез обозначенных технологий. AJAX чаще всего ассоцириуется с термином Web 2.0 и преподносится как новейшее Web-приложение.

При использовании AJAX нет необходимости обновлять каждый раз всю страницу, так как обновляется только ее конкретная часть. Это намного удобнее, так как не приходится долго ждать, и экономичнее, так как не все обладают безлимитным интернетом. Правда в этом случае, разработчику необходимо следить, чтобы пользователь был в курсе того, что происходит на странице. Это можно реализовать с использованием индикаторов загрузки, текстовых сообщений о том, что идёт обмен данными с сервером. Необходимо также понимать, что не все браузеры поддерживают AJAX (старые версии браузеров и текстовые браузеры). Плюс Javascript может быть отключен пользователем. Поэтому, не следует злоупотреблять использованием технологии и прибегать к альтернативным методам представления информации на Web-сайте.

Обобщим достоинства AJAX:

  • Возможность создания удобного Web-интерфейса
  • Активное взаимодействие с пользователем
  • Удобство использования

AJAX использует два метода работы с веб-страницей: изменение Web-страницы не перезагружая её, и динамическое обращение к серверу. Второе может осуществляться несколькими способами, в частности, XMLHttpRequest, о чем мы и будем говорить, и использование техники скрытого фрейма.

Обмен данными

Для того, чтобы осуществлять обмен данными, на странице должен быть создан объект XMLHttpRequest, который является своеобразным посредником между Браузером пользователя и сервером (рис. 1). С помощью XMLHttpRequest можно отправить запрос на сервер, а также получить ответ в виде различного рода данных.

Обмениваться данными с сервером можно двумя способами. Первый способ — это GET-запрос. В этом запросе вы обращаетесь к документу на сервере, передавая ему аргументы через сам URL. При этом на стороне клиента будет логично использовать функция Javascript`а escape для того, чтобы некоторые данные не прервали запрос.

Клиент часть, написанная на Javascript, должна обеспечивать необходимую функциональность для безопасного обмена с сервером и предоставлять методы для обмена данными любым из вышеперечисленных способов. Серверная часть должна обрабатывать входные данные, и на основе их генерировать новую информацию (например, работая с базой данных), и отдавать ее обратно клиенту. Например, для запроса информации с сервера можно использовать обычный GET-запрос с передачей нескольких и небольших по размеру параметров, а для обновления информации, или добавления новой информации потребуется использовать уже POST-запрос, так как он позволяет передавать большие объемы данных.

Как уже было сказано, AJAX использует асинхронную передачу данных. Это значит, что пока идёт передача данных, пользователь может совершать другие, необходимые ему действия. В это время следует оповестить пользователя о том, что идёт какой-либо обмен данными, иначе пользователь подумает, что произошло что-то не то и может покинуть сайт, или повторно вызвать «зависшую», по его мнению, функцию. Индикация во время обмена данными в приложении Web 2.0 играет очень важную роль: посетители могли еще не привыкнуть к таким способам обновления страницы.

Ответ от сервера может быть не только XML, как следует из названия технологии. Помимо XML, можно получить ответ в виде обычного текста, или же JSON (Javascript Object Notation). Если ответ был получен простым текстом, то его можно сразу вывести в контейнер на странице. При получении ответа в виде XML, обычно происходит обработка полученного XML документа на стороне клиента и преобразование данных к (X)HTML. При получении ответа в формате JSON клиент должен лишь выполнить полученный код (функция Javascript`а eval) для получения полноценного объекта Javascript. Но здесь нужно быть осторожным и учитывать тот факт, что с использованием этой технологии может быть передан вредоносный код, поэтому перед выполнением полученного с сервера кода следует его тщательно проверить и обработать. Существует такая практика, как «холостой» запрос, при котором никакой ответ от сервера не приходит, лишь изменяются данные на стороне сервера.

В разных браузерах данный объект обладает разными свойствами, но в целом он совпадает.

Методы объекта XMLHttpRequest

Заметьте, что названия методов записаны в том же стиле (Camel-style), что и другие функции Javascript. Будьте внимательны при их использовании.

abort() — отмена текущего запроса к серверу.

getAllResponseHeaders() — получить все заголовки ответа от сервера.

getResponseHeader(«имя_заголовка») — получить указаный заголовок.

open(«тип_запроса»,»URL»,»асинхронный»,» имя_пользователя»,» пароль») — инициализация запроса к серверу, указание метода запроса. Тип запроса и URL — обязательные параметры. Третий аргумент — булево значение. Обычно всегда указывается true или не указывается вообще (по умолчанию — true). Четвертый и пятый аргументы используются для аутентификации (это очень небезопасно, хранить данные об аутентификации в скрипте, так как скрипт может посмотреть любой пользователь).

send(«содержимое») — послать HTTP запрос на сервер и получить ответ.

setRequestHeader(«имя_заголовка»,»значение») — установить значения заголовка запроса.

Свойства объекта XMLHttpRequest

onreadystatechange — одно из самых главных свойств объекта XMLHttpRequest. С помощью этого свойства задаётся обработчик, который вызывается всякий раз при смене статуса объекта.

readyState — число, обозначающее статус объекта.

responseText — представление ответа сервера в виде обычного текста (строки).

responseXML — объект документа, совместимый с DOM, полученного от сервера.

status — состояние ответа от сервера.

statusText — текстовое представление состояния ответа от сервера.

Следует подробнее расммотреть свойство readyState:

  • 0 — Объект не инициализирован.
  • 1 — Объект загружает данные.
  • 2 — Объект загрузил свои данные.
  • 3 — Объек не полностью загружен, но может взаимодействовать с пользователем.
  • 4 — Объект полностью инициализирован; получен ответ от сервера.

Именно опираясь на состояние готовности объекта можно представить посетителю информацию о том, на какой стадии находится процесс обмена данными с сервером и, возможно, оповестить его об этом визуально.

Создание объекта XMLHttpRequest

Как уже говорилось выше, создание данного объекта для каждого типа браузера — уникальный процесс.

Например, для создания объекта в Gecko-совместимых браузерах, Konqueror`е и Safari, нужно использовать следующее выражение:

var Request = new XMLHttpRequest();

А для Internet Explorer`а используется следующее:

var Request = new ActiveXObject("Microsoft.XMLHTTP");

var Request = new ActiveXObject("Msxml2.XMLHTTP");

Теперь, чтобы добиться кроссбраузерности, необходимо сложить все функции в одну:

function CreateRequest() { var Request = false; if (window.XMLHttpRequest) { //Gecko-совместимые браузеры, Safari, Konqueror Request = new XMLHttpRequest(); } else if (window.ActiveXObject) { //Internet explorer try { Request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (CatchException) { Request = new ActiveXObject("Msxml2.XMLHTTP"); } } if (!Request) { alert("Невозможно создать XMLHttpRequest"); } return Request; }

После всего этого можно создавать данный объект и не беспокоится за работоспособность на популярных браузерах. Но создать объект можно в разных местах. Если создать его глобально, то в определенный момент времени возможен будет только один запрос к серверу. Можно создавать объект всякий раз, как происходит запрос к серверу (это почти полностью решит проблему).

Запрос к серверу

Алгоритм запроса к серверу выглядит так:

  • Проверка существования XMLHttpRequest.
  • Инициализация соединения с сервером.
  • Посылка запрса серверу.
  • Обработка полученных данных.

Для создания запроса к серверу мы создадим небольшую функцию, которая будет по функциональности объединять в себе функции для GET и POST запросов.

/* Функция посылки запроса к файлу на сервере r_method - тип запроса: GET или POST r_path - путь к файлу r_args - аргументы вида a=1&b=2&c=3... r_handler - функция-обработчик ответа от сервера */ function SendRequest(r_method, r_path, r_args, r_handler) { //Создаём запрос var Request = CreateRequest(); //Проверяем существование запроса еще раз if (!Request) { return; } //Назначаем пользовательский обработчик Request.onreadystatechange = function() { //Если обмен данными завершен if (Request.readyState == 4) { //Передаем управление обработчику пользователя r_handler(Request); } } //Проверяем, если требуется сделать GET-запрос if (r_method.toLowerCase() == "get" && r_args.length > 0) r_path += "?" + r_args; //Инициализируем соединение Request.open(r_method, r_path, true); if (r_method.toLowerCase() == "post") { //Если это POST-запрос //Устанавливаем заголовок Request.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=utf-8"); //Посылаем запрос Request.send(r_args); } else { //Если это GET-запрос //Посылаем нуль-запрос Request.send(null); } }

Создавать запрос стало намного проще. Для примера напишем функцию, которая будет получать содержимое файла на сервере и выводить его в контейнер.

function ReadFile(filename, container) { //Создаем функцию обработчик var Handler = function(Request) { document.getElementById(container).innerHTML = Request.responseText; } //Отправляем запрос SendRequest("GET",filename,"",Handler); }

Именно таким образом происходит взаимодействие с сервером.

Обработка ответа

В предыдущем примере мы сделали функцию запроса к серверу. Но она, по сути, небезопасна, так как мы не обрабатываем состояния объекта и состояния ответа от сервера.

Дополним наш код, чтобы он смог выводить визуальное оповещение о процессе загрузки.

Request.onreadystatechange = function() { //Если обмен данными завершен if (Request.readyState == 4) { //Передаем управление обработчику пользователя r_handler(Request); } else { //Оповещаем пользователя о загрузке } } ...

Как вы уже знаете, объект XMLHttpRequest позволяет узнать статус ответа от сервера. Воспользуемся этой возможностью.

Request.onreadystatechange = function() { //Если обмен данными завершен if (Request.readyState == 4) { if (Request.status == 200) { //Передаем управление обработчику пользователя r_handler(Request); } else { //Оповещаем пользователя о произошедшей ошибке } } else { //Оповещаем пользователя о загрузке } } ...

Варианты ответа от сервера

От сервера можно получить данные нескольких видов:

  • Обычный текст
  • Если вы получаете обычный текст, то вы можете сразу же направить его в контейнер, то есть на вывод. При получении данных в виде XML вы должны обработать данные с помощью DOM-функций, и представить результат с помощью HTML.

    JSON — это объектная нотация Javascript. С ее помощью можно представить объект в виде строки (здесь можно привести аналогию с функцией сериализации). При получении JSON-данных вы должны выполнить их, чтобы получить полноценный объект Javascript и произвести с ним необходимые операции. Помните, что такая передача данных и выполнение их не являются безопасными. Вы должны следить за тем, что поступает на исполнение.


    Наша жизнь непостоянна. Все в этом мире эволюционирует и изменяется. В том числе и виртуальная реальность. И одно из слов, с которым связаны эти изменения,- это AJAX. Об AJAX уже слышали не только веб-программисты, но и рядовые пользователи. Что реально кроется за этой магической аббревиатурой? Как это использовать на своем сайте? На эти вопросы я и попытаюсь ответить в данной статье.

    Впервые об AJAX я услышал весной этого года. Сразу заинтересовался этой технологией, и, как и положено в таких случаях, отправился в поиск за статьями, которые смогли бы ответить на возникшие у меня вопросы: "Что это такое? Как это работает? И в чем преимущества? Что нужно дополнительно установить на сервер/клиент для работы с AJAX? Как это можно использовать на своем сайте?". Прочтя с десяток статей, я получил ответ лишь на первые два вопроса, но на остальные ответа так нигде не нашел. И лишь после прочтения нескольких публикаций на английском языке я окончательно понял, что к чему. Это и подвигло меня к написанию данного материала.

    В первую очередь статья адресуется подготовленным людям, пишущим программы для интернета и знакомым с такими терминами как "объект", "метод", "свойства". Однако частично может оказаться полезной и тем, кто просто интересуется данным вопросом. В списке литературы имеется необходимый перечень ссылок, воспользовавшись которыми вполне реально освоить технологию "с нуля".

    По ходу изложения под термином "браузер" мы будем понимать браузеры: Internet Explorer 5.0+, Safari 1.3 и 2.0+, Netscape 7+, Opera 8.5+, Mozilla Firefox (плюс означает данную версию и более новые). Если речь станет заходить о других версиях, об этом будет упоминаться отдельно."Что это такое? Как это работает и в чем преимущества?"

    Для того чтобы понимать, какие преимущества дает AJAX, нужно знать, как работают веб-приложения в настоящее время. А работают они по клиент-серверной технологии (рис. 1).

    Пользователь в браузере открывает какую-либо страницу page . На странице есть гиперссылки, которые ведут на другие страницы. При нажатии на любую из них браузер посылает запрос URL на сервер, с которым связана эта ссылка. Если в природе не существует сервера, связанного с этой ссылкой (например, когда, набирая URL в адресной строке, вы ошиблись при написании имени ресурса), или имеются проблемы связи с интернетом, то браузер сгенерирует страницу, подобную показанной на картинке (так она выглядит в Operа-е):

    В случае существования сервера, но отсутствии на нем документа, указанного в запросе сервер сам создаст HTML страницу с описанием ошибки. Например, это может быть всем известная 404-ая ошибка (документ не найден). Или, если все верно, в ответ сервер отдаст новую страницу. В любом случае, в браузер будет загружена новая страница new_page, даже если по сравнению со старой на ней изменилась лишь пара слов. Довольно существенный минус данной технологии. Кроме того, работа ведется в синхронном режиме. То есть после того как браузер отослал на сервер запрос он ожидает от него ответ, и пока ответ не получен ничего предпринимать не будет. А порой ответ, и загрузка новой страницы может длиться слишком долго. Настолько долго, что пользователь может не дождаться загрузки страницы и просто закрыть её. Поэтому веб-программмисты прибегают к некоторым уловкам.

    СВОЙСТВА:

    readonly onreadystatechange function

    Указывает функцию обратного вызова (callback function), которая будет вызываться каждый раз, когда будет изменяться readyState свойство. Несмотря на то, что вызывается функция, параметры передать в нее не получиться. Но об этом чуть позже в примере.

    readonly readyState integer

    Состояние запроса. Может принимать значения:
    • 0 - не неинициализированный (uninitialized), метод open() еще не был вызван;
    • еще не вызван;
    • 2 - загружен (loaded), метод send() был вызван и ответные заголовки/статус (свойство status ) получены;
    • 3 - интерактивный (interactive), идет прием данных, которые доступны через свойство responseText ;
    • 4 - завершенный (completed), в ответ на запрос получены не только все заголовки и статус, но и приняты все данные от сервера, ответ завершен.

    readonly responseText string

    Ответ сервера в виде обыкновенного текста. Только чтение.

    readonly responseXML object

    Ответ сервера в виде объекта DOM Document. Используется, если ответ сервера является корректным XML документом. Если документ не корректный, данные не получены или еще не оправлены, то свойство равно NULL. Только чтение.

    readonly status string

    Статус ответа. Например: 200 (ОК), 404 (документ не найден), 503 (временная перегрузка сервера). МЕТОДЫ:

    void abort()

    Прерывает HTTP запрос или получение данных. Очищает все свойства объекта, которым присваиваются начальные значения. Метод полезен в связке с таймером, когда по прошествии определенного времени с момента запроса (вылете в тайм-аут) ответ от сервера так и не был получен.

    string getAllResponseHeaders()

    Возвращает все заголовки ответа сервера в виде отформатированной строки. Каждый новый заголовок начинается с новой строки.

    string getResponseHeader (string header)

    Вернуть заголовок с именем header.

    void open (string method, string uri, [boolean asynch])

    Подготавливает запрос по адресу uri методом method (POST или GET) с указанием режима asynch, асинхронный режим или нет. В результате вызова свойство readyState становиться равным 1.

    void send (string data)

    Инициирует запрос к серверу. В запросе передаются данные data.

    void setHeader (string header, string value)

    Присваивает заголовку с именем header, значение value. Перед началом использования этого метода не забудьте вызвать open() ! "Как это можно использовать на своем сайте?"

    Теперь, когда у нас есть не только необходимые теоретические знания, но и представление о том, на что практически опирается AJAX, можно преступать к написанию своего приложения. Далее я привожу один пример от момента формулирования задачи до полной ей реализации, в виде приложения, поясняя некоторые тонкости по ходу изложения.

    Итак, у нас задача: нужно реализовать базу данных (БД) драйверов для различных устройств. При этом БД настолько большая, что нет смысла пересылать её приложению-клиенту и делать выборку из неё посредством JavaScript. Из-за изменения одного значения на странице перегружать её тоже нежелательно. Лично я для реализации данной задачи применяю для серверных скриптов PHP, а реализации БД применяю XML файл.

    Структуру БД выбираю следующую:

    Листинг БД файл data.xml : ATI 9600 128 DDR (128bit) ATI 9600 256 DDR (128bit) ATI 9600XT 256 DDR (128bit) ATI X800GTO 256 DDR (256 bit) ATI X1300 512 DDR(128bit) ATI X1300 256 DDR (128bit) NVidia 6600 128 DDR (128bit) NVidia 7800GS 256 DDR (256 bit) ATI X1300Pro 256 DDR (128bit) ATI X1600Pro 256 DDR (128bit) ATI X1800GTO 256 DDR (256bit) ATI X1600Pro 256 DDR (128bit) ATI X1900XT 512 DDR (256bit) NVidia 6600 Silencer 128 DDR (128bit) NVidia 6600GT 128 DDR (128bit) ATI X1900XT 512 DDR (256bit) ATI X1900XTX 512 DDR (256bit) ATI X800 SilentPipe 128 DDR(256bit) Nvidia 6600GT 128 DDR (128bit) NVidia 6600GT PassiveHeatsink 128 DDR (128bit) PCI-E ATI X550 128 DDR (128bit) PCI-E ATI X800GT Uniwise 256 DDR (256 bit) ATI X800GTO 256 DDR (128bit) Audigy 2 6.1 Audigy 2 ZS 7.1 X-Fi Platinum Audiophile 192 Revolution 5.1 Audiophile Audiophile Fast Track PIXMA iP 90 PIXMA iP4200 PIXMA iP6600D Picture Mate 100 Stylus Color C48 Stylus Color C87U DeskJet 1280 DeskJet 5443 Photo Smart 385 Laser Shot LBP2900 Laser Shot LBP3300 ML 1615 ML 2015 LaserJet 1018 LaserJet 2420 LaserJet 2420DN 4200F LiDE500F LiDE60 Perfection 1270 Perfection 3590 Perfection 4990 Bear Paw 2400CU Perfection 4990

    Как в этой БД человек ведет поиск? Скорее всего, он от корневого элемента шел бы по дереву документа до тех пока в нужной ветви не нашел ссылку или убедился, что драйвера для данного устройства нет в базе. Также поступим и мы, используя для нахождения нужного узла или набора узлов выражения языка XPath .

    Листинг формы для отправки данных index.htm :

    видео картазвуковая картапринтерсканер

    В форме есть две переменные: path и flag . В первой хранится запрашиваемый путь, который отправляется на сервер. Так как один элемент в форме уже есть, то у этой переменной уже есть начальное значение. Вторая переменная служит для того, чтобы указать серверному скрипту, что из документа нужно извлечь определенный элемент Device . Кроме того, формат возвращаемых данных с сервера изменится.

    Теперь рассмотрим JS-движок. Все функции клиентской части собраны в скрипте ajax.js : y = new Object(); function httpRequest() { if (window.XMLHttpRequest) { //создание объекта для всех браузеров кроме IE requestObj = new XMLHttpRequest(); } else if (window.ActiveXObject) { //для IE requestObj = new ActiveXObject("Msxml2.XMLHTTP"); if (!requestObj) { requestObj = new ActiveXObject("Microsoft.XMLHTTP"); }; }; }; function sendRequest (url,data) { httpRequest(); //определяем call-back функцию requestObj.onreadystatechange = responseServer; //подготовка отправки данных, readyState=1 requestObj.open("POST",url,true); /* Т.к. данные отправляются POST методом, то необходимо серверу отослать заголовок информирующий его об этом */ requestObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); //отправка данных на сервер requestObj.send(data); }; function responseServer() { if (requestObj.readyState == 4) { var status = requestObj.status; if (status == 200) { addSelect(y); } else if (status == 400) { alert("Неправильный запрос"); } else if (status == 500) { alert("Внутреняя ошибка на сервере"); } else if (status == 503) { var time = requestObj.getResponseHeader("Retry-After")*1000; alert("Сервер перегружен. Запрос будет повторен через: "+time+" секунд"); setTimeout(sendRequest(url,path),time); } else { alert("Ошибочный ответ сервера"); }; }; }; function sendData(obj) { var Elpath = document.form.path; var url = "index.php"; if (document.form.flag.value == "0") { var path = Elpath.value + "/" + obj.value; } else { var path = Elpath.value + "/Device["" + obj.value + ""]"; /* методом GET отправляем серверному скрипту информация о том что необходим конкретный элемент Device */ url = "index.php?flag=1"; }; //присваиваем переменной формы path значение текущего запроса Elpath.value = path; //кодируем передаваемую строку path path = "path="+encodeURIComponent(path); y = obj; sendRequest (url,path); }; function addSelect(obj) { //ответ сервера в виде обычного текста var docTEXT = requestObj.responseText; obj.setAttribute("disabled",true); //создаем элемент div var div = document.createElement("div"); //добавляем ответ сервера в div div.innerHTML = docTEXT; //добавляем div с ответом сервера в дерево документа document.form.appendChild(div); }; function reset() { document.form.path.value="//Devices"; document.form.flag.value="0"; var NodeListDiv = document.form.getElementsByTagName("div"); var length = NodeListDiv.length; if (length > 1) { while (NodeListDiv != undefined) { document.form.removeChild(NodeListDiv); }; }; document.form.Devices.removeAttribute("disabled"); };

    Как я уже говорил, в функцию свойства onreadystatechange нельзя передать параметры. Точнее нельзя передавать параметры, которые являются объектами. Поэтому в самом начале создаем переменную, в которой и будем хранить ссылку на вызвавший функцию объект. Поскольку данная переменная находится в глобальной зоне видимости переменных, то обратиться к ней можно будет из любой части программы. На данный момент это самый разумный способ передать параметры call-back функции свойства onreadystatechange объекта.

    А теперь разберем по шагам работу движка.

    При наступлении события onblur (элемент select потерял фокус) вызывается функция sendData(), которая и подготавливает POST данные для оправки запроса. Кроме того, она формирует XPath выражение в зависимости от значения переменной flag=0 (например, //Devices/VideoCards ) или flag=1 (например, //Devices/VideoCards/AGP/Sapphire/Device["ATI 9600XT 256 DDR (128bit)"] ).

    Далее вызываем функцию sendRequest(), в которую передаем URL серверного скрипта, а также переменную типа строка, в которой содержатся готовые POST-данные. И первым делом создаем XMLHttpRequest объект, ссылку на который храним в переменной requestObj . Функция httpRequest() является кросс-браузерной, и будет работать во всех браузерах.

    Когда-то обращение к функции httpRequest() я делал сразу при загрузке страницы через и больше не создавал XMLHttpRequest объект. Но как оказалось, это работает для всех браузеров кроме IE, который каждый раз требует создавать новый объект. Поэтому вызов данной функции делается каждый раз перед отправкой данных.

    После отправки данных браузер ждет ответа с сервера. При каждом изменении свойства readyState будет вызываться функция responseServer() . Если статус ответа пришел с кодом "200" (все нормально), то будет вызвана функция addSelect(), которая и добавит полученный данные в DOM текущего документа. Все браузеры будут ждать ответа от сервера. Однако по истечении некоторого времени (time-out) принудительно назначат XMLHttpRequest.readyState = 4 и перестанут ожидать ответа с сервера. Например, для Opera значение тайм-аута составляет 10 секунд. Используя другие статусы, можно добавить в движок обработчик ошибок в ответах сервера.

    Функция addSelect() добавляет в DOM текущего документа еще один узел DIV, в который и помещает ответ с сервера. Может возникнуть вопрос, почему используется свойство responseText , а не responseXML ? У кого-то обязательно возникнет желание, используя это свойство, импортировать ответ сервера (а серверный скрипт в ответ присылает XML документ) прямо в DOM документа. Возникло такое желание и у меня. Я хотел импортировать корневой элемент присланного XML файла и все его потомки методом importNode . Но браузер импортировал элемент без потомков, даже несмотря на то, что второй параметр данного метода был установлен в true : importNode(Object importedNode,true) . Поэтому не точная реализация этого метода пока исключает его использование.

    Равнозначное решение было найдено, используя innerHTML метод элемента.

    На этом работа клиентской части заканчивается. Оставшаяся нерассмотренной функция reset() призвана вернуть DOM документа к начальному виду. Достичь того же можно, обновив страницу по F5, но AJAX-движок как раз и пишется для того, чтобы избежать перезагрузки страницы. Поэтому все элементы, добавленные в документ скриптом, должны быть удалены из него также скриптом.

    В ответ на запрос серверный скрипт формирует XML данные вида: childrenElementName_1 .... childrenElementName_1

    Если запрашиваемый узел имеет имя Device , то возвращается обычный отформатированный текст. Серверный скрипт написан на PHP V5 и не будет работать на более ранних версиях этого интерпретатора, так как расширение для работы с DOM было введено в этот язык только с пятой версии, и заменило собой расширение DOM XML, интерфейс которого не соответствовал спецификации. А теперь посмотрим на код серверного скрипта.

    Листинг файла index.php : load("data.xml"); //создаем объект XPath $DOMXPath = new DOMXPath($doc); $DOMNodeList = $DOMXPath -> query($_POST); //согласно запросу извлекаем нужный элемент $DOMNode = $DOMNodeList -> item(0); //создаем объект XML-документ $replyXML = new DOMDocument("1.0", "windows-1251"); /* если flag не равен единице, значит текущий элемент не является элементом Device и необходимо найти все элементы-потомки текущего элемента DOMNode */ if ($_GET != 1) { //получаем список все потомков элемента $childNodes = $DOMNode -> childNodes; /* Поскольку потомки могут быть не только элементы, но и узлы, то создаем индексный массив который содержит только элементы-потомки */ foreach ($childNodes as $Node) { if ($Node->nodeType == 1) { $arrayNodes = $Node; }; }; //создаем корневой элемент XML-документа $root = $replyXML->createElement("select"); $optgroup = $replyXML->createElement("optgroup"); /* если элементы-потомки не являются Device, то задаем атрибуты для корневого элемента и его элемента-потомка optgroup */ if ($arrayNodes -> nodeName != "Device") { $root->setAttribute("name",$DOMNode->nodeName); $AttributeNode = $arrayNodes->getAttributeNode("title"); $optgroup->setAttribute("label",$AttributeNode->value); $root->setAttribute("onblur","sendData(this)"); } else { /* в противном случае создаем атрибут с JS кодом который и присвоит переменной в форме flag значение "1" */ $root->setAttribute("onblur", "document.form.flag.value=1;sendData(this);"); }; /* цикл создающий для каждого элемента-потомка новые элементы option; сколько потомков, столько и элементов */ foreach ($arrayNodes as $Node) { $option = $replyXML->createElement("option"); $setNode = $Node->nodeName; if ($Node->nodeName == "Device") { $setNode = $Node->nodeValue; }; $option-> nodeValue = $setNode; $option->setAttribute("value",$setNode); $optgroup->appendChild($option); }; //вставляем в XML-документ получившиеся элементы $replyXML->appendChild($root); $root->appendChild($optgroup); /* если flag=1, то значит текущий элемент является Device элементом; элементы-потомки не нужны, а нужны атрибуты текущего элемента */ } else { //создаем корневой элемент $root = $replyXML->createElement("pre"); $DOMText = new DOMText(" OS URL"); $root -> appendChild($DOMText); $NamedNodeMapAttr = $DOMNode->attributes; $i = 0; /* цикл который находит все атрибуты элемента Device и для каждого атрибута создает строку с данными содержание ссылку */ while (($NodeAttr = $NamedNodeMapAttr->item($i)) != null) { if ($NodeAttr->name != "id") { $DOMText = new DOMText(" $NodeAttr->name "); $DOMElement = $replyXML->createElement("a"); $DOMElement -> setAttribute("href",$NodeAttr->value); $DOMElement -> nodeValue = $NodeAttr->value; $root -> appendChild($DOMText); $root -> appendChild($DOMElement); }; $i++; $NodeAttr = $NamedNodeMapAttr->item($i); }; $replyXML->appendChild($root); }; //отсылаем ответ клиенту echo $replyXML->saveHTML(); ?>

    AJAX - Что это?

    Когда существующих возможностей становиться мало, а совершенствовать существующее уже некуда, тогда и происходит технологический прорыв. Таким прорывом и есть AJAX (Asynchronous JavaScript and XML) - подход к построению пользовательских интерфейсов веб-приложений, при котором web-страница, не перезагружаясь, сама догружает нужные пользователю данные. AJAX - один из компонентов концепции DHTML.

    Что же дает нам эта технология. В настоящее время разработка WEB приложений стремится к разграничению клиентской части и серверной, этим и обуславливается повсеместное использование шаблонов, таких как Smarty и XSLT. Сейчас проекты становятся сложнее, и переплетать между собой различные технологии становиться слишком дорого для времени разработчика. Так, например, все стили форматирования выносятся в CSS или в XSL файлы, HTML или XML данные хранятся в других разделах, серверные обработчики в третьих, базы данных в четвертых. И если еще 5-6 лет назад практически везде можно было увидеть переплетение всего этого в одном файле, то сейчас это все чаще становиться редкостью.

    При разработке более сложных проектов возникает необходимость в структурированности и удобочитаемости кода. Не следует засорять код программиста кодом верстальщика, а код верстальщика - правками дизайнера, и так далее.

    Возникает необходимость в разграничении работы. Так, например, дизайнер будет делать свою работу, верстальщик свою, программист свою, и при этом никто друг другу мешать не будет. В итоге каждому участнику проекта достаточно будет знать только те данные, с которыми ему придется работать. В таком случае производительность группы и качество проекта повышается в разы. В настоящее время эта проблема с успехом решается путем использования шаблонов, однако это тоже создает определенные трудности, так как чтобы, скажем, подключить Smarty, необходимо подключить программный модуль обработки шаблонов, и четко связать со структурой проекта. А это далеко не всегда возможно и требует определенных затрат. Немного проще при использовании связки XML + XSL, так как они предоставляют больше возможностей, однако это альтернатива, не более. А что если посмотреть в сторону чего-то радикально нового, что позволяло бы объединить все лучше, используя возможности того, что есть? Попробуйте представить JavaScript, который обладает всем возможностями PHP или Perl, включая работу с графикой и базами данных, который имеет гораздо более удобную расширяемость и практичность, и к тому же кросс-платформен.

    Так что же такое AJAX? Впервые об Ajax заговорили после появления в феврале 2005-го года статьи Джесси Джеймса Гарретта (Jesse James Garrett) "Новый подход к веб-приложениям" . Ajax - это не самостоятельная технология. Это идея, которая базируется на двух основных принципах.

    Использование DHTML для динамичного изменения содержания страницы.

    Использование XMLHttpRequest для обращения к серверу "на лету".

    Использование этих двух подходов позволяет создавать намного более удобные WEB-интерфейсы пользователя на тех страницах сайтов, где необходимо активное взаимодействие с пользователем. Использование Ajax стало наиболее популярно после того, как компания Google начала активно использовать его при создании своих сайтов, таких как Gmail, Google maps и Google suggest. Создание этих сайтов подтвердило эффективность использования данного подхода.

    Итак подробнее: если взять классическую модель WEB-приложения:

    Клиент, набирая в строке поиска адрес интересующего его ресурса, попадая на сервер, делает к нему запрос. Сервер производит вычисления в соответствии с запросом, обращается к базе данных и так далее, после чего полученные данные идут клиенту и, в случае необходимости подставляются в шаблоны и обрабатываются браузером. Результатом является страница, которую мы видим, и которую 80% населения страны находящейся в WEB называют Интернетом. Это классическая модель, которая успела себя зарекомендовать и заслужить себе почетное место под солнцем. Это самая простая модель взаимодействия и, как следствие, самая распространенная. Однако ее все чаще становиться недостаточно. Представьте себе, on-line игру "Морской бой", в которую играют два закоренелых друга - житель ЮАР и житель Японии. Как с помощью такой модели сделать их игру максимально приятной? В любом случае данные потопленных кораблей будут хранится на сервере, и что бы проверить не походил ли оппонент, необходим будет каждый раз обновлять страницу и подгущать устаревшие данные. "Но ведь люди придумали кэширование" - скажете вы и будете абсолютно правы, но легче от этого не становиться. Кэширование всего лишь ускорит время взаимодействия с сервером, но не избавит от необходимости перезагружать страницу. Как вариант можно поставить определенное время самообновления, но и в этом случае страница будет перезагружаться полностью.

    Теперь посмотрим на модель взаимодействия AJAX:

    Последовательность действий клиента сохраняется и он, скорее всего не поймет того, что будет происходить, а слово AJAX будет ассоциироваться только с названием футбольного клуба. Но на стороне сервера все выглядит иначе.

    При обращении к серверу, генерируется страница, которая будет отображаться пользователю, и предлагать ему совершить интересующую его последовательность действий. При сознательном (хотя и не обязательно) выборе клиента, его запрос будет обращаться к AJAX модулю, который и будет производить все интересующие его вычисления и работу с сервером как таковым. Но в чем же новшество? Основное отличие в том что этот метод дает нам возможность динамически обращаться к серверу и выполнять интересующие нас действия. Например, нам нужно выполнить обращение к базе данных и получить интересующие нас данные которые затем будем использовать. Данные мы будем хранить в XML файле который будет формироваться динамически, таким образом:

    Создаем новый объект JavaScript:

    Var req = new ActiveXObject("Microsoft.XMLHTTP"); (для IE) var req = new XMLHttpRequest(); (Для всего остального)

    Затем пишем функцию использующий этот объект

    Var req; function loadXMLDoc(url) { // branch for native XMLHttpRequest object if (window.XMLHttpRequest) { req = new XMLHttpRequest(); req.onreadystatechange = processReqChange; req.open("GET", url, true); req.send(null); // branch for IE/Windows ActiveX version } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); if (req) { req.onreadystatechange = processReqChange; req.open("GET", url, true); req.send(); } } }

    Теле HTML файла пишем скрипт, который будет:

    Function checkName(input, response) { if (response != ""){ // Response mode message = document.getElementById("nameCheckFailed"); if (response == "1"){ message.className = "error"; }else{ message.className = "hidden"; } }else{ // Input mode url = "http://localhost/xml/checkUserName.php?q=" \\ + input; loadXMLDoc(url); } }

    В файле localhost/xml/checkUserName.php мы обрабатываем данные, полученные из командной строки в данном случае в переменной q. А результат сохраняем в XML структуре, которую храним в этом же файле. Так мы можем получить и обработать данные, полученные из БД, или что-нибудь другое необходимое нам. К тому же сервер будет обрабатывать только те данные, которые нам необходимо обновить, а не всю страницу в случае ее перезагрузки.

    Теперь вернемся к двум друзьям - любителям морского боя: ввиду появления данного новшества, мы можем сделать следующее: поставить проверку в течении каждых трех секунд XML файла данная проверка подразумевает под собой проверку базы данных на предмет новой записи, то есть - сделанного оппонентом хода. Если ход был сделан, страница без перезагрузки топит корабли, тем самым, портя настроение участникам водных баталий. Данная функциональность достигается элементарным использованием Javascript и таблиц стилей. Этот пример достаточно наглядный, однако далеко не полный, применение данной технологии гораздо существенней.

    Однако не все так просто. Давайте теперь рассмотрим отрицательные черты.

    Во-первых - мы можем передавать данные только методом GET, соответственно большие объемы данных придется оставить в покое. Данная проблема уже не раз поднималась в разных источниках, но господа, есть ведь Сookies, которые вполне приемлемы в случаях передачи больших данных, чем может вместить в себя GET запрос, а Javascript в свою очередь имеет функции для работы с ними.

    Вторая проблема - кросс-браузерность. Объект XMLHttpRequest еще не является частью какого-либо стандарта (хотя нечто подобное уже было предложено в спецификации W3C DOM Level 3 Load and Save). Поэтому существует два отличных друг от друга метода вызова этого объекта в коде скрипта. В Internet Explorer объект ActiveX вызывается так:

    Var req = new ActiveXObject("Microsoft.XMLHTTP");

    В Mozilla и Safari это делается проще (так как там это объект, встроенный в JavaScript):

    Var req = new XMLHttpRequest();

    Все современные браузеры поддерживают данный объект и проблемы возникнут только у 1,8% пользователей (согласно данными статистики компании SpyLog), которые пользуются очень старыми версиями браузеров, не поддерживающими этот объект.

    И, наконец, защищенность. На этом остановимся поподробнее. Основная проблема заключается в том, что все данные и исходный код JavaScript функций можно увидеть путем просмотра исходного кода страницы, соответственно злоумышленник может проследить логику выполнения запросов и при определенном стечении обстоятельств выполнить необходимый ему набор команд. Это не играет особой роли, когда у нас идет простое сопоставление данных, но что делать в более сложных ситуациях, например при авторизации, и как в таком случае передавать пароли. Как уже было сказано выше, на помощь приходят Cookies. Необходимые данные можно пересылать с их помощью, так же их и обрабатывать. Возьмем пример, в котором пользователь будет проходить аутентификацию с помощью технологии которой посвящена статья.

    Генерации страницы, мы формируем уникальные значения, которые затем помещаем в переменные сервера. И в Cookies браузера, затем при авторизации мы получаем имя пользователя и его пароль, которые нам необходимо передать модулю обработки на сервере.

    После того как пользователь ввел данные и нажал кнопку Submit его пароль заноситься в Cookies, а имя пользователя передается открыто - ссылкой например http://www.mubestajax.com/ajax.php?login=pupkin при получении данных сервер, в первую очередь проводит сверку полученных данных. Так как значения которые мы генерировали с начала работы сервера а затем передавали их глобальным переменным сервера и cookies должны совпадать, то при проверке целостности переданных данных в случае несовпадения программа перестает работать. Если же все прошло хорошо, то извлекаются все необходимые данные и проводятся необходимые вычисления и работы. Такой способ защиты достаточно прост и эффективен. Но для больших проектов он не подойдет.

    Когда на первый план выходит безопасность, лучше использовать более сложные и надежные решения. Но в большинстве случаев данных мер предосторожности будет более чем достаточно, так как использование более сложных модулей влечет за собой использование технологий которые не всегда входят в состав стандартного программного обеспечения современных серверов, основная черта которых - простота. Именно по этому такие технологии как MySQL и PHP получили очень широкое распространение, т.к. они обеспечивают простоту работы при своей небольшой ресурсоемкости и достаточной надежности. А к рамках данного программного обеспечения как нельзя лучше подойдет решение предложенное выше.

    В заключении хотелось бы сказать, что AJAX с одной стороны - огромный скачок в WEB инженерии, но с другой стороны это давно назревавший этап эволюции, который открыл новые горизонты в сфере разработки программного обеспечения. В тоже время эта технология еще довольно "сыра" так как ее использование на данный момент скорее приятное исключение. Но я уверен, что о ней еще не раз все услышат.