→ Пошук по сайту       Увійти / Зареєструватися
Знання Технологія AJAX Підбірка матеріалів по використанню AJAX

УСОВЕРШЕНСТВОВАННЫЕ ЗАПРОСЫ И ОТВЕТЫ В AJAX

Компетенція Використання AJAX

В совершенстве изучите коды состояния HTTP, состояния готовности и объект XMLHttpRequest

Подробно о состояниях готовности HTTP

Вы должны помнить из предыдущей статьи, что объект XMLHttpRequest имеет свойство readyState. Это свойство удостоверяет, что сервер завершил запрос, и обычно функция обратного вызова использует данные от сервера для обновления Web-формы или страницы. В листинге 1 приведен простой пример этого.



Листинг 1. Работа с ответом сервера в функции обратного вызова

                                                                                 
 function updatePage() {
   if (request.readyState == 4) {
     if (request.status == 200) {
       var response = request.responseText.split("|");
       document.getElementById("order").value = response[0];
       document.getElementById("address").innerHTML =
         response[1].replace(/\n/g, "
");
     } else
       alert("status is " + request.status);
   }
 }

Это определенно наиболее общее (и наиболее простое) использование состояний готовности. Как вы, возможно, догадались по числу "4", существует и несколько других состояний готовности:

  • 0: Запрос не инициализирован (перед вызовом open()).
  • 1: Запрос инициализирован, но не был передан (перед вызовом send()).
  • 2: Запрос был передан и обрабатывается (на данном этапе вы можете обычно получить заголовки содержимого из ответа).
  • 3: Запрос обрабатывается; часто в ответе доступны некоторые частичные данные, но сервер не закончил свой ответ.
  • 4: Ответ завершен; вы можете получить ответ сервера и использовать его.

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

Скрытые значения состояний готовности

Первое состояние готовности, обозначаемое значением свойства readyState равным 0 (readyState == 0), представляет неинициализированный запрос. Как только вы вызовете метод open() вашего объекта запроса, это свойство устанавливается в 1. Поскольку вы почти всегда вызываете open() сразу после инициализации вашего запроса, редко можно увидеть readyState == 0. Более того, неинициализированное состояние готовности достаточно бесполезно в реальных приложениях.

Тем не менее, в интересах полноты изложения взгляните на листинг 2, в котором показано, как получить состояние готовности, когда оно установлено в 0.



Листинг 2. Получение состояния готовности 0

                                                                                 
   function getSalesData() {
     // Создать объект request
     createRequest();                          
     alert("Ready state is: " + request.readyState);
 
     // Настроить  (инициализировать) запрос
     var url = "/boards/servlet/UpdateBoardSales";
     request.open("GET", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }

В этом простом примере getSalesData() – это функция, которую вызывает ваша Web-страница для запуска запроса (как при нажатии кнопки). Обратите внимание, что вы должны проверить состояние готовности перед вызовом open(). На рисунке 1 sпоказан результат выполнения этого приложения.



Рисунок 1. Состояние готовности 0
Рисунок 1. Состояние готовности 0

Очевидно, это не очень хорошо; существует очень мало случаев, когда вы должны проверять, что open() не была вызвана. Единственным использованием этого состояния готовности в "почти реальном" Ajax-программировании будет ситуация, когда вы выполняете несколько запросов, используя один и то же объект XMLHttpRequest из нескольких функций. В этой ситуации (довольно необычной) вы, возможно, захотите убедиться, что объект запроса находится в неинициализированном состоянии (readyState == 0) перед выполнением новых запросов. Это, в сущности, гарантирует, что другая функция не использует объект в это же время.

Обзор состояния готовности выполняющегося запроса

От состояния готовности 0 ваш объект запроса должен проходить через каждое другое состояние в обычном запросе и ответе, и, в конце концов, закончить на состоянии 4. Вот почему вы видите строку кода if (request.readyState == 4) в большинстве функций обратного вызова; она гарантирует, что сервер закончил свою работу с запросом и можно без опасений обновить Web-страницу или выполнить действие, основанное на полученных от сервера данных.

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



Листинг 3. Проверка состояния готовности

                                                                                 
   function updatePage() {
     // Отобразить текущее состояние готовности
     alert("updatePage() called with ready state of " + request.readyState);
   }

Вы должны создать функцию для вызова из вашей Web-страницы и передать в ней запрос серверному компоненту (просто такая же функция, как и приведенная в листинге 2, а также в примерах первой и второй статей данного цикла статей). Убедитесь в том, что при настройке вашего запроса вы установили функцию обратного вызова в updatePage(); для этого установите свойство onreadystatechange вашего объекта запроса в updatePage().

Этот код является отличной иллюстрацией того, что на самом деле означает onreadystatechange – каждый раз при изменении состояния готовности запроса вызывается updatePage(), и вы видите предупреждение. На рисунке 2 показан пример вызова этой функции, в данном случае с состоянием готовности 1.



Рисунок 2. Состояние готовности 1
Рисунок 2. Состояние готовности 1

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

Несовместимость браузеров

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

  • 1
  • 2
  • 3
  • 4

Это не должно быть сюрпризом, поскольку здесь представлено каждое состояние запроса. Однако если вы обратитесь к этому же приложению в Safari, вы должны увидеть (или скорее, не увидеть) кое-что интересное. Вот состояния в Safari 2.0.1:

  • 2
  • 3
  • 4

Safari на самом деле пропускает первое состояние, и нет здравого объяснения, почему; просто Safari работает таким образом. Это также выявляет важный момент: в то время как использование значения состояния запроса равное 4 перед использованием данных от сервера является неплохой идеей, написание кода, зависящего от каждого промежуточного состояния готовности, несомненно, путь к получению различных результатов в различных браузерах.

Например, при использовании Opera 8.5, отображение состояний готовности становится даже еще хуже:

  • 3
  • 4

И последнее, но важное - Internet Explorer отображает следующие состояния:

  • 1
  • 2
  • 3
  • 4

Если с запросом имеются проблемы, это самое первое место, где следует их искать. Добавьте вывод предупреждения, чтобы увидеть состояние готовности запроса и убедиться в том, что все работает нормально. Еще лучше - протестируйте и в Internet Explorer, и в Firefox – вы получите все четыре состояния готовности и сможете проверить каждый этап запроса.

Теперь посмотрим со стороны ответа.

Данные ответа под микроскопом

Разобравшись с различными состояниями готовности, происходящими во время запроса, вы готовы исследовать другую важную часть объекта XMLHttpRequest – свойство responseText. Вспомните из последней статьи, что это свойство используется для получения данных от сервера. Как только сервер завершит обработку запроса, он размещает все данные, необходимые для ответа на запрос, в свойство responseText запроса. После этого ваша функция обратного вызова может использовать эти данные, как показано в листингах 1 и 4.



Листинг 4. Использование ответа от сервера

                                                                                 
   function updatePage() {
     if (request.readyState == 4) {
       var newTotal = request.responseText;
       var totalSoldEl = document.getElementById("total-sold");
       var netProfitEl = document.getElementById("net-profit");
       replaceText(totalSoldEl, newTotal);
 
       /* Определить новую чистую прибыль */
       var boardCostEl = document.getElementById("board-cost");
       var boardCost = getText(boardCostEl);
       var manCostEl = document.getElementById("man-cost");
       var manCost = getText(manCostEl);
       var profitPerBoard = boardCost - manCost;
       var netProfit = profitPerBoard * newTotal;
 
       /* Обновить чистую прибыль на форме продаж */
       netProfit = Math.round(netProfit * 100) / 100;
       replaceText(netProfitEl, netProfit);
     }

Листинг 1 довольно прост. Листинг 4 немного более сложен, но в начале оба проверяют состояние готовности и затем извлекают значение (или значения) из свойства responseText.

Просмотр текстового ответа во время запроса

Аналогично состоянию готовности значение свойства responseText изменяется на всем протяжении жизненного цикла запроса. Чтобы увидеть это в действии, используйте код, аналогичный приведенному в листинге 5, для тестирования текстового ответа, также как и состояния готовности.



Листинг 5. Тестирование свойства responseText

                                                                                 
   function updatePage() {
     // Отобразить текущее состояние готовности
     alert("updatePage() called with ready state of " + request.readyState +
           " and a response text of '" + request.responseText + "'");
     }

Теперь откройте ваше Web-приложение в браузере и активируйте запрос. Для получения наибольшей информации из этого кода используйте либо Firefox, либо Internet Explorer, поскольку оба обрабатывают все возможные состояния готовности во время запроса. Например, когда состояние готовности равно 2, свойство responseText не определено (см. рисунок 3); вы должны увидеть ошибку, если открылась консоль JavaScript.



Рисунок 3. Текстовый ответ при состоянии готовности 2
Рисунок 3. Текстовый ответ при состоянии готовности 2

А при состоянии готовности 3 сервер поместил значение в свойство responseText, по крайней мере, в этом примере (см. рисунок 4).



Рисунок 4. Текстовый ответ при состоянии готовности 3
Рисунок 4. Текстовый ответ при состоянии готовности 3

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

Получение надежных данных

Во всей документации и спецификациях утверждается, что только при состоянии готовности 4 данные можно использовать безопасно. Поверьте мне, вы редко найдете случаи, когда данные не могут быть получены из свойства responseText при состоянии готовности равном 3. Однако полагаться на это в вашем приложении – плохая идея. Если вы напишете код, зависящий от завершенности данных при состоянии готовности 3, почти гарантируется, что наступит момент, когда данные будт не полны.

Лучшей идеей является предоставление некоторого рода обратной связи с пользователем – когда состояние готовности равно 3, отобразить, что ответ на подходе. В то время, как использование такой функции как alert(), очевидно, плохая идея, использование Ajax и блокирование пользователя диалоговым окном понять довольно трудно – вы могли бы обновить поле на вашей форме или странице при изменении состояния готовности. Например, попробуйте установить длину индикатора хода процесса в 25% при равенстве состояния готовности 1, 50% при 2, 75% при 3 и 100% (завершено) при 4.

Естественно, такой подход понятнее, но зависит от используемого браузера. В Opera вы никогда не получите первых двух состояний готовности, а Safari пропустит 1. По этой причине я оставляю этот пример как упражнение и не включаю в эту статью.

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

Пристальный взгляд на коды состояния HTTP

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

  • 401: Unauthorized (Не авторизован)
  • 403: Forbidden (Запрещен)
  • 404: Not Found (Не найден)

Можно найти еще. Для добавления еще одного уровня управляемости и оперативности (и особенно более надежной обработки ошибок) к вашим Ajax-приложениям необходимо проверять коды состояния в запросе и ответе соответственно.

200: Все OK

Во многих Ajax-приложениях вы увидите функцию обратного вызова, проверяющую состояние готовности и продолжающую работу с данными ответа сервера, как в листинге 6.



Листинг 6. Функция обратного вызова, игнорирующая код состояния

                                                                                 
   function updatePage() {
     if (request.readyState == 4) {
       var response = request.responseText.split("|");
       document.getElementById("order").value = response[0];
       document.getElementById("address").innerHTML =
         response[1].replace(/\n/g, "
");
     }
   }

Это недальновидный и подверженный ошибкам подход к Ajax-программированию. Если сценарий требует аутентификации, а ваш запрос не предоставляет корректных данных, сервер возвратит код ошибки 403 или 401. Однако состояние готовности будет установлено в 4, поскольку сервер ответил на запрос (даже если ответ на ваш запрос не такой, какой вы хотели или ожидали). В результате пользователь не получит правильных данных и может даже получить неприятную ошибку, когда ваш JavaScript-код попытается использовать несуществующие серверные данные.

Гарантировать то, что сервер не только завершил обработку запроса, но и возвратил код состояния "Все в порядке", требует минимальных усилий. Этот код равен "200" и возвращается в свойстве status объекта XMLHttpRequest. Итак, добавьте дополнительную строку в вашу функцию обратного вызова, как показано в листинге 7.



Листинг 7. Проверка кода состояния

                                                                                 
   function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         var response = request.responseText.split("|");
         document.getElementById("order").value = response[0];
         document.getElementById("address").innerHTML =
           response[1].replace(/\n/g, "
");
       } else
         alert("status is " + request.status);
     }
   }

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

Переадресация и перенаправление

Перед детальным рассмотрением ошибок имеет смысл поговорить о том, о чем вы, возможно, не должны беспокоиться при использовании Ajax – переадресации. В кодах состояния HTTP есть семейство кодов состояния 300, включающих:

  • 301: Moved permanently (Перемещен постоянно)
  • 302: Found (Найден) - запрос переадресован на другой URL/URI
  • 305: Use Proxy (Использовать прокси) - запрос должен использовать прокси для доступа к затребованному ресурсу

Ajax-программисты, возможно, не беспокоятся о переадресации по двум причинам:

  • Прежде всего, Ajax-приложения почти всегда пишутся для конкретного серверного сценария, сервлета или приложения. Исчезновение или перемещение в другое место этого компонента без вашего, Ajax-программиста, ведома встречается довольно редко. Поэтому почти всегда вы знаете, что ресурс переместился (потому что вы его переместили, или он уже был перемещен), меняете URL в вашем запросе и никогда не сталкиваетесь с такого рода проблемами.
  • И еще одна, даже более важная, причина: Ajax-приложения и запросы выполняются в "песочнице" безопасности. Это значит, что доменом, обслуживающим Web-страницу, с которой выполняются Ajax-запросы, является домен, который должен на них отвечать. Поэтому Web-страница, обслуживаемая ebay.com, не может выполнить Ajax-запрос сценарию, выполняющемуся на amazon.com; Ajax-приложения ibm.com не могут выполнять запросы сервлетам, работающим на netbeans.org.

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

Ошибки

Как только вы позаботились о коде состояния 200 и поняли, что можете в значительной степени проигнорировать 300-е семейство кодов состояния, единственной группой кодов, о которой надо подумать – это 400-е семейство, указывающее на ошибки различного типа. Повторно взгляните на листинг 7 и обратите внимание, что хотя ошибки и обрабатываются, пользователю выдается очень общее сообщение об ошибке. И хотя это шаг в правильном направлении, это сообщение все еще остается бесполезным с точки зрения информирования пользователя или программиста, работающего с приложением, о том, что произошло.

Прежде всего, добавьте поддержку отсутствующих страниц. Они действительно не должны часто появляться в реальных системах, но это нередко случается при тестировании перемещенного сценария или при вводе программистом неверного URL. Если вы можете изящно обрабатывать ошибки 404, то намного лучше поможете поставленным в тупик пользователям и программистам. Например, если сценарий был удален с сервера, а вы используете приведенный в листинге 7 код, вы должны увидеть ничего не объясняющее сообщение об ошибке, показанное на рисунке 5.



Рисунок 5. Общая обработка ошибок
Рисунок 5. Общая обработка ошибок

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



Листинг 8. Проверка кода состояния

                                                             
   function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         var response = request.responseText.split("|");
         document.getElementById("order").value = response[0];
         document.getElementById("address").innerHTML =
           response[1].replace(/\n/g, "
");
       } else if (request.status == 404) {
         alert ("Requested URL is not found.");
       } else if (request.status == 403) {
         alert("Access denied.");
       } else
         alert("status is " + request.status);
     }
   }

