Кафедра: АСОИиУ
Лабораторная работа
На тему: Создание приложений на AJAX
Душанбе, 2009
Обзор Ajax
ИспользованиеAjax начинается с JavaScript-объекта, называемого XMLHttpRequest. Как ипредполагает имя, он позволяет в клиентской части вашего кода выполнятьHTTP-запросы и анализирует ответ XML-сервера. Первый шаг в изучении Ajax — созданиеданных объекта XMLHttpRequest. Метод протокола HTTP при использовании длязапроса (GET или POST) и пути затем устанавливаются в объекте XMLHttpRequest.
Теперьвспомните, почему «а» в слове Ajax обозначает «асинхронный»?Когда вы посылаете HTTP-запрос, вы не хотите, чтобы браузер повис, ожидаяответа сервера. Вместо этого вы хотите также реагировать на действиепользователя на странице и работать с ответом сервера, когда тот в конечномитоге придет. Чтобы выполнить это, вам нужно зарегистрировать функцию обратноговызова с помощью XMLHttpRequest и послать асинхронно запрос XMLHttpRequest. Контрольостанется за браузером, а функция обратного вызова будет вызвана, когда придетответ сервера.
НаWeb-сервере запрос придет так же, как и любой другой HttpServletRequest. Послеанализа параметров запроса, сервлет выполнит все необходимые действия дляприложения, сериализует его запрос в формате XML и вписывает его вHttpServletResponse.
Возвращаяськ клиенту, функция обратного вызова, зарегистрированная в XMLHttpRequest,теперь выполняется для того, чтобы обработать XML-документ, возвращаемыйсервером. Наконец, пользовательский интерфейс обновляется в соответствии сданными сервера, используя JavaScript-код для преобразования страницы. Рисунок1 — диаграмма последовательности изучения Ajax.
/>
Рисунок 1. ОбзорAjax
Я начну ссамого начала: создание XMLHttpRequest и отправка его из браузера. К сожалению,метод создания XMLHttpRequest отличается от браузера к браузеру. Функция вJavaScript в листинге 2 сглаживает эти трудности разных браузеров, определяякорректный подход для данного браузера и возвращая XMLHttpRequest готовым киспользованию. Лучше всего думать об этом как о коде-шаблоне: простоекопирование его в вашу библиотеку JavaScript и его использование, когда вампонадобится XMLHttpRequest.
Листинг 2. СозданиеXMLHttpRequest для разных браузеров
/*
* Возвращаетновый XMLHttpRequest объект или false, если браузер его не поддерживает
*/
function newXMLHttpRequest() {
var xmlreq = false;
if (window. XMLHttpRequest) {
// Создадим XMLHttpRequest объект дляне-Microsoft браузеров
xmlreq = new XMLHttpRequest();
} else if (window. ActiveXObject) {
// Создадим XMLHttpRequest с помощью MS ActiveX
try {
// Попробуемсоздать XMLHttpRequest для поздних версий
// Internet Explorer
xmlreq = new ActiveXObject(«Msxml2. XMLHTTP»);
} catch (e1) {
// Неудалось создать требуемый ActiveXObject
try {
// Пробуемвариант, который поддержат более старые версии
// Internet Explorer
xmlreq = new ActiveXObject(«Microsoft. XMLHTTP»);
} catch (e2){
// Не всостоянии создать XMLHttpRequest с помощью ActiveX
}
}
}
returnxmlreq;
}
Позже мыобсудим технические приемы для браузеров, которые не поддерживаютXMLHttpRequest. А сейчас примеры предполагают, что функция newXMLHttpRequest излистинга 2 всегда возвращает значение XMLHttpRequest.
Возвращаясьобратно к сценарию примера корзины покупателя, я бы хотел воспользоваться Ajaxдля случаев, когда пользователь нажимает Add to Cart кнопку для каталогапродуктов. Оператор onclick, вызванный addToCart(), ответственен за обновлениесостояния корзины в Ajax-вызове (см. листинг 1). Как показано в листинге 3,первое, что необходимо сделать addToCart(), — получить данные XMLHttpRequest спомощью вызова функции newXMLHttpRequest() из листинга 2. Следующим шагом онрегистрирует функцию обратного вызова для получения ответа сервера, (я объяснюэто подробно позже, смотрите листинг 6).
Посколькузапрос изменит состояние на сервере, я буду пользоваться методом HTTP POST,чтобы это выполнить. Отправка данных методом POST выполняется в три шага. Во-первых,мне необходимо открыть соединение с источником сервера, с которым я соединяюсь,что в данном случае является сервлетом, занесенным в cart. do страницы. Далее яустанавливаю заголовок в XMLHttpRequest, говорящий о том, что содержимоезапроса — закодированные данные. И, наконец, я посылаю запрос с закодированнымиданными в теле содержимого.
Листинг 3показывает все эти шаги вместе.
Листинг 3. ПосылаемXMLHttpRequest для кнопки Add to Cart
/*
* Добавляетпродукт, определенный его кодом,
* в корзинупокупателя
* itemCode — код продукта для добавления.
*/
functionaddToCart(itemCode) {
// Возвращаетсодержимое XMLHttpRequest
var req =newXMLHttpRequest();
// Оператордля получения сообщения обратного вызова
// изобъекта запроса
var handlerFunction= getReadyStateHandler(req, updateCart);
req. onreadystatechange = handlerFunction;
// ОткрываемHTTP-соединение с помощью POST-метода к сервлетукорзины покупателя.
// Третийпараметр определяет, что запрос асинхронный.
req. open(«POST», «cart. do», true);
// Определяет,что в содержимом запроса есть данные
req. setRequestHeader(«Content-Type»,
«application/x-www-form-urlencoded»);
// Посылаемзакодированные данные, говорящие о том, что я хочу добавить
// определенныйпродукт в корзину.
req. send(«action=add&item=»+itemCode);
}
После всегоэтого вы поняли первую часть в механизме Ajax — собственно создание и передачаHTTP-запроса от клиента. Следующим шагом будет код сервлета на языке Java дляобработки запроса.В начало. Обработка запроса сервлета
ОбработкаXMLHttpRequest с помощью сервлета такая же, как и обработка обычногоHTTP-запроса из браузера. Закодированные данные, отсылаемые в содержимомзапроса POST, могут быть получены с помощью вызовов HttpServletRequest. getParameter().Ajax запрашивает участие в HttpSession, такой же, как и в регулярных Webзапросах из приложения. Это полезно для примера сценария корзины покупателя,поскольку позволяет мне заключить состояние пользовательской корзины покупателяв JavaBeans и удерживать это состояние во время сессии между двумя запросами.
Листинг 4 — частьпростого сервлета, который обрабатывает Ajax запросы, чтобы обновить корзинупокупателя. Cart получается из сессии пользователя и его состояние обновляетсяв соответствии с параметрами запроса. Cart затем сериализуется в XML-формате, аXML образуется в ServletResponse. Важно установить тип содержимого запроса вapplication/xml, в противном случае XMLHttpRequest не будет анализироватьсодержимое ответа.
Листинг 4. Кодсервлета для обработки Ajax-запросов
public void doPost(HttpServletRequest req,
HttpServletResponse res)
throws java. io. IOException {
Cart cart = getCartFromSession(req);
String action = req. getParameter(«action»);
String item = req. getParameter(«item»);
if ((action! = null) &&(item! = null)) {
// Добавитьили удалить продукт из Cart
if («add». equals(action)) {
cart. addItem(item);
} else if («remove». equals(action)) {
cart. removeItems(item);
}
}
// Сериализуем состояние Cart в XML-формате
StringcartXml = cart. toXml();
// Записываемполученный XML в запрос.
res. setContentType(«application/xml»);
res. getWriter(). write(cartXml);
}
Листинг 5показывает пример XML-кода, полученный с помощью метода Cart. toXml(). Сделатьэто достаточно просто. Запомните атрибут generated в cart элементе, которыйявляется временной меткой, полученной с помощью System. currentTimeMillis().
Листинг 5. Примерсериализации в XML-формате объекта Cart
Hat
2
Chair
1
Dog
1
Если вывзглянете на Cart. java в источнике приложения, доступном в секции Загрузка, выувидите, что XML получается просто с помощью соединения строк вместе. Примененныйв примере, этот способ — зачастую самый худший способ получить XML из кода наязыке Java. Я предложу приемы интереснее и лучше в следующей части этой серии.
Теперь вызнаете, что CartServlet запрашивается с помощью XMLHttpRequest. Следующим шагомбудет возвращение к коду клиента, где вы можете увидеть, как ответ вXML-формате будет использоваться для обновления состояния страницы.
Запрос, обрабатывающийся с помощью JavaScript
readyStateсвойство XMLHttpRequest — числовое значение, которое определяет статус циклазапроса. Оно изменяется от 0 для «неопределенного» и до 4 для «завершенного».Каждый раз, когда меняется readyState, появляется событие readystatechange ивызывается с помощью onreadystatechange свойства оператор.
В листинге 3вы увидели, как вызывалась функция getReadyStateHandler() для созданияоператора. Этот оператор затем приписывается свойству onreadystatechange. getReadyStateHandler()использовал тот факт, что функции — объекты первого класса в JavaScript. Этообозначает, что функции могут быть параметрами к другим функциям и могут такжесоздавать и возвращать значения других функций. Обязанности getReadyStateHandler()- возвращать значение функции, которая проверяет, завершился ли XMLHttpRequestи был отослан ли ответ в XML-формате в оператор, определенный вызовом. Листинг6 — код для getReadyStateHandler().
Листинг 6. ФункцияgetReadyStateHandler(). /*
* Возвращаетфункцию, которая ожидает, чтобы закончился
*определенный XMLHttpRequest, затем передает его ответ в XML-формате
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|------ — XML error: The previous line is longer than the max of 90characters — --------|
* заданномуоператору * req — это XMLHttpRequest, чье состояние изменяется
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|------ — XML error: The previous line is longer than the max of 90characters — --------|
*responseXmlHandler — функция, передаваемая запрос XML
*/
functiongetReadyStateHandler(req, responseXmlHandler) {
// Возвращаетнеопределенную функцию, которая считывает
// данныеXMLHttpRequest return function () {
// Еслитребуется статус «закончен»
if (req. readyState== 4) {
// Проверяем,пришел ли успешный ответ сервера
if (req. status== 200) {
// Передает XMLоператору
responseXmlHandler(req. responseXML);
} else {
// Возниклаошибка HTTP
alert(«HTTP error: „+req. status);
}
}
}
}
О getReadyStateHandler()
getReadyStateHandler() — относительно сложный кусок кода,особенно если вы ранее не знали JavaScript. Но с другойстороны преимущество в том, что с помощью включения этой функции в вашубиблиотеку JavaScript вы можете спокойно обрабатывать Ajax-запросы сервера без работы с данными XMLHttpRequest.Важно также, что вы понимаете, как использовать getReadyStateHandler()в вашем собственном коде.
В листинге 3вы видели getReadyStateHandler(), вызванный так: handlerFunction = getReadyStateHandler(req, updateCart). Функция,возвращаемая с помощью getReadyStateHandler(), в этомслучае проверит, выполнен ли XMLHttpRequest впеременной req и затем вызовет функцию, называющуюся updateCart, с помощью запроса в формате XML.Данные корзины
Листинг 7 — собственносам код updateCart(). Функция запрашивает XML-документ корзины покупателя, используя DOM-вызовыи обновляя Web-страницу (см. листинг 1) для того, чтобыпоказать новые элементы содержимого новой корзины. Обратите здесь внимание навызовы, использованные для того, чтобы достать данные из XML DOM. Атрибут generatedэлемента cart, созданный, когда Cartбыл сериализован в формате XML, проверяется, чтобыудостовериться, что более новые данные корзины не перезаписаны более старымиданными. Ajax-запросы по своей сути асинхронны, поэтомуэта проверка предохраняет против ответов сервера, которые прибывают вбеспорядке.
Листинг 7. Обновлениестраницы с отображением XML документа корзины
function updateCart(cartXML) {
// Получитькорневой " cart" элемент из документа
var cart = cartXML. getElementsByTagName(“cart») [0] ;
// Проверим,что более ранний документ корзины не был обработан еще
var generated = cart. getAttribute(«generated»);
if (generated > lastCartUpdate) {
lastCartUpdate = generated;
// Очистимсписок HTML, необходимый для отображения содержимогокорзины
var contents = document. getElementById(«cart-contents»);
contents. innerHTML = "";
// Соединяемпродукты в корзине
var items = cart. getElementsByTagName(«item»);
for (var I = 0; I
var item = items [I] ;
// Достаемключевые понятия из имени и элементов количества
var name = item. getElementsByTagName(«name») [0]
. firstChild. nodeValue;
var quantity = item. getElementsByTagName(«quantity») [0]
. firstChild. nodeValue;
// Создаем идобавляем список продуктов, HTML элемент для этогопродукта
var li = document. createElement(«li»);
li. appendChild(document. createTextNode(name+" x "+quantity));
contents. appendChild(li);
}
}
// Обновляемитого в корзине, используя значение из документа корзины
document. getElementById(«total»). innerHTML =
cart. getAttribute(«total»);
}
После того,что мы проделали, путешествие в Ajax закончено, хотя вымогли бы захотеть получить Web-приложения и увидеть ихв действии (смотрите секцию Загрузка). Пример очень прост, многие строки в немеще можно улучшить. К примеру, я включил код сервера, чтобы убрать продукты изкорзины, но не сделал доступ к ним из интерфейса. Для вас будет хорошимупражнением попытаться построить в приложении существующего JavaScript-кодаданную функциональность.
Задачи использования Ajax
Как и вомногих других технологиях, в Ajax есть множество способов сделать ошибки. Некоторыеиз проблем, которые я обсуждаю здесь, в данный момент имеют удачные и легкиерешения, но могут быть улучшены в качестве Ajax-продуктов. Поскольку сообществоразработчиков дает опыт в разработке Ajax-приложений, лучшие практики исправочники будут записаны.Польза XMLHttpRequest
Одной изважных задач, встающих перед лицом Ajax-разработчиков, является вопрос, какответить, когда XMLHttpRequest недоступен. В то время как большинствосовременных браузеров поддерживают XMLHttpRequest, всегда есть пользователи, укоторых XMLHttpRequest не поддерживается или чья безопасность браузера не далавыполниться XMLHttpRequest. Если вы разрабатываете Web-приложение, котороеприменимо в корпоративной сети, вероятно, у вас есть преимущество вопределении, какие браузеры поддерживаются и когда XMLHttpRequest всегдадоступен. Если вы разрабатываете для публичной сети, то вы должны знать, что,предполагая, что XMLHttpRequest доступен, вы отрекаетесь от пользователей болеестарых браузеров, браузеров людей с закрытым доступом или легковесных браузеровс сервисом против выполнения вашего приложения.
Поэтому вамнеобходимо попытаться сделать ваше приложение с «изящным ухудшением»,чтобы остаться функциональным даже в браузерах без поддержки XMLHttpRequest. Впримере корзины покупателя лучший способ ухудшить приложение — кнопки Add toCart, выполняющие обычную роль, обновляя страницу для нового статуса корзины. Спомощью Ajax можно добавить в страницу через JavaScript-код, когда страницазагружена, прикрепить оператор JavaScript к каждой кнопке Add to Cart, еслитолько доступен XMLHttpRequest. Другой способ — определить XMLHttpRequest,когда пользователь вошел, и затем обеспечить любую Ajax версию приложения иливерсию, основанную на обычных формах, если понадобится.Относительно функциональности (usability)
Некоторые иззадач функциональности в Ajax — достаточно общие. К примеру, может быть, важнопозволить пользователям узнать, что их ввод зарегистрирован, поскольку обычныймеханизм обратной связи («песочные часы» курсора и «крутящийсябраузер») не применим для XMLHttpRequest. Выход — заменить кнопкиПодтвердить сообщением «Обновляется…», чтобы пользователи не нажималинесколько раз на кнопки в ожидании запроса.
Другаязадача состоит в том, что пользователи могут не заметить, что части страницы,которые они видят, уже обновлены. Вы можете решить эту проблему, используямножество визуальных техник и приемов, например, нарисовать глаз пользователя кобновленным местам страницы. Другие задачи, вызванные обновлением страницы приAjax, включают в себя нерабочую кнопку возвращения обратно, некорректноеотображение адреса или невозможность добавить в избранное. Смотрите секциюРесурсы для статей, которые специализируются на задачах функциональности вAjax-приложениях.Перегрузка сервера
ВыполнениеAjax-интерфейса в месте, где действуют обычные формы, может привести к тому,что неожиданно возрастет количество запросов к серверу. К примеру, обычныйGoogle Web-поиск вызывает один запрос на сервер, когда пользователь нажимаеткнопку поиска. Однако, Google Suggest, который пытается автоматически закончитьваше действие поиска, посылает несколько запросов к серверу, пока пользовательпечатает. Когда вы разрабатываете Ajax-приложение, помните, сколько запросов выотошлете серверу, и возникнет ли из-за этого перегрузка сервера. Вы можетеуменьшить перегрузку сервера, храня запросы на стороне клиента и пряча запросыв клиенте, если это возможно. Вы можете также попытаться разработатьAjax-приложения так, чтобы большая часть действия могла быть выполнена в клиентскойчасти вашего кода без необходимости соединения с сервером.Разбираясь с асинхронностью
Очень важнопонять, что нет гарантии, что XMLHttpRequest закончатся в той самойочередности, в которой они были отправлены. В самом деле, вам необходимо допустить,что они не вернутся так, и разработать ваше приложение с учетом этой поправки. Впримере корзины покупателя время отметки последнего обновленного продуктараньше гарантировало, что более новые данные корзины не могут быть переписаныболее старыми, (см. листинг 7). Этот недоработанный подход работает длясценария корзины покупателя, но может не выполняться в других сценариях.
Заключение
Вы, должнобыть, сейчас хорошо понимаете основные принципы программирования в Ajax иособенности компонент сервера и клиента, которые имеют место в Ajax. Этофундамент Ajax-приложения на языке Java. В добавление к сказанному вам следуетпонимать некоторые из задач разработки более высокого абстрактного уровня,которые появляются с техническими приемами в Ajax. Создание успешногоAjax-приложения потребует общего подхода, начиная с разработки интерфейсов спомощью JavaScript-кода и заканчивая архитектурой сервера, но вы теперьвооружены основными принципами программирования в Ajax, необходимыми длярассмотрения всех этих аспектов.
Оченьхорошо, если вы чувствуете уныние из-за сложности написания большихAjax-приложений с использованием приемов, продемонстрированных здесь. Такиеструктуры, как Struts, Spring и Hibernate, развиваются, разрабатывая мелкиедетали Servlet API и JDBC, поэтому появляются средства для облегченияAjax-разработки. Некоторые из них сфокусированы исключительно на проблемахклиентской стороны кода, находя легкие пути для добавления визуальных эффектовк вашим страницам или тщательно анализируя использование XMLHttpRequest. Некоторыеидут дальше, разрабатывая средства для автоматической генерацииAjax-интерфейсов со стороны сервера. Эти структуры выполняют тяжелую работу занас, поэтому вы можете сконцентрироваться на подходах более высокого уровня. Ярассмотрю некоторые из них в этой серии.
СообществоAjax быстро развивается, и здесь есть большое количество важной информации. Передтем как читать следующую часть этой серии статей, я бы рекомендовал вамознакомиться со статьями в секции Ресурсы, особенно если вы — «новичок»в Ajax или в разработке клиентской части кода. Также вам понадобится времяразобраться с примером кода и подумать о способах его улучшения.
В следующейстатье этой серии я рассмотрю XMLHttpRequest более детально и предложу способысоздания XML сразу из вашего JavaBeans. Я также покажу вам альтернативы к XMLдля передачи Ajax-данных, такие как нотация JSON.