В этой статье мы рассмотрим практическое приложение, созданное с использованием 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>"
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.