AJAX: Asynchronous JavaScript And XML

Морев Н. В.


Содержание

Введение

AJAX (Asynchronous XML And JavaScript) -- это способ построения веб-приложений на основе объединения целого ряда технологий:

Аббревиатура AJAX, также как DHTML[*] или LAMP[*], не является технологией, а обозначает совместное использование группы существующих технологий.

В качестве платформы для выполнения AJAX-приложений может выступать любой популярный браузер последних версий: Mozilla Firefox, Microsoft Internet Explorer, Opera, Apple Safari.

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


Рис. Модель веб-приложения


Рис. Сравнение традиционной модели взаимодействия в веб-приложениях и AJAX

На самом деле идея сделать веб-приложения более похожими на обычные приложения в смысле пользовательского интерфейса не нова и такие возможности уже давно могли быть реализованы с помощью различных технологий: Macromedia Flash, Java Applets, Microsoft ActiveX и т.д. Однако, все они не получили очень широкого распространения из-за того, что использовали частные закрытые решения отдельных фирм, требовали установки дополнительного программного обеспечения и не имели достаточной переносимости между платформами. AJAX лишен этого недостатка.

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

Другие недостатки AJAX связаны с удобством использования -- нарушается работа кнопки «Назад» браузера и возможность делать закладки на определенное состояние приложения. Для обхода этих недостатков также разработаны и применяются различные техники.

Внедрение AJAX идет полным ходом как на любительских сайтах, так и на крупных сайтах, обслуживающих миллионы запросов ежедневно[*].


Простой пример

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

Для того, чтобы испытать данный пример необходимо иметь доступ к веб-серверу с поддержкой PHP. Полные исходные коды примера приведены на github-е (в архиве).

Серверная часть

Серверная часть примера представляет собой PHP-скрипт, являющийся простейшим веб-сервисом. Понятие веб-сервиса в общем случае не ограничивается SOAP/WSDL-сервисами. Возможно применять и другие XML-протоколы. Один из наиболее популярных из них XML/RPC. В нашем примере для простоты между сервисом и клиентом будут передаваться чистые данные без какой-либо XML-разметки.

Функция разработанного веб-сервиса -- принимать на входе методом POST число и выдавать на выходе это число возведенное в квадрат. Число передается в виде строки и никакой проверки правильности входных данных не производится.

Серверная часть примера test.php

<?php // vim:ts=4

$x = $GLOBALS['HTTP_RAW_POST_DATA'];
print($x * $x);

?> 

Клиентская часть

Клиентская часть примера представляет собой HTML-файл с внедренными JavaScript-инструкциями. Клиент посылает веб-сервису число и асинхронно принимает ответ. Асинхронность означает, что веб-браузер не подвисает во время запроса и пользователь может продолжать работать пока запрос выполняется. (рис. 2)

Основой поддержки AJAX в браузере является объект XMLHttpRequest для браузеров Mozilla, Safari и прочих, и объект ActiveX Microsoft.XMLHTTP для браузеров фирмы Microsoft.

Ответ веб-сервиса выдается пользователю с помощью стандартной функции JavaScript alert(), но чаще всего он внедряется непосредственно в страницу с помощью DOM, встроенной в браузер.

Клиентская часть примера test.html

<html>
<head>
</head>

<body>

<script type="text/javascript" language="javascript">
// <![CDATA[		

var http_request = false;

function makeRequest(url) {
	http_request = false;

	if (window.XMLHttpRequest) { // Mozilla, Safari,...
		http_request = new XMLHttpRequest();
		if (http_request.overrideMimeType) {
			http_request.overrideMimeType('text/xml');
		}
	} else if (window.ActiveXObject) { // IE
		try {
			http_request = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				http_request = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {}
		}
	}

	if (!http_request) {
		alert('Giving up :( Cannot create an XMLHTTP instance');
		return false;
	}

	http_request.onreadystatechange = alertContents;
	http_request.open('POST', url, true);
	http_request.send('5');
}

function alertContents() {
	if (http_request.readyState == 4) {	// COMPLETED
		if (http_request.status == 200) {	// HTTP STATUS
			alert(http_request.responseText);
		} else {
			alert('There was a problem with the request.');
		}
	}
}

// ]]>	
</script>

<span
	style="cursor: pointer; text-decoration: underline"
	onclick="makeRequest('http://freedomnet.ru/services/simple/test.php')">

	Make a request

</span>

</body>
</html>

Испытание примера


Рис. Пользовательский интерфейс примера

Заголовки пересылаемые при вызове веб-сервиса


POST /services/simple/test.php HTTP/1.1
Host: freedomnet.ru
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7
Accept: text/xml,application/xml,application/xhtml+xml,index.htm;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/xml
Content-Length: 1
Pragma: no-cache
Cache-Control: no-cache

5

