Объект XMLHttpRequest

192

Мы начали изучать HTML5 с возможностей маркировки, таких как семантические элементы, веб-формы и видео. Но по мере овладения материалом мы постепенно перешли на программирование веб-страниц HTML5 с использованием JavaScript. А теперь мы затронем новые возможности HTML5, которые выводят веб-программирование на новый уровень.

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

Добавление в наш инструментарий серверного программирования представляет небольшую проблему. С одной стороны, выбор языка серверного программирования не имеет значения при условии, что он может работать с чисто HTML5-страницами (а все эти языки могут). Но с другой стороны, бессмысленно влезать по уши в изучение новой технологии, которую вы не планируете использовать или которая не поддерживается вашим веб-хостом. А хороших языков серверного программирования предостаточно, включая PHP, ASP.NET, Ruby, Java, Python и многие другие.

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

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

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

Объект XMLHttpRequest является идеальным инструментом для получения данных с веб-сервера. Далее приведено несколько примеров данных, которые можно получить с веб-сервера посредством этого объекта:

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

Отправка запроса веб-серверу

На рисунке показана веб-страница, которая просит веб-сервер выполнить простое математическое вычисление. Этот запрос отправляется веб-серверу посредством объекта XMLHttpRequest:

Отправка запроса веб-серверу

По нажатию кнопки "Спросить у сервера" веб-страница создает объект XMLHttpRequest и отправляет два числа на веб-сервер. Веб-сервер отрабатывает простой сценарий, выполняющий математическую операцию над числами, и отправляет ответ обратно на веб-страницу.

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

Создание сценария

Сценарий PHP создается в простом текстовом файле (WebCalculator.php) и имеет следующую структуру:

<?php
   // Код вставляется сюда
?>

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

<?php
    $num1 = $_GET['number1'];
    $num2 = $_GET['number2'];
    $sum = $num1 + $num2;
    echo($sum);
?>

Даже если вы и не эксперт по PHP, у вас, скорее всего, не будет трудностей разобраться, что этот код делает. Первым делом мы получаем два числа, отправленные веб-страницей. Знак доллара ($) обозначает переменную, поэтому код создает две переменные: $num1 и $num2. Значения для переменных код извлекает из встроенной в PHP коллекции, называющейся $_GET. Эта коллекция содержит всю информацию из URL, посредством которой был запрошен данный сценарий.

Например, если поместить сценарий PHP в файл WebCalculator.php, строка с запросом этого сценария будет выглядеть так:

http://mysite.com/WebCalculator.php?number1=34&number2=58

В данном случае URL содержит две единицы информации в конце (в части URL, называющейся строкой запроса — query string). Значение первой переменной в этой части, number1, равно 34, а второй, number2, равно 58. Начало строки запроса обозначается знаком вопроса (?), а каждая последующая переменная — знаком амперсанда (&).

Когда код PHP начинает исполняться, он извлекает эту информацию из URL и сохраняет ее в коллекции $_GET, где она доступна для последующих операций. (Большинство платформ серверного программирования поддерживает модель, подобную этой. Например, в технологии ASP.NET эта информация сохраняется в коллекции Request.QueryString.)

Ветераны HTML знают, что данные на веб-сервер можно отправлять двумя способами — посредством строки запроса или вставкой их в тело запроса. В любом случае данные кодируются одинаково, и доступ к ним на сервере предоставляется похожим образом. Например, в PHP данные, переданные в теле запроса, помещаются в коллекцию $_POST.

Получив эти два числа, сценарий PHP просто суммирует их. Последний шаг — отправить результаты назад веб-странице, которая подала запрос. Эти результаты можно было бы обернуть в разметку HTML или даже в приспособленную для данных разметку XML, но это будет слишком. Для данного примера, и простого текста будет вполне достаточно. Но независимо от выбранного формата данных, все, что требуется для их отправки — это простая PHP-команда echo.

Итак, сценарий состоит из всего четырех строк PHP-кода. Но этого достаточно, чтобы установить основной шаблон: веб-страница задает веб-серверу вопрос, а веб-сервер предоставляет ответ на этот вопрос.

Простота вычислений в данном примере может породить вопрос: нельзя ли выполнить их полностью в браузере посредством JavaScript, а не прибегать к помощи сервера? Конечно же, можно. Но в данном случае важно не собственно вычисление. Рассмотренный сценарий PHP служит примером для выполнения любой задачи, которую нужно выполнить на сервере. Вычисления могут быть любой сложности, но базовый образец обмена данными будет тем же.