Это все довольно просто, но действительно предоставляется дополнительная информация. На рисунке 6 показана эта же ошибка, что и на рисунке 5, но в данном случае код обработки ошибок выдает пользователю или программисту лучшую картину того, что случилось.



Рисунок 6. Индивидуальная обработка ошибок
Рисунок 6. Индивидуальная обработка ошибок

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

Дополнительные типы запроса

Если вы действительно хотите управлять объектом XMLHttpRequest, рассмотрим еще одну, последнюю тему. Добавьте HEAD-запросы в ваш репертуар. В первых двух статьях я рассказал, как выполнять GET-запросы; в следующей статье вы научитесь всему, что связано с передачей данных на сервер при помощи POST-запроса. В духе усовершенствованной обработки ошибок и сбора информации вы должны научиться выполнять HEAD-запросы.

Выполнение запроса

В действительности выполнение HEAD-запроса является довольно тривиальной задачей; вы просто вызываете метод open() с "HEAD" вместо "GET" или "POST" в качестве первого параметра, как показано в листинге 9.



Листинг 9. Выполнение HEAD-запроса с Ajax

                                                                                 
   function getSalesData() {
     createRequest();
     var url = "/boards/servlet/UpdateBoardSales";
     request.open("HEAD", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }

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

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



Листинг 10. Вывод всех заголовков ответа их HEAD-запроса

                                                                                 
   function updatePage() {
     if (request.readyState == 4) {
       alert(request.getAllResponseHeaders());
     }
   }

На рисунке 7 показаны заголовки ответа из простого Ajax-приложения, выполняющего HEAD-запрос на сервер.



Рисунок 7. Заголовки ответа из HEAD-запроса
Рисунок 7. Заголовки ответа из HEAD-запроса

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

Проверка URL

Вы увидели, как проверить ошибку 404 при отсутствии URL. Если это превращается в общую проблему (возможно, определенный сценарий или сервлет в данный момент времени находитcя в режиме offline) – вы, вероятно, захотите проверить URL перед выполнением полного GET или POST-запроса. Для этого выполните HEAD-запрос и проверьте 404-ю ошибку в вашей функции обратного вызова; в листинге 11 приведен пример такой функции.



Листинг 11. Проверка существования URL

                                                                                 
   function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         alert("URL exists");
       } else if (request.status == 404) {
         alert("URL does not exist.");
       } else {
         alert("Status is: " + request.status);
       }
     }
   }

По правде говоря, это имеет небольшое значение. Сервер должен ответить на запрос и обработать его для заполнения значения длины содержимого заголовка ответа, поэтому вы не экономите время обработки. Кроме того, выполнение запроса и определение существования URL при помощи HEAD-запроса занимает столько же времени, сколько и выполнение GET или POST-запроса и последующая обработка ошибок (как показано в листинге 7. Хотя иногда может быть полезно знать точно, что доступно; вы никогда не знаете, когда вас пробьет на творчество и вам понадобится HEAD-запрос!

Полезные HEAD-запросы

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

В таких случаях вы просто используете соответствующее имя заголовка и передаете его в метод getResponseHeader() объекта XMLHttpRequest. То есть, для получения длины ответа вызовите request.getResponseHeader("Content-Length");. Для получения типа содержимого используйте request.getResponseHeader("Content-Type");.

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

загрузка...
Сторінки, близькі за змістом