HTTP/1.x 200 OK
Date: Wed, 09 Nov 2005 11:40:36 GMT
Server: Apache/2.0.54
X-Powered-By: PHP/5.0.5
Content-Length: 2
Keep-Alive: timeout=10, max=100
Connection: Keep-Alive
Content-Type: index.htm; charset=WINDOWS-1251
Content-Language: ru

25

Библиотеки

В последнее время в сообществе веб-разработчиков AJAX является горячей темой. Неудивительно, что уже появилось множество библиотек поддержки AJAX для различных языков программирования. [2,3] Среди них можно выделить некоторую классификацию. Во-первых, по языку программирования:

JavaScript
Эти библиотеки работают на стороне браузера, что естественно накладывает определенные ограничения на размер библиотеки, а следовательно и на ее функциональность. Эти библиотеки предоставляют следующую функциональность:
PHP, Java и др.
Работают на стороне сервера. Предоставляют следующую функциональность:

По функциональности AJAX-библиотеки можно разделить на:

Компонентно-ориентированные
Реализуют отдельные визуальные компоненты для реализации на странице, такие как форма ввода текста с автодополнением, валидация форм, progress bar и др.
Универсальные
Предоставляют уровень абстракции для более удобной работы с AJAX. Позволяют реализовывать свою функциональность.

Компонентно-ориентированные библиотеки

Рассмотрим компонентно-ориентированные библиотеки поддержки AJAX на примере компонентов для JavaServer Faces (JSF).

JavaServer Faces -- это набор серверных компонентов Java для создания HTML-интерфейсов. Для него существуют компоненты, использующие Ajax.[*]

Рассмотрим компонент Progress Bar, который показывает ход выполнения некоторого процесса на сервере в виде заполняющейся полосы и процентов выполнения. (рис. 4)

Данный пример выполняется в любом Java-веб-контейнере, поддерживающем стандарт сервлетов. Я использовал Apache Tomcat 5.5. Полные исходные коды примера приведены на сайте https://bpcatalog.dev.java.net/nonav/ajax/progress-bar-jsf/frames.html.


Рис. Компонент Progress Bar

Текст JSP-страницы с компонентом Progress Bar

<%-- Copyright 2004-2005 Sun Microsystems, Inc.  All rights reserved.  You may not modify, 
use, reproduce, or distribute this software except in compliance with the terms of the 
License at: 
 http://developer.sun.com/berkeley_license.html
 --%>

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/blueprints/ajax/progress-bar" prefix="d" %>

<f:view>

<html>
<head>

<title>AJAX enabled Button and Progress Bar</title>
</head>
<body>

  <h:form id="form">
  <table>

    <tr><td><d:progressBar id="progressBar" 
                           value="#{process.percentage}" 
                           interval="#{process.pollInterval}" 
                           action="complete" />
        </td> <td> </td></tr>
        
  </table>
  
  <p>For a detailed description of this component, please see <a href="https://bpcatalog.dev.java.net/ajax/progress-bar-jsf/">the blueprints
  catalog entry</a>.</p>

</h:form>
</f:view>

</body>
</html> 

Переменная process соответствуют классу ProcessBean:

Класс ProcessBean

/* Copyright 2005 Sun Microsystems, Inc. All rights reserved. You may not modify, use, reproduce, or distribute this software except in compliance with the terms of the License at: http://developer.sun.com/berkeley_license.html
 */

/*
 * $Id: ProcessBean.java,v 1.2 2005/06/11 06:05:10 edburns Exp $
 */
 
package com.sun.j2ee.blueprints.bpcatalog.ajax.progressbar;

import java.util.Random;

public class ProcessBean {
    
    public ProcessBean() {
    }
    
    private int percentage = 0;
    private int increment = 10;
    
    public int getPercentage() {
        if (100 < percentage) {
            percentage = 0 - increment;
        }
        return percentage += increment;
    }
    
    /**
     * Getter for property randomPercentage.
     * @return Value of property randomPercentage.
     */
    public int getRandomPercentage() {
        
        return random.nextInt(101);
    }
    
    private Random random = new Random();

    /**
     * Holds value of property pollInterval.
     */
    private int pollInterval = 250;

    /**
     * Getter for property pollInterval.
     * @return Value of property pollInterval.
     */
    public int getPollInterval() {

        return this.pollInterval;
    }

    /**
     * Setter for property pollInterval.
     * @param pollInterval New value of property pollInterval.
     */
    public void setPollInterval(int pollInterval) {

        this.pollInterval = pollInterval;
    }
    
} 

Пример на Java

Для написания примера AJAX-приложения на Java я выбрал библиотеку DWR[*].

Библиотека Direct Web Remoting (DWR) позволяет «экспортировать» код Java в код JavaScript веб-страницы и непосредственно вызывать Java-методы из веб-страницы. На самом деле эта библиотека реализует удаленные вызовы с помощью технологий AJAX. Кроме того она предоставляет удобные JavaScript-функции для манипулирования содержимым страницы через DOM.

