WAP и ASP - часть III
В этой статье мы рассмотрим практическое приложение, созданное с использованием WML и ASP. Мы разработаем "корзину" для магазина некой компании по продаже бумаги для печатающих устройств. Предложенное приложение покажет продавцу компании состояние склада, используя его WAP-телефон, и в то же время позволит ему разместить заказ для клиента. Это приложение иллюстрирует некоторые концепции в WML и то, как WML и ASP могут использоваться вместе для создания динамических WAP-приложений.
Проектирование БД
Компания поддерживает базу данных Stock.mdb, содержащую следующие таблицы: Inventory (Опись), Orders (Заказы) и Staff (Персонал). Отношения для таблиц следующие:

Таблица Inventory содержит идентификатор (SKU) изделия и его запас, а также себестоимость и продажную цену. Таблица Orders используется, чтобы фиксировать заказы, сделанные продавцом компании. Таблица Staff содержит информацию входа в систему продавца.
Запуск приложения
Чтобы проверить примеры приложения, описываемые в этой статье, вы можете использовать UP.SDK 4.0 Beta 1 WAP Phone Emulator. UP.SDK доступен для загрузки на сайте Phone.com. Чтобы протестировать приложение, используйте UP.SIMULATOR.

WAP Телефоны, поддерживающие UP.BROWSER
В настоящее время более 20 изготовителей лицензировали UP.BROWSER для своих мобильных телефонов. Среди них Motorola, Ericsson, Nokia и Toshiba. Для получения полного списка мобильных телефонов, поддерживающих UP.BROWSER, посетите http://updev.phone.com/dev/ts/up/phones.html.
Переносимость приложения
Пример, иллюстрированный здесь, был проверен только на UP.SIMULATOR. Поскольку различные изготовители телефонов имеют слегка различное исполнение WML, приложение, написанное в WML и проверенное на одной программе просмотра, может не работать правильно в другой. Поэтому возможно, что при загрузке данных исходных текстов вам потребуется адаптировать их к некоторым иным программам просмотра.
Вход в программу

Каждому продавцу от нашей гипотетической компании PaperClip присваивается StaffID и пароль для доступа к информации склада.
Код для входа продавца:
<% Response.ContentType = "text/vnd.wap.wml" %> <?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <!-- Welcome Screen --> <card id="Welcome" title="Welcome"> <p> Welcome to PaperClip WAP Services <img alt="" localsrc="paperclip" src=""/> <br/> StaffID ? <input name="StaffID" type="text" maxlength="8" /> Password ? <input name="Password" type="password" maxlength="8" /> <do type="accept" label="Login"> <go href="Login.asp" method="post"> <postfield name="StaffID" value="$StaffID" /> <postfield name="Password" value="$Password" /> </go> </do> </p> </card>
Первая часть кода должна установить тип документа. Вы можете обратить внимание, что вышеупомянутый код содержит только WML-разметку за исключением первой строки:
<% Response.ContentType = "text/vnd.wap.wml" %>
Использование свойства ASP Response.ContentType гарантирует, что тип MIME установлен правильно.
<% Response.ContentType = "text/vnd.wap.wml" %> <?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
Обратите внимание на изображение скрепки на экране входа в систему. Оно создано WML–тэгом
<img>.<img alt="" localsrc="paperclip" src=""/>
Атрибут localsrc <img> элемента определяет список
изображений, доступных телефону локально. Для полного списка доступных
изображений, обратитесь к WML Language Reference, стр. 37.
Большинство
элементов WML подобны HTML. Прежде, чем мы перейдем к следующему экрану,
рассмотрим этот код:
<do type="accept" label="Login"> <go href="Login.asp" method="post"> <postfield name="StaffID" value="$StaffID" /> <postfield name="Password" value="$Password" /> </go> </do>
Элемент сопоставляет задачу с клавишей интерфейса пользователя телефона. Для UP.SIMULATOR:

После того, как продавец ввел свой StaffID и пароль, он нажимает на клавишу accept (Login), для подтверждения подлинности его идентификации.
Идентификация
Как только продавец введет StaffID и пароль, приложение перейдет к подтверждению его подлинности.
<template>
<do type="options" label="Search">
<go href="Search.asp" />
</do>
</template>
<!-- Login Card -->
<card id="Login" title="Login">
<p>
<%
sqlQuery = "SELECT * FROM Staff WHERE StaffID='"
Request.Form("StaffID") & "' AND Password='"
Request.Form("Password") & "'"
set rs = conn.Execute(SQLquery)
if rs.EOF then
Response.Write "Invalid Login"
Response.Write "<do type='accept' label='Retry'>"
Response.Write " <go href='index.asp'/>"
Response.Write "</do>"
'---override the <template> element
Response.Write "<do type='options' label=''>"
Response.Write " <go href=''/>"
Response.Write "</do>"
else
Session("StaffID") = Request.Form("StaffID")
Session("Password") = Request.Form("Password")
Response.Write "<strong>Welcome, " &
rs.Fields("Name") & "! </strong><br/>"
sqlQuery = "SELECT * FROM Inventory"
Set rs = conn.Execute(SQLquery)
%>

Если продавец не прошел идентификацию, появляется экран, показанный здесь. Заметьте, что, когда пользователь не сумел войти, элемент <template>:
<template> <do type="options" label="Search"> <go href="Search.asp" /> </do> </template> заменяется на: '---override the <template> element Response.Write "<do type='options' label=''>" Response.Write " <go href=''/>" Response.Write "</do>"
Это гарантирует, что функция поиска недоступна неидентифицированному пользователю. Как только пользователь прошел регистрацию, его StaffID и пароль сохраняются в объекте session.
Относительно поддержки Cookies
В этом приложении был использован объект Session в ASP. Поскольку объект Session требует поддержки cookies на стороне клиента, предположим, что платформа, которая используется, чтобы выполнить это приложение, поддерживает cookies. В частности, при разработке приложений, которые будут развернуты в реальном мире, гарантируйте, что Ваш WAP шлюз использует поддержку cookies. UP.SIMULATOR поддерживает cookies, и, таким образом, мы можем использовать их для создания двух переменных сессии:
Session("StaffID") = Request.Form("StaffID")
Session("Password") = Request.Form("Password")
Эти две переменные сессии могут использоваться повсюду в приложении для идентификации пользователя, который его запустит. Если платформа не поддерживает cookies, то StaffID и пароль должны передаваться от входной формы до их повторного использования при помощи методов get или post (элементы <go> и <postfield >).
Получение списка описи

Как только пользователь идентифицирован, сценарий ASP начнет генерировать список доступных изделий. Для простоты изложения ограничим число предметов в списке десятью предметами.
<anchor>
View Cart
<go href="cart.asp" />
</anchor>
<br/>Stock List:
<select name="stockID">
<%
while not rs.EOF
%>
<option value="<% =rs("SKU") %>">
<% =rs("SKU") %>-<% =rs("Name") %>(<% =rs("OnHand") %>)
$$<% =rs("CostPrice") %> - $$<% =rs("SellPrice") %>
</option>
<%
rs.MoveNext
Wend
%>
</select>
<do type="accept" label="Order">
<go href="cart.asp" method="get">
<postfield name="SKU" value="$stockID" />
</go>
</do>
Мы будем использовать элемент <anchor> для создания гиперсвязи просмотра содержания корзины покупок. Что касается списка предметов, доступных в таблице описи, будем использовать элементы <select> и <option>.
В этом примере информация о товаре выводится в одну строку. На UP.SIMULATOR длинные строки переводятся на следующую строку. Однако внимательный читатель может заметить, что различные WAP - броузеры имеют различную обработку длинных строк и могут просто обрезать все избыточные символы, которые не помещаются на той же самой строке. Поэтому с самого начала рассмотрите платформу, на которой будет выполняться ваше приложение.
Ввод для функций «Заказ» и «Поиск» соответственно:
>do type="accept" label="Order"< >go href="cart.asp" method="get"< >postfield name="SKU" value="$stockID" /< >/go< >/do< >template< >do type="options" label="Search"< >go href="Search.asp" /< >/do< >/template<
Обратите внимание, что здесь был
использован элемент < template >, чтобы отделить параметр «Поиск». Элемент
template определяет связи на уровне деки и относится ко всем платам в
деке.
Если пользователь щелкает на клавише Up, «фокус» переходит на ссылку
View Cart.
Формат отображенного товара - SKU-Название- (Количество
-Себестоимость-Цена Продажи).

Клавиша accept теперь изменена на Link. Щелчок по клавише accept покажет содержимое корзины.
Добавление товара а корзину
Для добавления товара в корзину просто выберем элемент и нажмем на клавишу accept.

При добавлении товара отображается содержание корзины. Изображенный ниже код добавляет товар в корзину:
sqlQuery = "INSERT INTO Orders (StaffID, SKU, Qty, OrderDate) "
sqlQuery = sqlQuery + "Values ('" & Session("StaffID") & "','" & "
sqlQuery = sqlQuery + "Request.QueryString("SKU") & "',1,'" & date & "')""
On Error Resume Next '---prevent duplicate items from crashing my program---
set rs = conn.Execute(sqlQuery)
rs.Close 'Displaying the Cart Content
Следующий код показывает содержание корзины:
'---Displays the cart content---
Response.Write "<br/><strong>Cart Contents</strong>"
sqlQuery = "SELECT * FROM Orders INNER JOIN Inventory ON
Orders.SKU=Inventory.SKU WHERE StaffID='" &
Session("StaffID") & "'"
set rs = conn.Execute(sqlQuery)
if not rs.EOF then
Response.Write "<select name='CartItem'>"
While not rs.EOF
Response.Write "<option value='" & rs("SKU") & "'>" & rs("SKU") & _
"-" & rs("Name") & "(" & rs("qty") & ")</option>"
rs.MoveNext
Wend
Response.Write "</select>"
rs.Close
%>
<do type="option" label="Qty">
<go href="EditCart.asp" method="get">
<postfield name="SKU" value="$CartItem" />
</go>
</do>
<%
else
%>
<br/>Cart is empty!
<do type="accept" label="Main">
<go href="login.asp" method="post">
<postfield name="StaffID" value="<% =Session("StaffID") %>" />
<postfield name="Password" value="<% =Session("Password") %>" />
</go>
</do>
<%
end if
%>
Заметьте, что, если тележка пуста, клавиша ACCEPT будет отображена как Main и возвратит пользователя к основной экранной странице.

В корзине две ссылки [Главная] и [Поиск], и два параметра: Delete и Qty (чтобы изменить количество товара). Чтобы удалить товар, щелкните на клавише ACCEPT (Delete).
<template> <do type="accept" label="Delete"> <go href="Cart.asp" method="get"> <postfield name="delete" value="$CartItem" /> </go> </do> </template>
Чтобы изменить количество товар, щелкните на клавише option (Qty).
<do type="option" label="Qty"> <go href="EditCart.asp" method="get"> <postfield name="SKU" value="$CartItem" /> </go> </do>
Изменение количества товара в корзине
Когда пользователь нажимает на клавишу option (Qty), исполняется файл EditCart.asp.
<card id="GetQty" title="Get Qty">
<p>
Qty? <input name="Qty" type="text" maxlength="3" />
<do type="accept" label="Set">
<go href="EditCart.asp" method="get">
<postfield name="SKU" value="<% =Request.QueryString("SKU")%>" />
<postfield name="qty" value="$Qty" />
</go>
</do>
</p>
</card>

Чтобы изменить количество, введите число и нажмите на клавишу accept ( Set ).
sqlQuery = "UPDATE Orders SET Qty=" &
Request.QueryString("qty") & _
" WHERE StaffID='" &
Session("StaffID") & "' AND SKU='" & _
Request.QueryString("SKU")
& "'"
set rs = conn.Execute(sqlQuery)
'response.write
sqlquery
'---display the content of the
cart---
Response.Redirect "cart.asp"

После изменения количества WAP – броузер перенаправит на cart.asp, где отобразится обновленное содержание корзины.
Удаление товара из корзины

Для этого нажмите на кнопку accept (Delete).
'---check to see if this is a deletion?---
ItemToDelete = Request.QueryString("delete")
if ItemToDelete<>"" then
'---delete an item---
sqlQuery = "DELETE FROM Orders WHERE StaffID='" & Session("StaffID")"
sqlQuery = sqlQuery + " & "' AND SKU='" & ItemToDelete & "'""
set rs = conn.Execute(sqlQuery)
'rs.Close
Поиск товара
Поиск можно производить из основной страницы и со страницы корзины

Для поиска введите SKU товара и нажмите на кнопку accept (Locate).

sqlQuery = "SELECT * FROM Inventory WHERE SKU LIKE '%" & SearchStr & "%'" Set rs = conn.Execute(SQLquery)Выведем результаты поиска
Response.Write "<select name='stockID'>"
While not rs.EOF
Response.Write "<option value='" & rs("SKU") & "'>" & rs("SKU") & "-" _
& rs("Name") & "(" & rs("OnHand") & ") $$" & rs("CostPrice") & "-$$" _
& rs("SellPrice") & "</option>"
rs.MoveNext
Wend
Response.Write "</select>"

Со страницы поиска можно заказать товар или перейти на главную страницу
Отладка WAP-программы
Немногие симуляторы позволяют удобно работать с отлаживаемым WAP-приложением. Однако, в UP.Simulator во все время работы открыто окно Phone Information Window.

Окно Phone Information представляет полезную информацию для WAP разработчика. В нем отображены все HTTP запросы и WML страницы, которые вы загружаете с сервера. Наиболее полезная особенность - способность показать неправильно созданную WML страницу. Пример ниже:
<% Response.ContentType = "text/vnd.wap.wml" %> <?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <!-- Welcome Screen --> <card id="Welcome" title="Welcome"> <p> Welcome to PaperClip WAP Services <img alt="" localsrc="paperclip" src=""/> <br> StaffID ? <input name="StaffID" type="text" maxlength="8" /> Password ? <input name="Password" type="password" maxlength="8" /> <do type="accept" label="Login"> <go href="Login.asp" method="post"> <postfield name="StaffID" value="$StaffID" /> <postfield name="Password" value="$Password" /> </go> </do> </p> </card>
Обычная ошибка - пропущено "/" после <br>. UP.Simulator отобразит такую картинку:

В окне Phone Information мы получим
======================= WML Errors ===================== WML translation failed. (10) : error: Expected tag end(>) instead of <newline> (10) : error: Expected </ instead of TEXT '' (10) : error: Invalid element 'PCDATA' in content of 'br'. Expected closing tag (10) : error: Invalid element 'input' in content of 'br'. Expected closing tag (11) : error: Invalid element 'PCDATA' in content of 'br'. Expected closing tag (11) : error: Invalid element 'input' in content of 'br'. Expected closing tag (12) : error: Invalid element 'do' in content of 'br'. Expected closing tag (18) : error: Close tag 'p' does not match start tag 'br' (19) : error: Close tag 'card' does not match start tag 'p' (29) : error: Close tag 'wml' does not match start tag 'card' (29) : error: Expected the end of root element instead of end of file ======================= End Errors ===================== ************************ Current WML ****************************************** <?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD /wml_1.1.xml"> <wml> <!-- Welcome Screen --> <card id="Welcome" title="Welcome"> <p> Welcome to PaperClip WAP Services <img alt="" localsrc="paperclip" src=""/ <br> StaffID ? <input name="StaffID" type="text" maxlength="8" /> Password ? <input name="Password" type="password" maxlength="8" /> <do type="accept" label="Login"> <go href="Login.asp" method="post"> <postfield name="StaffID" value="$StaffID" /> <postfield name="Password" value="$Password" /> </go> </do> </p> </card> </wml> ******************************************************************************** Translation failed for content-type: text/vnd.wap.wml ----------------- DATA SIZE ------------------------ Uncompiled data from FILE is 266 bytes. ...found Content-Type: text/vnd.wap.wml. Compiled WAP binary is 96 bytes. ----------------------------------------------------
В сообщении об ошибках мы можем видеть, что строка 10 содержит ошибку и что ожидается "/". Мы можем также просмотреть коды WML, которые посланы UP.SIMULATOR. Коды отображены в конце окна Phone Information. Еще одна интересная информация – размер неоткомпилированных и откомпилированных WAP данных.
----------------- DATA SIZE ------------------------ Uncompiled data from FILE is 266 bytes. ...found Content-Type: text/vnd.wap.wml. Compiled WAP binary is 96 bytes. ----------------------------------------------------
Заключение
В этой статье Вы увидели, как типичное приложение E-коммерции может быть адаптировано для WAP устройств. Очевидно , что размер экрана накладывает серьезные ограничения на разрабатываемые приложения. Однако есть и хорошие новости. Навыки программирования HTML и ASP существенно помогают при создании приложений WAP. Очевидно, в ближайшие месяцы компании будут писать две различные версии приложений для Интернет: одну для Web, а другую для WAP. Другая проблема – совместимость броузеров для WAP.Это напоминает создание страниц под Microsoft Internet Explorer и Netscape Navigator.