Обращение к веб-серверу

Вторым шагом будет создание страницы, которая с помощью объекта XMLHttpRequest использует сценарий PHP. Структура страницы показана ниже:

<div>
   <p>Вставьте два числа:
      <input id="number1" type="number">
      <input id="number2" type="number">
   </p>
   <p>Чтобы спросить у сервера сумму этих чисел без перезагрузки страницы, нажмите эту кнопку: </p>
      <button onclick="askServer()">Спросить у сервера</button>
   </p>
</div>
   <p id="result"></p>
body {
    font-family: Verdana;
    font-size: 12px;
}

div {
    margin: 20px;
    border: black 1px solid;
    padding: 10px;
    background-color: #FBF3CB;
}

input {
    width: 75px;
}

#result {
    color: darkred;
    font-weight: bold;
}
button {
    padding:6px;
}

Код начинается достаточно просто. Сначала создается объект XMLHttpRequest для использования во всех функциях:

var req = new XMLHttpRequest();

При нажатии кнопки "Спросить у сервера" вызывается функция askServer():

function askServer() {
  // Получаем введенные в форму числа
  var number1 = document.getElementById("number1").value;
  var number2 = document.getElementById("number2").value;

  // Структурируем строку запроса
  var dataToSend = "?number1=" + number1 + "&number2=" + number2;
  
  ...
}

Теперь все готово для создания запроса, который начинается методом open() объекта XMLHttpRequest. В качестве параметров методу передается тип операции HTTP (get или post), URL запроса и значение true или false, определяющее режим выполнения запроса браузером — асинхронно или нет:

// ...
req.open("GET", "WebCalculator.php" + dataToSend, true);
// ...

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

Но прежде чем отправлять запрос, к событию onReadyStateChange объекта XMLHttpRequest необходимо подключить функцию. Это событие активируется при получении информации от сервера, включая конечный результат вычислений.

И, наконец, можно начинать процесс отправки запроса, вызвав метод send() объекта XMLHttpRequest. Но помните, что код продолжает исполняться без каких-либо задержек. Единственный способ получить ответ — это обратиться к событию onReadyStateChange, которое можно будет активировать позже:

// ...
  req.onreadystatechange = handleServerResponse;
  req.send();

  document.getElementById("result").innerHTML = "Запрос отправлен на сервер.";
}

Получив ответ, нужно немедленно проверить два свойства объекта XMLHttpRequest. Сначала проверяется свойство readyState, значение которого изменяется от 0 до 4 в процессе выполнения запроса. При инициализации запроса это значение равно 1, при отправке — 2, при частичном получении ответа — 3, при завершении выполнения — 4.

Если значение свойства readyState не равно 4, нет смысла продолжать дальнейшую обработку и нужно проверить значение свойства status, которое предоставляет код состояния HTTP. Для наших целей значение этого свойства должно быть 200, что означает отсутствие каких-либо проблем. Значение этого свойства будет равно 401 при запросе неразрешенной страницы; 404 - если запрошенная страница не была найдена; 302 - если страница была перемещена; или 503 - если страница занята. (Полный список кодов ошибок HTTP.)

Код для проверки значения этих свойств выглядит так:

function handleServerResponse() {
  if ((req.readyState == 4) && (req.status == 200))
  {
     // ...

Если свойства содержат требуемые значения, можно извлечь результаты из свойства responseText объекта XMLHttpRequest. В нашем случае это свойство содержит сумму двух исходных чисел. Полученный результат отображается на странице следующим кодом:

function handleServerResponse() {
  if ((req.readyState == 4) && (req.status == 200))
  {
    var result = req.responseText;
    document.getElementById("result").innerHTML = "Ответ сервера: " + result;
  }
}

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

В действительности, в настоящее время объект XMLHttpRequest используется намного чаще для работы с данными, отличными от XML, так что пусть его название не сбивает вас с толку.

Получение нового содержимого

Другой ситуацией, в которой можно использовать объект XMLHttpRequest, будет загрузка нового содержимого в страницу. Например, новостная статья может сопровождаться несколькими фотографиями, но одновременно отображается только одна из них. Чтобы отобразить другую фотографию, пользователь нажимает соответствующую кнопку, а JavaScript-код получает ее и вставляет вместо предыдущей. Этот же способ можно использовать для показа изображений в слайд-шоу.

Пример такого слайд-шоу показан на рисунке ниже:

Использование XMLHttpRequest для загрузки изображений

Содержимое этой страницы состоит из нескольких отдельных слайдов. Управление слайдами осуществляется щелчком по ссылкам "Пред" и "След", в результате чего загружается новое изображение со своей подписью. Запрос нового содержимого осуществляется посредством объекта XMLHttpRequest.

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

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

Для создания примера, показанного на рисунке, сначала нам нужно выделить место для динамического содержимого. Для этого мы используем элемент <div>, который создает нам рамку с золотистым обрамлением и двумя ссылками внизу:

 <h1>Экзотические места в Китае</h1>
 <article>
    <div id="slide">Нажмите на ссылку, чтобы показать картинку.</div>
    <a onclick="return previousSlide()" href="#">< Пред</a> 
    <a onclick="return nextSlide()" href="#">След ></a>
 </article>
body {
  font-family: Lucida Sans Unicode, serif;
  font-size: small;
  padding: 10px 50px;
}

h1 {
  font-family: Palatino, serif;
  font-size: 35px;
  margin: 0px;
  border-bottom: double 5px rgb(218,165,32);
}

#slide {
  background: rgba(218,165,32,0.8);
  border: solid 1px black;
  width: 530px;
  min-height: 300px;
  padding: 10px;
}

#slide h2 {
  font-family: Lucida Sans Unicode, serif;
}

#slide img {
  margin-top: 10px;
  border: solid 1px white;
}

a {
  color: green;
}

Ссылки вызывают функцию previousSlide() или nextSlide() в зависимости от направления просмотра слайдов. В обеих функциях используется счетчик, начальное значение которого равно 0, увеличивается до 5 и начинает новый цикл со значения 1. Код функций nextSlide() и previousSlide() выглядит следующим образом:

function nextSlide() {
   if (slideNumber == 5) {
      slideNumber = 1;
   } else {
      slideNumber += 1;
   }

   goToNewSlide();
   return false;
}

function previousSlide() {
   if (slideNumber == 1) {
      slideNumber = 5;
   } else {
      slideNumber -= 1;
   }

   goToNewSlide();
   return false;
}

Обе функции используют еще одну функцию, goToNewSlide(), которая в действительности и выводит новое изображение:

function goToNewSlide() {
   // Отправляем номер слайда в файл exotic_china.php
   req.open("GET", "exotic_china.php?slide=" + slideNumber, true);
   
   // Подключаем функцию для обработки данных слайдов
   req.onreadystatechange = newSlideReceived;
   
   // Отправляем запрос
   req.send();
}

Последний шаг в клиентской части — это скопировать полученные данные и элемент <div>, который отображает текущий слайд:

function newSlideReceived() {
  if ((req.readyState == 4) && (req.status == 200))
  {
      document.getElementById("slide").innerHTML = req.responseText;
  }
}

Серверный код получает номер слайда и возвращает необходимую разметку:

<?php
    // exotic_china.php
    $slide = $_GET["slide"];
	
	switch($slide)
	{
		case 1:
		   echo '<figure>
<h2>Рисовая терраса</h2>
<img src="http://professorweb.ru/downloads/XMLHttpRequest_Sample/rice_terrace.jpg">
</figure>';
		   break;
		case 2:
		   echo '<figure>
<h2>Жемчужная башня</h2>
<img src="http://professorweb.ru/downloads/XMLHttpRequest_Sample/oriental_pearl_tower.jpg">
</figure>';
		   break;
		case 3:
		   echo '<figure>
<h2>Насыщенная улица</h2>
<img src="http://professorweb.ru/downloads/XMLHttpRequest_Sample/percival_street.jpg">
</figure>';
		   break;
		case 4:
		   echo '<figure>
<h2>Обычная деревня</h2>
<img src="http://professorweb.ru/downloads/XMLHttpRequest_Sample/traditional_village.jpg">
</figure>';
		   break;
		case 5:
		   echo '<figure>
<h2>Дерево желаний</h2>
<img src="http://professorweb.ru/downloads/XMLHttpRequest_Sample/wishing_tree.jpg">
</figure>';
		   break;
		default:
		   echo 'Слайд не найден';
	}
?>

Чтобы придать этому примеру больше шика, изображения можно менять, используя эффект перехода. Например, новое изображение может постепенно проявляться, в то время как старое постепенно исчезает. Это одно из достоинств динамических страниц, использующих объект XMLHttpRequest — они могут полностью управлять способом представления содержимого.

Лучший чат для C# программистов