Для выполнения данного примера необходим Java-веб-контейнер, поддерживающий стандарты сервлетов. Я использовал Apache Tomcat 5.5. Кроме того необходимо скачать и установить в контейнер библиотеку DWR. Полные исходные коды примера находятся на github-е (в архиве).

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

Исходный код

Java-код примера состоит всего из одного простого класса:

Calculator.java

package org.koliamorev.dwr;

public class Calculator {
	public float Square(float num) {
		return num * num;
	}
}

Этот класс через десриптор dwr.xml экспортируется в JavaScript код:

dwr.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
    "http://www.getahead.ltd.uk/dwr/dwr10.dtd">

<dwr>
	<allow>
		<create creator="new" javascript="Calculator">
			<param name="class" value="org.koliamorev.dwr.Calculator" />
		</create>
	</allow>
</dwr>

В JSP-странице мы включаем сгенерированные заглушки для класса Calculator, а также вспомогательный JavaScript-код библиотеки DWR. Кроме того здесь мы задаем функцию назначенную для кнопки «Отправить» и функцию-callback для получения результатов вычислений.

index.jsp

<%@ page language="java" contentType="index.htm; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="index.htm; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type='text/javascript' src='dwr/engine.js'></script>
<script type='text/javascript' src='dwr/interface/Calculator.js'></script>
<script type='text/javascript' src='dwr/util.js'></script>
<script type='text/javascript'> 
// <![CDATA[
function sendNumber()
{
    var number = DWRUtil.getValue("number");
    DWRUtil.setValue("result", "");
    Calculator.Square(gotResult, number);
}

function gotResult(number)
{
    DWRUtil.setValue("result", number);
}
// ]]> 
</script>

<p>
  Введите число:
  <input id="number"/>
  <input type="button" value="Возвести в квадрат"
      onclick="sendNumber()"/>
</p>
<p>Ответ: <span id="result"></span></p>
</body>
</html> 

JavaScript-заглушки Java-классов генерируются каждый раз во время вызова страницы. Происходит это через специальный сервлет библиотеки DWR. Поэтому в дексрипторе веб-приложения web.xml мы назначаем переадресацию на этот сервлет всех вызовов адресов вида dwr/*.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
	xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<display-name>dwr</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>

	<servlet>
		<servlet-name>dwr-invoker</servlet-name>
		<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
		<init-param>
			<param-name>debug</param-name>
			<param-value>true</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>dwr-invoker</servlet-name>
		<url-pattern>/dwr/*</url-pattern>
	</servlet-mapping>
</web-app>

Испытание


Рис. Пользовательский интерфейс примера

Обмен данными с сервером

http://localhost:8080/dwr/dwr/exec/Calculator.Square

POST /dwr/dwr/exec/Calculator.Square HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7
Accept: text/xml,application/xml,application/xhtml+xml,index.htm;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/xml
Content-Length: 119
Cookie: JSESSIONID=E0ACB6407919818A27C1EBA4DD0A6753
Pragma: no-cache
Cache-Control: no-cache

callCount=1
c0-scriptName=Calculator
c0-methodName=Square
c0-id=7260_1132038473906
c0-param0=string:3.1415926
xml=true


HTTP/1.x 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Tue, 15 Nov 2005 07:07:53 GMT

<script type='text/javascript'>
var s0=9.869603;
window.parent.DWREngine._handleResponse('520_1132038593390', s0);
</script>
 

Заключение

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

Отметим важные вопросы, которые не были рассмотрены в данном докладе: обратная совместимость AJAX-приложений с устаревшими браузерами, техники сохраняющие usability традиционных веб-приложений (кнопка «Назад», соответствие URL и состояния веб-приложения и т. д.).

Bibliography

1
Ajax (programming) / From Wikipedia, the free encyclopedia. (http://en.wikipedia.org/wiki/AJAX, 12.10.2005)

2
Ajax Frameworks / Ajax Patterns. (http://www.ajaxpatterns.org/Ajax_Frameworks, 9.11.2005)

3
Components, Widgets & Libraries / AjaxInfo.com. (http://www.ajaxinfo.com/default~area~components.htm, 10.11.2005)

4
Ajax for Java developers: Ajax with Direct Web Remoting / IBM developerWorks. (http://www-128.ibm.com/developerworks/java/library/j-ajax3/?ca=dgr-lnxw03AJAX-DWR, 15.11.2005)

5
Developing AJAX Applications the Easy Way / java.net. (http://today.java.net/pub/a/today/2005/08/25/dwr.html, 15.11.2005)

Footnotes

... DHTML[*]
Dynamic HTML
... LAMP[*]
Linux, Apache, MySQL, Perl
... ежедневно[*]
http://en.wikipedia.org/wiki/List_of_websites_using_Ajax
... Ajax.[*]
https://bpcatalog.dev.java.net/nonav/ajax/index.html, http://smirnov.org.ru/en/myfaces-ajax.html
... DWR[*]
http://getahead.ltd.uk/dwr/