В первой статье серии "<% ASP на блюдечке %>" ("ASP на блюдечке. Часть 1. Построение интерфейса к базе данных") мы познакомились с ASP, а также с принципами построения простейшего интерфейса к базе данных с его помощью (газетный сайт со встроенными возможностями его пополнения новыми статьями, снабжаемыми фотографиями, непосредственно с самого сайта и без программирования). Вторая статья "ASP на блюдечке. Часть 2. ActiveX компоненты и ASP" была посвящена основам использования ActiveX-компонентов в ASP, а также азам разработки собственных компонентов с помощью известных всем Microsoft VB и Microsoft VC++.
Итак, серия продолжается, и теперь предлагаю читателям ознакомиться с возможностями, предоставляемыми ASP в поддержку программирования WAP-сайтов, и создать собственную WAP-систему бронирования авиабилетов.
Что дает WAP
WAP добавляет к Интернету новое измерение - мобильность. Вы можете покупать или бронировать билеты, заказывать различные услуги или производить операции с вашим банковским счетом и т.д. На первый взгляд все это может показаться недоступным для обычного пользователя, однако, как показывают многие социологические опросы, WAP уже захватил многие области сервиса и продолжает захватывать новые. В автобусе, на пляже, в кино... - везде. Информация там, где она необходима. И не только информация, но и взаимодействие с пользователем. Более того, согласно прогнозам некоторых статистических компаний, в недалеком будущем мобильный телефон станет постоянной и неотъемлемой частью практически любого человека. В развитых странах мира, да и у нас в России, он уже давно перестал являться атрибутом роскоши. Так, к примеру, количество зарегистрированных мобильных телефонов на апрель 2000 года в Японии приблизилось к количеству абонентов городских телефонных сетей, а в ближайшие два года, согласно прогнозам, превысит его более чем в 1,5 раза. Одновременно со спросом на мобильные телефоны растет и число компаний-провайдеров (поставщиков услуг сотовой связи), увеличивается конкуренция между ними и, как следствие, в ближайшее время ожидается снижение платы за оказываемые ими услуги. Таковы законы рынка. И не стоит забывать самого главного: волна популярности мобильных телефонов порождает волну популярности WAP-сайтов и WAP-приложений. Ведь купив мобильный телефон, клиент рано или поздно обязательно захочет узнать о погоде, о спорте, заказать билеты в театр или ужин в ресторане и т.д.
Как читатель, очевидно, согласится, исходя из чисто эргономических соображений мобильный телефон не может соперничать с настольным или портативным ПК. Экранчик мобильного телефона попросту не может вместить сколь-нибудь красочный сайт, он для этого и не предназначен. Пока он может отобразить только небольшие тексты и очень немногие элементы графики специального формата. Кроме того, мобильный телефон не может соперничать с персональным компьютером и по объемам передаваемых данных (поток передачи данных для мобильных телефонов жестко ограничен и ни в какое сравнение не идет с объемами данных, передаваемых по компьютерным сетям). Поэтому разработчикам WAP-сайтов приходится отказываться от красочности и ограничиваться функциональностью. Однако есть по крайней мере два положительных фактора, обусловливающих популярность WAP-сайтов: мобильность и механизм оплаты.
Мобильность означает повсеместный доступ к WAP-приложению (что, собственно, и обеспечивает мобильный телефон).
Относительно механизма оплаты следует сказать следующее. Поскольку оплата за телефонный звонок и другие услуги, предоставляемые телефонными компаниями, производится непосредственно с использованием баланса абонента (SIM-карты, или обычной пластиковой карты), то в самом ближайшем будущем взимать определенную плату и за пользование WAP-услугами, используя при этом тот же самый баланс абонента, не составит труда. Таким образом, телефонный счет будет включать в себя и счет за оказание WAP-услуг. Так что заводить новый счет или новую карту для оказания WAP-услуг пользователю не придется.
Основы WAP
WAP открывает новые возможности для мобильных телефонов, делая их не просто телефонами, а устройствами, связывающими своих абонентов с Интернетом в самом широком смысле этого слова.
Итак, WAP - открытая спецификация, предназначенная для стандартизации способов и форматов (протоколов), с помощью которых мобильные телефоны получают доступ к различным услугам в Сети. Надо сказать, что спецификация WAP в настоящее время все еще находится в стадии разработки, поэтому мы будем рассматривать ее текущую версию - 1.1. Автором — разработчиком спецификации WAP является так называемый WAP-форум WAP Forum, в настоящее время ведущий разработку версии 1.2, выхода спецификации которой мы ожидаем в самое ближайшее время. Желающим скачать всевозможные описания стандартов WAP- и других протоколов для работы с мобильными телефонами советую посетить специальную страничку WAP-форума.
WAP-протокол является не просто протоколом передачи, это некая надстройка над известными всем протоколами передачи данных, а точнее их частный случай. Данное обстоятельство в значительной мере облегчает разработку WAP-сайтов и WAP-приложений. WAP-протокол использует очень схожую с используемой для передачи данных по компьютерным сетям и, в частности, по Интернету (известная всем семиуровневая модель ISO). Как известно, более низкие уровни (физический, канальный и сеансовый) отвечают за физическую передачу данных и не представляют особого интереса для разработчиков, а вот более высокие уровни модели, используемой протоколом WAP (транспортный, сетевой и прикладной), очень походят на своих "собратьев", используемых для протоколов передачи данных по WWW. В частности, на известный всем старый добрый протокол HTTP. Так что создатели WAP-протокола позаботились и о разработчиках WAP-приложений: разработчикам WAP-сайтов не придется переучиваться. Используя привычные CGI-средства, можно разрабатывать и WAP-сайты.
Конечно, у мобильных телефонов более строгие ограничения на форматы и на объемы передаваемых данных, а следовательно, на возможности клиентов. Поэтому в WAP используется язык описания, несколько отличающийся от HTML и представляющий собой его частный случай. Называется он WML (Wireless Mark-up Language). По сути, это XML-основанный язык компоновки форматированного текста, предоставляющий навигационные возможности различным WML-документам по переходам между собой и между частями одного и того же WML-документа. Аналогом же языка описания сценариев JavaScript для WAP-сайтов и WAP-приложений является WMLScript, который не только позволяет в какой-то степени программировать "логику мобильников", но и получать доступ к некоторым функциям мобильных телефонов.
Прежде всего определим понятие клиента в случае взаимодействия сервер-телефон. С точки зрения разработчика клиентом является не сам телефон, а программный компонент, называемый шлюзом WAP, который преобразовывает запросы мобильного телефона в запросы HTTP и перенаправляет HTTP-ответы Web-сервера мобильному телефону (опять-таки через WAP-шлюз). Шлюз WAP сжимает WML-файлы в двоичный формат. Несмотря на то что некоторые современные шлюзы WAP способны преобразовывать HTML в WML, не стоит полагаться на то, что HTML-сайт будет столь же приглядно выглядеть и на мобильной трубке, и все-таки разработать его WML-версию.
Однако нам шлюз WAP не понадобится, поскольку для отладки WAP-приложений и WAP-сайтов мы будем использовать эмулятор WAP, который будет скачивать WML-файл непосредственно с Web-сервера (или открывать его с локальной файловой системы, как в нашем случае) и воспроизводить все содержимое в окошке эмулятора мобильной трубки. На рисунке показана обобщенная схема взаимодействия мобильного телефона с Web-сервером. Пунктиром обозначено взаимодействие Web-сервера с эмулятором WAP.
Краткий обзор основ WML
Прежде чем приступить к разработке нашего первого WAP-приложения, необходимо представить себе хотя бы основы "беспроводного" языка разметки–WML.
Как уже было отмечено, WML - XML-основанный язык, наследующий набор символов XML-документа. Основу WML составляет понятие карты (card), определяющей единичное взаимодействие между пользователем и сервером. Карты, в свою очередь, группируются в "палубы" (decks). Палубы - наименьшие части WML, которые Web-сервер может послать клиенту и которые, как правило, активируют первую карту в палубе, хотя те же действия можно проделать практически с любой картой палубы. Проводя аналогию, можно представить палубу как HTML-документ, состоящий из множества адресуемых разделов. Это означает, что к каждой карте палубы можно адресоваться непосредственно с использованием специального синтаксиса: #label.
Порядок работы клиента (мобильного телефона) с каждой палубой таков: мобильный телефон скачивает палубу целиком, а показывает только одну карту. Переход от одной карты палубы к другой не требует взаимодействия с сервером и выполняется локально.
WML, в отличие от HTML, - case-sensitive язык компоновки. Это означает, что он различает прописные и строчные буквы. Кроме того, WML преобразовывает несколько последовательных символов новой строки, возврата каретки или табуляции в один.
Рассмотрим простейший WML-документ (предполагается, что читатель знаком с основами XML).
Установите и запустите Nokia WAP Toolkit - появится два окна: первое - представляет исходный текст WML, второе - собственно эмулятор мобильного телефона.
Выберите команду File->New->WML Deck для создания шаблона пустой палубы. Появится пустая форма палубы:
<?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
$BREAK$
по сути, палуба является полноценным XML-документом. Поэтому WML-документ должен начинаться со стандартного XML-заголовка и ссылки на DTD:
<!-- If WML 1.2 features are required, then use the following DOCTYPE instead: <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.2//EN" "http://www.wapforum.org/DTD/wml12.dtd"> --> <wml> <!-- Possible <head> element here. --> <template> <!-- Template implementation here. --> <do type="prev"><prev/> </do> </template> <card id="card1" title="Card #1"> <!-- Possible <onevent> elements here. --> <do type="unknown" label="Next"><go href="#card2"/></do> <!-- Additional <do> elements here. --> <!-- Possible <timer> element here. --> <p align="center"> <!-- Card implementation here. --> <big><b>First Card</b></big> </p> <!-- Additional <p> elements here. --> </card> <card id="card2" title="Card #2"> <p align="center"> <big><b>Second Card</b></big> </p> </card> <!-- Additional <card> elements here. --> </wml>
Порядок работы телефона с нашим первым WML-документом таков. Получив палубу, мобильный телефон покажет текст первой карты. Нажатие на левую программную кнопку приведет к переходу на следующую (вторую) карту - путем добавления всего лишь одной строчки ко второй карте рассмотренного нами примера, а именно:
Можно свободно перемещаться между двумя картами первой палубы посредством нажатия на левую программную кнопку. После работы рекомендую сохранить первый WML-документ File->Save, а затем откомпилировать (кнопка Compile) и выполнить (кнопка Show). После нажатия на кнопку Show эмулятор выполнит все необходимые действия по загрузке палубы и покажет нам ее первую карту в окошке самого эмулятора мобильного телефона. Сразу скажу, что Nokia WAP Toolkit не поддерживает русификацию, поэтому в настоящей статье будут рассматриваться примеры на английском языке. А вот теперь настала пора разобраться в нашем WML-документе со всей обстоятельностью.
Основные элементы и WML-тэгиМеню опций
Тэг <p> в WML выполняет, по сути, те же функции, что и в HTML - он является разделителем абзаца и служит для разделения текстового потока. Тэг "do" служит для ассоциации программных кнопок с определенными действиями:
Наиболее часто используемое значение атрибута "type" - это "accept", и по нажатии на программную кнопку к списку опций будет добавлена новая опция. Атрибут label служит для задания заголовка ассоциируемого действия на экране клиента (экранчике мобильного телефона). Помимо этого выполняемые действия могут быть следующих типов:
"go" - безусловный переход к требуемой карте палубы или к требуемой палубе. Он требует наличия стандартного атрибута "href", который в WML ничем не отличается от своего HTML-собрата. Элемент WML "go" в зависимости от контекста заменяет либо тэг HTML <a>, либо тэг <form>;
"prev" - переход к предыдущей карте;
"refresh" - обновление содержимого дисплея;
"noop" - пустое действие.
Несмотря на всевозможные значения атрибута "type" тэга "do", все они могут быть воспроизведены с помощью лишь одного из них - "go".
Якоря
Якоря - WML-аналоги HTML-тэгов <a>. Они определяются с помощью элемента <anchor>. Якорь может содержать действие, точно так же, как тэг "do". Единственное отличие между якорями и тэгами "do"заключается в том, что якоря не являются опциями, отображаемыми на дисплейчике, однако транслируются как ссылки в WML-потоке, в принципе, точно так же, как и обычные HTML-ссылки. К примеру:
<anchor> Go to card 2 <go href="#card2"/> </anchor>
Переменные
Концепция переменных реализована в WML несколько иначе, чем в обычном HTML или JavaScript. Хотя переменные WML, как правило, используются в WML-скриптах, они могут быть использованы и непосредственно в WML-документах для организации ссылок между картами. Другими словами, в зависимости от значения той или иной переменной можно изменять и поведение карты. Переменной может быть присвоено значение одним из двух способов: с помощью "userthrough" как ввод пользователя либо посредством "setvar" - оператора присвоения значения. Безусловно, манипулировать переменными можно с помощью языков описания сценариев, однако WML позволяет это делать и без использования таковых. В этом, пожалуй, и заключается основное отличие WML от HTML. К примеру, если первая карта запрашивает имя пользователя, а вторая - его приветствует, то в обычном HTML для реализации этого потребовалось бы создавать HTML-форму и обрабатывать ее (читать значения текстового поля, введенного пользователем). В WML все эти действия могут быть заменены простейшей строкой: "Hello,$(firstname)", где переменная "firstname" содержит введенное пользователем значение.
Поскольку значения переменных не уничтожаются при переходах от одной карты к другой, их можно использовать в различных картах. Разумеется, это обстоятельство является преимуществом, хотя и таящим в себе некоторую опасность. Опасность заключается в том, что при переходах от карты к карте фактические значения элементов карты могут начать расходиться с ее реальными значениями. Во избежание этого разработано несколько "событий" и атрибутов, позволяющих разработчикам отслеживать "маршрут", которым пользователи "пришли" к текущей карте "onenterforward" (по переходу "вперед") и "onenterbackward" (по переходу "назад"), и ситуацию, когда все содержимое палубы должно быть "сброшено" (newcontext = "true"). "Сброс" используется для обновления карт, содержимое которых зависит от значений каких-либо переменных.
Пользовательский ввод
Конечно, без пользовательского ввода в WAP-приложениях можно организовать разве что только информационную систему без расширенных возможностей поиска. Для реализации пользовательского ввода используются следующие элементы:
"input" - совсем как в HTML. Пользователь может вводить значения с клавиатуры телефонной трубки, хотя в отличие от персонального компьютера использование клавиатуры мобильного телефона несколько затруднительно. Поэтому не стоит утруждать пользователей вводом длинных фрагментов текста. Атрибут "name" служит для указания значения переменной, которой будет присвоено значение, введенное пользователем;
"select/option" - служит для организации списка выбора и тоже вполне соответствует своему HTML-аналогу:
Значение атрибута multiple = true позволяет устанавливать режим множественного выбора (допускается выбор сразу нескольких значений из списка). Событие "onpick" срабатывает, когда пункт выделяется либо когда с него снимается выделение (select/deselect);
"Postfield" - аналог скрытых полей ввода в HTML-формах. Поле типа "Postfield" не показывается на экране, а используется в основном для передачи упорядоченных пар {имя, значение} от клиента серверу.
Другие элементы
Тэг "img" поддерживается в WML, однако графику следует создавать в специальном формате, называемом WBMP. И хотя некоторые мобильные телефоны поддерживают не только "черно-белую" графику, все-таки рекомендуется отказаться от использования многоцветных изображений с целью экономии памяти (которой у мобильных телефонов очень немного. А новый формат WBMP позволяет WAP-устройствам быстро считывать и отображать на экранчике элементы графики. Для любителей порисовать есть "плагины" для Adobe PhotoShop и Paint Shop Pro, поддерживающие формат WBMP, и перекодировщики графических файлов различных форматов в формат WBMP. И не забудьте установить необходимые значения MIME-типов на сервере для всех файлов формата WAP:
Кроме рассмотренных выше существует множество тэгов, используемых в WML практически без изменений ( то есть так же, как и в обычном HTML), к примеру тэги em , i, b, small и т.д. Тэг BR следует использовать как <br /> в соответствии с нотацией XML.
Таймеры
Довольно часто бывает необходимо выполнить определенные действия по прошествии заданного интервала времени - начиная с определенного момента. На такой случай предусмотрен элемент таймера. Элемент "timer" "следит" за временем (например, с момента загрузки WML-карты) и по истечении заданного интервала времени генерирует событие "ontimer". Событие может быть использовано для выполнения любого задания.
Телефонные особенности
К сожалению, в процессе программирования WAP-сайтов возникают небольшие проблемы. Как того и следовало ожидать, производители мобильных телефонов с таким рвением ринулись перехватывать друг у друга пользователей, что не смогли договориться о поддержке стандартизованного WAP-протокола. И хотя спецификация стандарта WAP существует, многие модели мобильных телефонов тем не менее не поддерживают некоторые тэги, элементы или атрибуты спецификации WML. Так, Nokia 7710 не поддерживает метод передачи данных на сервер "POST", что ставит под вопрос секретность и сохранность передаваемых данных, как результат вынужденного использования метода "GET" вместо "POST". Таких примеров, к сожалению, можно привести очень много.
Еще одно слабое место мобильных телефонов - ограниченный объем памяти, выделяемой для загрузки одной палубы. В частности, Nokia 7710 не загружает палубы с объемом более 1,4 Кбайт. И хотя WAP-шлюз значительно сжимает WML в более компактный двоичный формат, тем не менее объем разрабатываемых палуб приходится по возможности сокращать. Некоторые модели "мобильников" и вовсе не поддерживают таблиц. Поэтому, прежде чем начать разрабатывать сколь-нибудь универсальный WAP-сайт, советую все-таки для начала определить круг пользователей, а следовательно, и круг наиболее типичных моделей телефонов и в соответствии с этим пользоваться только теми тэгами, которые они поддерживают. Остается надеяться, что когда-нибудь мобильные телефоны будут производиться в строгом соответствии со спецификацией WAP.
Конкуренция в области продаж мобильных телефонов породила, в свою очередь, конкуренцию в области средств разработки WAP-сайтов и WAP-приложений. Почти каждая компания - производитель мобильных телефонов стремится разработать свою IDE (Integrated Development Environment) для разработчиков. Выбор автором настоящей статьи именно Nokia Toolkit 1.2 обусловлен наличием сопроводительной документации по WML - как для программистов, так и для дизайнеров, что, несомненно, является преимуществом.
Что же нам понадобится?
Предполагается, что читатель знаком с основами ASP- и SQL-программирования (первых двух частей настоящей статьи для этого вполне достаточно).
Эмулятор
Прежде всего необходимо скачать какой-нибудь эмулятор. Советую воспользоваться Nokia WAP Toolkit 2.0, который можно установить безотлагательно. Следует отметить, что Nokia WAP Toolkit 2.0 позволяет разработчикам писать и проверять WAP-приложения для трубок как серии Nokia 7100, так и для других, созданных в соответствии с концепцией Nokia.
Однако необходимо заметить, что Nokia WAP Toolkit 2.0 есть не что иное, как Java-приложение, требующее наличия Java Runtime (которое устанавливается вместе с Nokia WAP Toolkit 2.0); как следствие - эмулятор несколько медлителен в работе.
Альтернативными вариантами эмуляторов и средств разработки являются:
Перед установкой Ericsson WapIDE 2.1 необходимо установить серверный компонент (WML-сервер Xitami компании iMatrix) Ericsson Wap3PP. Поскольку серверный компонент WML Xitami использует порт с номером 80, не забудьте остановить выполнение IIS (протокол HTTP которого использует именно этот порт) на время сеанса работы WML-сервера.
Сравнивая описанные выше эмуляторы, можно отметить, что все они неплохо справляются со своими задачами. При этом достоинством Nokia WAP Toolkit 2.0 является внушительный сопроводительный материал по WML, WMLScript и WBMP для разработчиков и дизайнеров, UP SDK 4.0 обладает удобным окном "Info", позволяющим отлаживать WAP-приложения, и, кроме того, имеет в своем арсенале различные "конфигурации" для загрузки интерфейсов различных (всего пяти) мобильных телефонов.
Ericsson WapIDE 2.1 - довольно удобное средство (здесь впервые предпринята попытка упорядочить проект WAP-приложения и создана рабочая среда (IDE)), однако создается впечатление некоторой незавершенности решения специалистов компании Ericsson.
Таким образом, средой разработки, к сожалению, нельзя назвать ни один из трех продуктов, все они по сути являются эмуляторами мобильных телефонов и у всех имеется один общий и весьма существенный недостаток: отладка приложений с их помощью крайне сложна по той причине, что в случае возникновения ошибок разработчику будет крайне сложно определить их причины и устранить последствия таковых (UP SDK 4.0. позволяет делать это лишь в очень редких случаях).
$BREAK$
После выбора эмулятора необходимо создать и подготовить базу данных, а для этого - определить конкретную задачу.
Предположим, нам следует разработать систему бронирования авиабилетов. Согласитесь, задача весьма актуальна и для первоначального ознакомления с основами WAP-программирования может показаться несколько сложной. Однако это не совсем так.
Для начала представим, в чем заключается процесс бронирования авиабилетов, и попытаемся формализовать его. Последовательность шагов, описывающих процесс бронирования авиабилетов, такова:
выбор аэропорта назначения;
выбор даты и времени вылета;
выбор билета (класса и цены), а также авиакомпании;
просмотр заказа и его подтверждение;
регистрация заказа в системе с выдачей клиенту регистрационного идентификатора, с помощью которого он сможет в дальнейшем оплатить и получить свой билет.
Как видно, последовательность выполняемых действий по взаимодействию клиента с системой крайне проста, и в нашем случае каждый из перечисленных шагов будет представлен отдельной WML-палубой и отдельным ASP-файлом.
Создание и подготовка базы данных
Для создания базы данных (назовем ее AirTickets) нам понадобится Microsoft Access 97 или 2000. От процесса проектирования базы данных будет зависеть дальнейший объем программной логики нашего WAP-приложения, поэтому этот этап крайне ответственен. Однако принципы проектирования баз данных выходят за рамки настоящей статьи, поэтому, не задерживаясь на них, представим структуру нашей базы данных следующим образом:
Создадим три таблицы для хранения информации о городах назначения (таблица Destinations), датах и времени вылетов (таблица Dates), а также для информации о типе воздушного судна, номере рейса, авиакомпании и наличии авиабилетов (таблица Flights). Именно по данным этих трех таблиц клиент и будет последовательно выбирать подходящий ему билет.
Четвертая таблица предназначена для хранения базы произведенных клиентами заказов, с целью возможной последующей продажи заказанных авиабилетов. Информация о произведенном заказе будет попадать в нее только после одобрения клиентом.
Каждая из трех создаваемых таблиц будет связываться с предыдущей посредством ключевых полей таким образом, чтобы, выбрав город в первой таблице, клиент мог затем выбрать ту дату и время вылетов из второй, которые соответствуют выбранному городу (аэропорту) назначения, а из третьей - те рейсы, которые соответствуют датам из второй таблицы, соответствующим городам из первой. Для этого в каждой из таблиц создадим поля типа "счетчик", повторения в значениях которых запрещены и которые, следовательно, будут однозначно определять выбор пользователя в каждой из таблиц, то есть на каждом этапе интерактива "клиент—приложение".
Так выглядит взаимосвязь трех таблиц:
Как видно из рисунка, первая таблица (Destinations) содержит список всех городов назначения и ключевое поле (счетчик) для связи со второй таблицей (Dates), которая содержит информацию о дате и времени вылета, а также поле связи с предыдущей таблицей (CityID) и своим ключевым полем (счетчиком). Третья таблица содержит дополнительную информацию о типе воздушного судна (PlaneType), количестве оставшихся билетов (Tickets), ценах на них для первого и второго классов (Price1 и Price2), авиакомпании (Company) и номере рейса (FlightNo) и связывается со второй посредством поля TimesID.
Создание и подготовка первого WAP-приложения
Теперь попробуем разобраться, каким же образом осуществляется работа WAP-приложения на практике. Для начала необходимо посмотреть на мобильный телефон глазами разработчика и разобраться, что же все-таки доступно нашему клиенту.
Так выглядит наш маленький браузер. Читатель наверняка уже понял, что нам придется ограничиться лишь немногими навигационными возможностями (за исключением, пожалуй, некоторых продвинутых моделей сотовых телефонов). Далее мы рассмотрим наиболее типичный мобильный телефон классической формы без "выкрутасов". Программные кнопки, как правило, используются для выбора из меню таких требуемых функций (или для переходов к возможным подменю), как "Select" и "OK" в нашем случае. Кнопки с изображением стрелочек служат для пролистывания списков вверх, вниз, вправо и влево соответственно ( как правило, не уменьшаются в экранчике).
Прежде чем мы приступим...
Во-первых, следует запомнить, что для того, чтобы ASP-код в сочетании с WML-директивами выполнялся корректно, в самой первой строке каждого из ASP-файлов необходимо определять MIME мнемокод для его содержимого следующим образом:
Response.ContentType = "text/vnd.wap.wml"
Таким образом, для странички устанавливается MIME-тип WML-документа - text/vnd.wap.wml независимо от расширения файла (.asp в нашем случае).
В противном случае WAP-эмулятор определит весь последующий код как HTML и при его компиляции выдаст ошибку.
Во-вторых, успешное выполнение ASP-скриптов в WAP-эмуляторах возможно лишь в том случае, когда скрипты выполняются как серверные, а не как локальные (файлы операционной системы). Поэтому необходимо определить сервер и при работе с эмуляторами пользоваться не открытием ASP-файла из операционной системы, а открытием ссылки (в эмуляторах, как правило, Location), к примеру http://localhost/Wap/index.asp или http://aquarius.mmtech.ru/Wap/index.asp.
В-третьих, часто WAP-сайты совмещены на серверах с обычными не-WAP-сайтами. А если быть более точным, многие сайты имеют свои WAP-версии, поэтому целесообразно предусмотреть скрипт, который будет определять дальнейшее "развитие событий" в зависимости от того, загрузился клиент с помощью обычного браузера или в качестве такового им был использован мобильный телефон. Затем этот скрипт следует сделать документом по умолчанию.
Это очень просто: в Interner Service Manager’e, во вкладке "Documents" свойств Web-сайта следует добавить (кнопка Add...) имя файла, к примеру dispatcher.asp, и с помощью стрелочек придать ему первоочередное право на загрузку. Далее следует написать соответствующий скрипт и разместить его в корневом каталоге сайта (как правило, C:\InetPub\wwwroot).
Напишем скрипт - определитель версии браузера (файл dispatcher.asp):
<% Response.Buffer = TRUE 'разрешает буферизацию вывода. 'Это обеспечивает обработку всего документа на сервере '(генерацию соответствующей странички) и только потом посылку его клиенту. Dim IsWap ' Логическая переменная httpAccept = LCase(Request.ServerVariables("HTTP_ACCEPT")) ' Чтение серверной переменной "HTTP_ACCEPT" 'и ее преобразование в строку из прописных символов с помощью функции LCase If Instr (httpAccept,"wap") Тhen 'Если строка содержит подстроку "wap" Response.Redirect "Wap/index.asp" : Response.Flush : Response.End 'То переадресоваться к"Wap/index.asp" и завершить работу Else Response.Redirect "index.asp" : Response.Flush : Response.End ' В противном случае переадресоваться к "index.asp" и завершить работу End if %>
Выбор аэропорта назначения (index.asp)
Для облегчения организации программного доступа к нашей базе данных, вынесем его в отдельный ASP-скрипт и будем "включать" его в тело каждого из последующих скриптов по мере необходимости:
<% Option explicit Dim conn Set conn = Server.CreateObject("ADODB.Connection") conn.open "DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" _ & Server.Mappath("AirTickets.mdb") & ";" %>
Соединение с базой данных выполним наиболее простым и компактным образом (не забудьте разместить файл базы данных "AirTickets.mdb" в корневом каталоге сайта, так как Server.Mappath будет возвращать именно этот путь).
Теперь можно приступить к опросу нашего клиента. Согласитесь, что для начала неплохо было бы его поприветствовать:
Данная карта и будет приветствовать наших клиентов, показывая заставку из файла logo.wbmp, а если ей это не удастся, то заменит иллюстрацию альтернативной надписью "WAP AirLines", поле чего (по истечении двух секунд) будет загружаться карта "card1" палубы:
... <card id="card1" title="Flight to:"> <% ' Выборка всех строк таблицы Destinations SQLquery = "SELECT * FROM Destinations" set rsDestinations = conn.Execute(SQLquery) ' Если таблица не содержит ни одной if rsDestinations.eof then rsDestinations.close ' строки ' то закрыть соединение с базой данных set rsDestinations = nothing Response.write("<p>Sorry, this flight " _ & "is not available now!") ' и выдать сообщение о невозможности ' бронирования авиабилетов Response.write("<br /><anchor title='") Response.write("ReStart'") Response.write(">Go Home to Restart-><go href='") Response.write("index.asp'") Response.write("/></anchor>") Response.write("</p></card></wml>") Response.end end if %> <p><br /> <select name='TheCity'> <% Do while not rsDestinations.eof 'В цикле отобразить в качестве элементов списка 'все аэропорты назначения response.write("<option value='" _ & rsDestinations("CityID") _ & "'>" & rsDestinations("City") _ & "</option>" & vbcrlf) rsDestinations.MoveNext Loop %> </select> <anchor title="Proceed!">Proceed-> <!-- Обеспечить переход к следующему этапу опроса --> <go href="Date.asp" method="get"> <!—Передав идентификатор выбранного пользователем города из списка --> <postfield name="TheCity" value="$(TheCity)"/> </go> </anchor> </p> <% rsDestinations.close set rsDestinations = nothing %> </card> ...
Как видите, WML-страница формируется из базы данных в основном посредством ASP-метода response.write, отображающего список городов (значение полей rsDestinations("City") таблицы). Заметьте, что в качестве возвращаемого значения списка выбора select фигурирует значение именно ключевого поля первой таблицы - rsDestinations("CityID").
Далее выбранное пользователем значение передается в следующий скрипт (Date.asp) с помощью якоря Proceed.
Выбор даты и времени вылета (Dates.asp)
Скрипт Date.asp, в свою очередь, организует список из всех возможных значений дат и времени вылета рейсов, соответствующий значению выбранного города на основании полученного значения.
Для этого в первую очередь необходимо считать значение, введенное пользователем на предыдущем этапе:
<% ... City = Request("TheCity") ... %>
Затем считать из первой таблицы название выбранного города в переменную, с тем чтобы показать его клиенту в дальнейшем:
<% ... SQLQuery = "SELECT * FROM Destinations WHERE CityID = " _ & Request("TheCity") set rsCity = conn.Execute(SQLQuery) Set CityName = rsCity("City") ... %>
После этого необходимо считать из второй таблицы все соответствующие значения и сформировать список из них:
<% ... SQLQuery = "SELECT * FROM Dates WHERE CityID = " _ & Request("TheCity") set rsDates = conn.Execute(SQLQuery) ... %>
Аналогично предыдущему случаю здесь и во всех последующих скриптах следует подумать о возможном отсутствии значений в таблице:
<% ... if rsDates.eof then rsDates.close set rsDates = nothing Response.write("<p>Sorry, this flight is not available now!") Response.write("<br /><anchor title='") Response.write("ReStart'") Response.write(">Go Home to Restart-><go href='") Response.write("index.asp'") Response.write("/></anchor>") Response.write("</p></card></wml>") Response.end end if ... %>
Теперь покажем клиенту название города, который он выбрал, и предоставим ему возможность выбрать дату и время вылета:
Как видно, поля списка формируются из сочетаний дат и времени полей второй таблицы. Здесь, так же как и в первом случае, фигурирует значение ключевого поля таблицы "Dates":
rsDates("DatesID").
Передадим в следующий скрипт (Flight.asp) два значения: название города назначения и ключевое значение, соответствующее выбранному пользователем значению времени вместе с датой во второй таблице:
Покажем надпись перехода к предыдущему экрану с помощью следующего элемента:
<do type="prev" label="Previous"> <prev/> </do>
Выбор атрибутов рейса (Flight.asp)
Теперь пришло время выяснить, имеются ли билеты на выбранный клиентом рейс. Конечно, данный момент - не самый удачный для этого (по-хорошему это необходимо делать на каждом этапе регистрации выбранных пользователем значений, что потребует ввода полей с количеством оставшихся билетов во все таблицы), но для лучшего усвоения материала настоящей статьи мы не будем загромождать наши скрипты дополнительными проверками, а таблицы - дополнительными полями.
Получим значения, переданные предыдущим скриптом, и считаем их:
... <% ... Dim TheDateTime Set TheCity = Request("City") Set TheDateTime = Request("DateTime") ... %>
Выберем из таблицы дат и времени рейсов значение, соответствующее номеру выбранного пользователем поля, и считаем в переменные соответствующие значения даты и времени:
... <% SQLQuery = "SELECT * FROM Dates WHERE DatesID = " _ & Request("DateTime") set rsDateTime = conn.Execute(SQLQuery) Set FDate = rsDateTime("FlightDate") Set FTime = rsDateTime("FlightTime") ... %>
Теперь из третьей таблицы выберем строки, соответствующее значениям времени и дате, выбранным клиентом на предыдущем этапе:
... <% SQLQuery = "SELECT * FROM Flights WHERE TimesID = " _ & Request("DateTime") set rsFlights = conn.Execute(SQLQuery) ... %>
Покажем пользователю выбранные им значения и огранизуем список выбора класса и цены авиабилетов: