Professional Documents
Culture Documents
Фролов А. В. Базы данных в Интернете практическое руководство по созданию Web-приложений с базами
Фролов А. В. Базы данных в Интернете практическое руководство по созданию Web-приложений с базами
в Интернете
практическое руководство по созданию
Web-приложений с базами данных
. Р У С С К И РЕШШ
УДК 004.7
ББК 32.973.202
ФЭ1
ISBN 5-7502-0174-0
УДК 004.7
ББК 32.973.202
getDay 398
getHours 398
getMinutes •. 398
getMonth 398
getSeconds 398
getTime 398
getTimeZoneOffset 398
getYear 399
parse 399
setDate 399
setHours 399
setMinutes 399
setMonth 400
setSeconds 400
setTirne 400
set Year 400
toGMTString 400
toLocaleString 400
UTC 400
Встроенные функции 401
eval ! ; 401
parselnt ". 401
parseFloat 401
escape 401
unescape 401
Charset 406
ContentType 406
Expires 406
ExpiresAbsolute 406
IsClientConnected 406
Status 406
Методы 406
AddHeader 406
AppendToLog 406
BinaryWrite 406
Clear! 407
End 407
Flush 407
Redirect 407
Write 407
Объект Application 407
Наборы 407
Contents 407
StaticObjects 407
Методы 407
События 407
Объект Session 408
Наборы 408
Contents 408
StaticObjects 408
Свойства 408
CodePage 408
LCID 408
SessionID 408
Timeout 408
Методы 408
События 408
06 авторах 415
Предисловие ко второму изданию
Уважаемые читатели!
Мы рады представить Вам второе издание книги «Базы данных в Интерне-
те». Мы исправили ошибки, обнаруженные нами в первом издании, а также об-
новили содержимое компакт-диска, прилагаемого к книге. В частности, помес-
тили на компакт-диск новые версии пакетов исправлений для Microsoft Visual
Studio (Service Pack 4) и для Microsoft SQL Server 7.0 (Service Pack 2).
Пока готовилось второе издание, некоторые изменения претерпели и Web-
страницы издательства «Русская Редакция». Мы обновили узел Web «Русской
Редакции» (http://www.rusedit.ru), а также разработали для издательства Интер-
нет-магазин IT Book (http://itbook.ru).
ghllp //www luiedit ru/ Microsoft Inteinet tiiplorei pinvidert by www лигапииЬа in
'
Ж
услуги юр г* с*йт»
НАШИ КНИГИ Н О В 01
' ПИАНЫ
Раширвнный
Учебный курс
Microsoft Windows 98?NT/2000
Базы данных
Технологии Интернета
Visual С и Visual C++
Официальный тест
ПИШИТЕ НАМ Visual Basic и VBA
Популярные издания
I J
| Учебный курс
Служоа клталогпв
распределенных Active Diiectory в
приложений НА Microsoft Windows 200(1.
Microsoft Visual Basic Ученным курс
6.0. Учеоныи курс Microsoft Coip
Microsoft Cap. Книга содержит всю
Предисловие ко второму изданию XVII
Требования к читателям
Мы предполагаем, что Вы уже изучили язык HTML и имеете опыт создания хотя
бы простейшего сервера Web. Это может быть домашняя страничка на сервере
поставщика услуг Интернета, сервер в интрасети или даже сервер Web на Ва-
шем локальном компьютере. Для создания сервера Web Вам необходимо хоро-
шо знать Microsoft Internet Information Server версии 4.0 (мы рассказываем об
установке и настройке именно этого сервера) или более новой.
Неплохо также, если Вы освоите какие-нибудь средсгва визуального проек-
тирования документов HTML, такие, как Microsoft FrontPage. Список литера-
туры, которая понадобится Вам для этого, приведен в конце нашей книги.
Так как книга посвящена созданию приложений с базами данных, Вам нуж-
но уметь работать с Microsoft SQL Server и знать язык SQL. Мы не рассмотри-
XXIV Базы данных в Интернете. Практическое оуководство
Структура книги
В первой главе описаны отличия приложений Web от обычных программ. Рас-
смотрен принцип работы сервера Web, объяснена разница между активными и
пассивными серверами Web. Кроме того, из этой главы Вы узнаете о серверной
активности, достигаемой средствами технологии ASP, расширений CGI и ISAPI,
а также об активности на стороне клиента, связанной с применением сценари-
ев JavaScript, VB Script, элементов управления ActiveX и аплетов Java. Здесь
перечислены преимущества и недостатки использования серверной активности
и активности на стороне клиента в различных ситуациях. Приведен краткий
обзор методов интеграции сервера Web и сервера СУБД, связанных с исполь-
зованием расширений CGI, ISAPI, драйверов ODBC, объектов OLE DB, и ADO.
Анализируются недостатки и преимущества этих методов при создании прило-
жений Web.
Практическая часть первой главы содержит рекомендации по установке про-
граммных средств, необходимых для дальнейшей работы с книгой, — MS IIS 4.0,
Введение XXV
Благодарности
Мы благодарим генерального директора издательства компьютерной литерату-
ры «Русская Редакция» (http://www.rusedit.ru) Михаила Царейкина, главного
редактора Андрея Козлова и редактора Юлию Леонову, усилиями которых ста-
ло возможным появление этой книги.
Стараниями Андрея Козлова и Юлии Леоновой книга стала интереснее и
понятнее, а определения терминов — последовательнее и точнее.
Авторы книги выражают признательность Максиму Синеву и Сергею Ножен-
ко из компании Spektrum Web Development (http://www.spektrum.org.ru) за
многочисленные консультации и предоставленные исходные тексты разработан-
ных ими проектов, в частности элемента управления MTASend, предназначен-
ного для передачи почтовых сообщений. В нашей книге мы отразили накоплен-
ный ими опыт реализации коммерческих проектов в Интернете и интрасетях.
ГЛАВА 1
В этой главе мы рассмотрим основные понятия, такие, как активный сервер Web,
и относительно новый класс программ — приложения Web. Свое название они
получили из-за того, что их работа самым непосредственным образом связана с
серверами Web, составляющими основу информационной структуры Интерне-
та. Эта глава имеет ознакомительный характер. Введенные здесь понятия будут
раскрыты в последующих главах нашей книги.
Компьютер
пользователя Сервер Web
с браузером
Компьютер
пользователя Серзер Web
с браузером
Приложения Web
В нашей книге мы постоянно будем-Оперировать такими понятиями, как «при-
ложения Web», «приложения для Интернета и интрасети». Теперь, когда Вы
узнали об активных серверах Web, мы раскроем эти понятия. Однако вначале
напомним, как работают обычные программы.
Первые программы для мэйнфреймов работали в пакетном режиме. Им были
доступны все ресурсы компьютера. Немного позже появились интерактивные
системы, поддерживающие одновременное обращение к ресурсу компьютера
многих пользователей при помощи неинтеллектуальных терминалов. Создате-
ли таких систем учли, что программам придется работать в многозадачном и
многопользовательском режиме. Если система содержала базу данных, то все
интерактивные пользователи обращались к ней через терминалы с помощью
специального программного обеспечения, работающего на мэйнфрейме.
В рамках приложений Web на стороне клиента работает браузер, который по
своим интеллектуальным возможностям далеко опережает алфавитно-цифровые
или графические терминалы. Как видно из следующих разделов, браузер спосо-
бен предварительно обрабатывать данные, отправляемые на сервер, а также об-
рабатывать и представлять результаты, полученные от сервера, в удобном для
пользователя виде.
В роли основного сервера для приложений Web выступает, как можно легко
догадаться, сервер Web — разумеется, активный.
6 Базы данных в Интернете. Практическое эуководство
Аплеты Java
Подмножество приложений Java, называемых аплетами Java, используют наря-
ду с клиентскими сценариями для организации активности на стороне клиен-
Базы данных в Интернете. Практическое руководство
кенбгодиныи злгтентом усшшшго вивихя бизнеса как опя неЗопышп §хрн, так а
гая крупных корпораций. Эта канта прещгаэданеш рукоюдитепяк ПШЕОШИЙ я
изрдаривш, технический днрнторал, CIKUKUIHCIBK гю агтоиапоадан вгонвса к ЕССМ
book»ru
Найдено книг: 17 {в щ-
ызаимапвиаеаш
•
Проект Интернет-магазина
Параллельно увеличению влияния Интернета на все сферы человеческой дея-
тельности появилась и быстро развивается электронная коммерция. В Интер-
нете открываются все новые и новые электронные магазины, благодаря которым
Вы можете покупать товары в разных странах мира, не вставая из-за своего ра-
бочего стола.
Наша книга, как мы надеемся, поможет Вам в вашей практической деятель-
ности, поэтому мы рассмотрим вопросы создания отдельных элементов такого
магазина. Предположим, он торгует книгами (а на что еще, спросите Вы, хватит
фантазии авторов книг?), но Вы, конечно, вправе разместить в нем любой дру-
гой товар.
В качестве примера мы приведем список книг, расположенный в новом Ин-
тернет-магазине IT Book (рис. 1-5).
Базы данных в Интернеге. Практическое руководство
a afJ J 1 1М я !
* " -"-: "'i"-''•'"''"•" "•"• "" "' ^'Г'"-'т"'''"'^ *fl»fioluv.pp.(irtb2-S:CAIF3aEFlffiB Miami* Internet Liplo. ИВЕ)
УЧЕБНЫЙ КУРС
Ученный typt
Microsoft Civp.. CD-RO*f, 512 стр.. с ал. ХЮО >
MS Sterner Ыо.тлПи» S&IV*F 4.0 « Mi Pi<i«s
Изменив значения в поле Кол-во или i тку фпатаов в поле ClTK.il ш покупки, щелкните к-опку
Пересчитать
Всего Вы отобрали 7В экэ 5 наимзн на сумму 1 '350 руб. (Ееэ учета доставим)
Йптовын покупателям: для пс-л/чения окончательной суумы заказа примените скидку, согласованн
поставщиком
ЙИЮВШЕШ Доставка
Перед; оформлением заказа выберите наименование области, края или республику, куда необ«одимс
поставить приобретенный Вами товар В эаеисимо:ти от BaiLerc выбора а также от общего веса
приобретении» «чиг будет рассчитана стоимость доставки.
:
Передает номер своей
Выполняет операцию
кредитной карточки
снятия денег со счета
в процессинговый
покупателя
центр
СП ^ __
Посетитель '"—
Интернет-магазина роцессинговый
центр
Передает результат
заверщения транзакции
и номер транзакции
на сервер
Интернет-магазина
Сообщает покупателю
о результате
выполнения платеса
Сервер Web
Для работы с книгой Вам потребуется сервер Web на базе Microsoft Internet
Information Server IIS 4.0. Он поставляется в составе Microsoft Windows NT 4.0
Option Pack.
Устанавливая Option Pack, не забудьте установить расширение FrontPage
Extensions, а также средства взаимодействия с Microsoft InterDev. Последнее
потребуется для отладки серверных сценариев, расположенных в страницах ASP.
Если Вы будете устанавливать Microsoft Windows NT 4.0 Option Pack после
пакета обновления Service Pack 4.0-6.0 для Windows NT, то получите предупреж-
дающее сообщение о том, что Option Pack не тестировался с пакетом обновле-
ний данной версии. Вы можете проигнорировать это сообщение, однако по за-
вершении установки Option Pack обязательно повторите установку Service
Pack 6.0 для Windows NT.
кие его компоненты, как Microsoft InterDev и Microsoft Visual C++ (если Вы со-
бираетесь создавать собственные элементы управления ActiveX или расширения
сервера Web в виде программ CGI или ISAPI). Для разработки аплетов Java Вам
пригодится Microsoft Visual J++.
После установки Microsoft Visual Studio обязательно установите для этой
системы пакет обновлений Service Pack 3.0 или более новой версии. Без этого,
в частности, не будут транслироваться программы, подготовленные с примене-
нием стандартной библиотеки шаблонов STL.
Б процессе установки Microsoft Visual Studio Вам будет предложено устано-
вить справочную систему MSDN Library. Устаревшая версия этот системы по-
ставляется в комплекте с Microsoft Visual Studio 6.0, однако мы рекомендуем
Вам оформить подписку в Microsoft на ежеквартальное получение MSDN Lib-
rary. Если у Вас есть новая версия MSDN Library, используйте ее вместо старой
версии. Не поленитесь скопировать все необходимые Вам разделы MSDN Lib-
rary на жесткий диск. Несмотря на то, что полная установка этой грандиозной
библиотеки документации и примеров потребует свыше 1 Гб памяти, имеет
смысл все же переписать ее на жесткий диск компьютера, а не запускать с ком-
пакт-дисков. Это позволит сэкономить немало времени, потому что в процессе
разработки приложений Вы будете постоянно обращаться к различным разде-
лам MSDN Library.
Microsoft FrontPage
Мы не относим себя к тем разработчикам серверов Web, которые стремятся ре-
дактировать документы HTML исключительно примитивными текстовыми ре-
дакторами, украшая свои страницы надписями вроде «Powered by Notepad».
Когда Вам нужно быстро создать сервер Web, насыщенный большим количе-
ством сложно структурированной информации и многочисленными ссылками,
лучше применять современные мощные средства проектирования, к которым
можно отнести систему Microsoft FrontPage.
На наш взгляд, основным достоинством Microsoft FrontPage является нали-
чие программы FrontPage Explorer. Она позволяет отслеживать ссылки при пе-
ремещении каталогов и переименовании страниц и других файлов и умеет вы-
полнять комплексное тестирование серверов Web на отсутствие оборванных
ссылок. Немаловажное значение имеет и возможность импорта в редактор Front-
Page документов, подготовленных средствами самых разных программ, а также
наличие встроенного сервера Personal Web Server, удобного для проектирования
и тестирования.
Тем не менее при установке и использовании Microsoft FrontPage необходи-
мо учитывать ряд особенностей.
Если Вы уже установили на своем компьютере Microsoft Internet Information
Server IIS 4.0, то встроенный в Microsoft FrontPage сервер Web устанавливать
не надо. Настройте Microsoft FrontPage таким образом, чтобы он взаимодейство-
вал с сервером Microsoft Internet Information Server.
К сожалению, редактор Microsoft FrontPage не всегда «понимает* клиентские
и серверные сценарии, особенно если они содержат строки с тегами HTML. Для
разрешения этой проблемы клиентские сценарии JavaScript, например, можно
20 Базы данных в Интернете. Практическое руководство
вынести в отдельные файлы с расширением имени js, указав ссылки на эти фай-
лы в документе HTML. Серверные сценарии ASP можно также выносить в от-
дельные файлы и включать их при помощи оператора ^include.
Однако наилучший редактор сценариев, на наш взгляд, — это редактор, встро-
енный в Microsoft EnterDev. Он, в частности, отображает строки сценария и кон-
струкции HTML с использованием цветового выделения. Таким образом, зна-
чительно повышается читаемость сложных сценариев.
Г Л А В А 2
all
history links
navigator anchors
location frames
event images
screen selection
stylesheets
body
.
Информация о вашем браузере
Название браузера: Mic nis о ft Internet Explorer
Кодовое имя браузера Mozul»
4 Л (сошраНЫе: MSIE 5Й: Windows NT;
•
www.iHirameiia.ru; Di°£\t)
тчШа/40 (сочфапЬ*; MSIE 5Л; Windows NT;
Агент HTTP l
wu wjLurunedia.j~u; DigExt)
Тип процесс op a: x-86
Язык системы;
Язъ IE п йпьз ОБ ателя.
Втор м цифра Еер п
браузера
Информация о вашем
браузере
Название браузера Netscape
Кодовое имя браузера. Mozifla
Версия браузера. 4.51 [<?п[ (\VinNT; I)
Агент HTTP Ы(кШа.;4.51 [та] (WinNT: I)
[Россия 3
Вши жо.таын почтовый алргг:
E-Mtil:
|ivanov@MaNServBr.MoilDomain
Ваш телефон:
<option уа1ие="Швейцария">Швейцария</ор11оп>
</selectx/td></tr>
if(document.RegForm.password.value != document.RegForm.passwordcnk.value)
var sRegInfo="";
sReglnfo = "Сведения о регистрации:\п" +
"Имя: " + document.RegForm.name.value +
"\пСтрана: " + document.RegForm.country.options
[document.RegForm.country.selectedlndex].value +
" <" + document.RegForm.country.selectedlndex + ")" +
"ХпПочтовый адрес: " + document.RegForm.addr.value +
"\nE-Mail: " + document.RegForm.email.value +
"\пТелефон: " + document.RegForm.phone.value +
"\пПароль: " + document.RegForm.password.value;
alert(sReglnfo);
document.RegForm.submitC);
Базы данных в Интернете. Практическое руководство
Поиск
....
I My Cqinputei
нал дата — 3 марта 1998 года, а конечная — 29 февраля 2000 года. Для нагляд-
ности названия месяцев и календарные даты выделены в окне формы желтым
цветом.
Для того чтобы запустить поиск, достаточно щелкнуть мышью ссылку Поиск.
Если снять флажок у переключателя искать по дате регистрации:, календа-
ри в форме исчезнут, причем это произойдет мгновенно, без повторной загруз-
ки формы с сервера Web (рис. 2-6).
ВЕЛШиКЮл EdilionV! Book Inlejnel DB\Souice\.ch01 \CuslomeiSearch.h... ВВЕЗ I
Поиск
JTJ My Compute
</td>
</tr>
</table>
</form>
Полшяй исходный текст документа HTML, содержащего эту форму, мы при-
вели в листинге 2-3.
Листинг 2-3 хранится в файле ch01/CustomerSearch.html на прилагаемом к
книге компакт-диске.
Изучая этот листинг, прежде всего обратите внимание на то, что он ссылает-
ся на файл calendar.js:
<html>
<headxscript LANGUAGE="javascript" SRC="calendar.js"x/script>
<titlex/title>
</head>
<tr valign="top">
<td ID="ToMonths1"></td>
<td ID="ToDays" valign="top"x/td>
<td ID="ToMonths2"></td></tr>
</table>
</td>
</tr>
<tr bgcolor="#ffffcc">
<tdXstrongXbig><bigxbigxa href="javascript;go(); ">Поиск</а></Ыд></
bigx/big></strongx/td>
<tdx/td>
</tr>
</table>
Заполнение этих таблиц осуществляется при помощи сценария JavaScript.
Как это происходит?
В теле тега <BODY> мы определили обработчик события onload, вызывающий
функцию buildmap:
<body onload="buildmap()">
Вот исходный текст функции buildmap:
function buildmapQ
FromYear.innerText=byear.toString();
if(byear>1901) FromPrev.innerHTML='<a
href =" javascript :prevbyear();''><<<</a>';
else FromPrev.innerHTML="<<<";
Глава 2. Сценарии в страницах HTML и DHTML 33
function fillfmonthsO
{
if(byear==eyear && bmonth>=emontn) {
emonth=bmonth;
if(bday>=eday) eday=bday;
}
var month_selected=false;
var line="<table border=0 cellpadding=0 cellspacing=0>";
forfvar j=0; J<6; j++) {
if(j+1==bmonth) month_selected=true;
if(byear==eyear && j==emonth) month_selected=false;
if(month_selected) {
line=line+'<tr><td bgcolor="eFFFFCC">';
} else {
line=line+'<tr><td bgcolor="#FFFFFF">';
I
ifCj+1==bmonth) {
line=line+months[j];
} else {
line=line+'<a h ref=" javascript : selfmonth( '+j .toString()+' ); ">'
+ months[j] +'</a>';
line=line+'</tdx/tr>';
} •
1 ine=line+ ' </table> ' ;
FromMonths! . innerHTML=line;
line=line+X/tdx/tr>';
}
line=line+'</table>';
FromMonths2. innerHTML=line;
function fillfdays()
var markday=false;
line=line+'</td>';
cur_day++;
}
line=line+'<td bgcolor="SCCCCEE" width="*">
line=line+'</tr>' ;
line=line+daysOfWeek[j] ;
Hne=line+'</font> &nosp; </td>';
}
line=line+'<td bgcolor="#CCCCEE" width="*"> </td>';
line=line+'</tr>';
line=line+x/table>';
FromDays.innerHTML=line;
}
Для заполнения полей таблицы конечной даты вызываются функции ref ey,
f i l l t m o n t h s и filltdays.
Эти функции интенсивно используют свойство i n n e r T e x t различных объек-
тов таблиц календарей, заданных в тексте HTML их идентификаторами ID:
function refbyO
{
FromYear. innerText=byear. toStringO;
if(DateCheck == "true")
document.all.UseDataCheckBox.checked=true;
else
(см. след, стр.)
Базы данных в Интернете. Практическое руководство
:
document.all.UseDataCheckBox.checked=false;
oCurrentNod€!=document.getElementById("calendarTable");
sCalendarPreserve = oCurreritNode. inner-HTML;
oNewNode = document.createElement("<table>");
oCurrentNode.replaceNode(oNewNode);
oNewNode.outerHTML =
"<table id='TableReplaced'xtr align='middle1 bgcolor='#ffffcc*><tdxh1>
<a href=' javascript:go(); p >n.oHCK</a> </h1x/tdx/trx/table>";
function hidecalendar()
if(document.all.UseDataCheckBox.checked == false)
oCurrentNode=document.getElementById("calendarTable");
sCalendarPreserve = oCurrentNode.innerHTML;
oNewNode = document.createElement("<table>");
oCurrentNode.replaceNode(oNewNode);
oNewNode.outerHTHL = "<table id='TableReplaced' border='0 -
cellpadding='5' cellspacing='0'>
<tr align='middle' bgcolor='»ffffcc'XtdXh1>
<a href=']avascript;go(); '>floHCK</a> </hlx/tdX/trx/table>";
document.all.UseDataCheckBox.checked=false;
else
oCu rrentNode=document.getElementById("TableReplaced");
oPNode = document.createElement("<table>");
oCurrentNode.replaceNode(oPNode);
oPNode.outerHTML = xtable border="0" cellpadding="0" cellspacing="0"
id="calendarTable">'
+ sCalendarPreserve + '</table>';
document,all.UseDataCheckBox.checked=true;
Глава 2. Сценарии в страницах HTML и DHTML 37
"&FRCE="+Matn.random().toStringO;
Параметр с именем FRCE представляет собой случайное число, полученное от
функции Math, random и преобразованное в текстовую строку функцией toString.
Другие способы отключения кэширования страниц будут описаны в главе,
посвященной серверным сценариям ASP.
3 Зак. 357]
40 Базы данных в Интернете. Практическое руководство
Заметим, что описанные здесь средства лучше применять для создания адми-
нистративных приложений, доступ к которым осуществляется не всеми пользо-
вателями Интернета, а только сотрудниками Вашей фирмы. Это связано с тем,
что посетители могут отключить в их браузере возможность работы клиентских
сценариев.
Вывод сообщений
alert(sMessage);
,
Л Шевиадняч!
Заходите еше!
ЮЛЬ ЭОВГ5Т6
^ " ЛЯ I
I My Com
.I
Рис. 2-10. Документ HTML, предназначенный для удаления данных о пользователе
Если Вы щелкнете кнопку Удалить пользователя, клиентский сценарий за-
просит имя «жертвы» (рис. 2-11). При этом он вызовет метод prompt.
MS бвзькваты* покцпвгеявй?
[ТИП! CanEd
alert(sMessage);
Регистрация товара
,5$ My СотрЫн
Регистрация книги
Название |Бозыдвнныхи Интернет
Издательство (русскаяРедакция
От менн т ь р е ги страцию
if(re9Fields["Return"] == "OK")
window.returnValue = formFields;
event.returnValue=false;
window,close();
var wndNewWindow;
wndNewWindow=open("helloWindow.html", "HelloWindow",
"toolbar=no,menubar=no,scrollbars=no,width=300,height=100");
Параметр определяет действия с указанным адресом URL в списке истории
просмотренных страниц. Если он равен t r u e , данный адрес замещает текущий
в указанном списке, а если false — этот адрес будет добавлен в конец списка.
Б таблице 2-3 мы перечислили все возможные параметры окна.
Таблица 2-3. Параметры окна
Параметр Описание
toolbar Если параметр toolbar имеет значение yes или 1, окно снабжается стан-
дартной инструментальной линейкой. Если же значение этого параметр
равно по или 0, то инструментальная линейка отсутствует
1 ocation Определяет, будет ли отображаться поле ввода адреса документа
directories Аналогично предыдущему, но управляет отображением кнопок каталогов
браузера Netscape Navigator — What's New и What's Cool
status Отображение строки состояния
[renubar Отображение линейки меню
scrollbars Отображение полос просмотра
resizable Если этот параметр указан как yes или 1, пользователь сможет изменять
размер вновь созданного окна
width Ширина окна в пикселах
height Высота окна в пикселах
В качестве примера, иллюстрирующего применение немодальных окон, рас-
смотрим страницу HTML, предназначенную для просмотра других страниц с
заданным адресом URL. Такая страница показана на рис. 2-17.
icm Edition^! Book Ink-met DB4ch02\ShowURLhtml - Mi.
ес
Brawsertnfo.html
1Й Done
Рис. 2-17. Выбор адреса URL страницы для просмотра в немодальном окне
Здесь в поле Адрес URL Вы можете задать адрес любой страницы, располо-
женной как на локальном диске, так и в сети Интернет, Мы указали имя доку-
мента Browserlnfo.html, исходный текст которого уже рассматривался ранее в на-
шей книге.
После того как пользователь щелкнет кнопку GO!, на экране появится новое
окно, и в него будет загружена указанная Вами страница (рис. 2-18). Конечно,
загрузка выполняется только в том случае, если при вводе адреса URL Вы не до-
пустили ошибку.
50 Базы данных в Интернете. Практическое руководство
window.open{document.GoForm.URLAddress.value,
"NewWindow","toolbar=no,menubar=no,resizable=yes");
Эта функция вызывает метод window, open, передавая ему в качестве первого
параметра значение, извлеченное из поля формы с именем U R L A d d r e s s . Именно
там находится указанный пользователем адрес URL.
Второй параметр определяет имя окна, а третий задает параметры его отобра-
жения. В нашем случае мы указываем, что в окне не должно быть инструмен-
тальной линейки (параметр toolbar=no) и меню (параметр menubar=no). Кроме
Глава 2. Сценарии в страницах HTML и DHTML 51
того, пользователь получит возможность изменять размеры окна после его ото-
бражения по своему усмотрению (параметр resizable=yes).
Работа с фреймами
Использование фреймов для создания приложений Web для Интернета имеет
свои положительные и отрицательные стороны. С одной стороны, фреймы зна-
чительно облегчают процесс разработки и сопровождения крупных серверов
Web, так как ссылки на разделы сервера располагаются не на всех страницах, а
только на некоторых, специально предназначенных для этой цели. С другой
стороны, не все браузеры в состоянии корректно просматривать страницы с
фреймами.
Принимая решение о применении фреймов или об отказе от них, учитывай-
те, что такие приложения, как Интернет-магазин, содержат помимо страниц,
предназначенных для широкого круга посетителей, страницы администрирова-
ния. На них записаны процедуры администрирования работой магазина, поэтому
доступ к ним, как правило, имеет небольшое число сотрудников Вашей фирмы.
Вы можете установить на компьютерах этих сотрудников браузер Microsoft
Internet Explorer самой новой версии и забыть о проблемах несовместимости с
фреймами. В этом случае применение фреймов значительно упростит админи-
стративную часть Вашего проекта.
Не вдаваясь в детали использования фреймов (которые Вы легко найдете в
других книгах), мы приведем только необходимые сведения о них.
Файл описания фреймов
Для того чтобы объединить несколько страниц HTML при помощи фреймов,
Baxf нужно подготовить специальный документ HTML, в котором описаны та-
кие параметры фреймов, как их размер и расположение.
Особенность такого документа — отсутствие на своем обычном месте облас-
ти тела документа, выделенного тегами <BODY> и </BODY>. Вместо этого в файле
списания фреймов присутствуют теги <FRAMESET>, < / F R A M E S E T > , < N O F R A M E > и
</NOFRAME>:
<html>
<head>
</head>
<frameset гсмз="Высота_строк:и" со1з="Ширина_колонки"
onLoad="06pa6oT4HK_co6biTHfl" опип!оас!="0брабс1тчик_сабытия">
<frame src="Aflpec_URL" пате="Имя_фрейма">
</body>
</noframe>
</frameset>
</html>
52 Базы данных в Интернете. Практическое руководство
Десятично-шестнадцатеричный преобразователь
В качестве примера применения клиентских сценариев для передачи данных
между окнами различных фреймов мы рассмотрим десятично-шестнадцатерич-
ный преобразователь. Внешний вид этого преобразователя показан на рис. 2-19.
: Базы данных в Интернете. Практическое оуководство
а Десятично-тестнадцатеричный преобразователь
Десятично-шестнядцатеричный
преобразователь
Hex: J4GQ
I
parent.mainpage.document.forms[0].hexValue.value - "";
parent.mainpage.document.forms[0].decValue.value = "";
bNewNumber = false;
:
szOld = parent.mainpage.document.forms[0].decValue. value;
szNew = szOld.concat(btn.name);
nCurrent = eval(szNew);
parent.mainpage.document.forms[0].decValue.value = nCurrent;
!
Задача функции putNumber — ввод числа и его отображение в двух текстовых
полях, расположенных в верхней части калькулятора:
function putNumber(btn,form)
:
var szOld = "";
var szNew = "";
if(bNewNumber)
!
parent.mainpage.document.formsfO].hexValue.value = "";
parent.mainpage.document.forms[0].decValue.value = "";
bNewNumher = false;
i
szOld = parent.mainpage.document.forms[0].decValue.value;
szNew = sz01d.concat(btn.name);
nCurrent = eval(szNew);
parent.mainpage.document.forms[0].decValue.value = nCurrent;
:
В самом начале своей работы функция p u t N u m b e r проверяет двоичную пере-
менную newnumber. Если ее значение равно true, пользователь может ввести пер-
вую цифру нового числа. В этом случае функция p u t N u m b e r обнуляет содержи-
мое текстовых полей h e x V a l u e и d e c V a l u e , а также устанавливает значение
n e w n u m b e r , равное false.
Далее функция проставляет введенную пользователем цифру перед перемен-
ной szOld, равной текущему значению из поля d e c V a l u e . При этом она вызыва-
ет из класса String метод concat, предназначенный для слияния (конкатенации)
строк.
На следующем этапе текущее значение вычисляется функцией eval. Эта фун-
кция пытается интерпретировать текстовую строку, переданную ей в качестве
параметра, как арифметическое выражение, возвращая результат интерпретации
в виде численного значения. Результат сохраняется в переменной n C u r r e n t и
отображается в текстовом поле decValue.
Функция getResult, выполняющая десятично-шестнадцатеричное преобразо-
вание, получает исходное число из поля decValue и передает его функции dec2hex.
Результат преобразования записывается затем в поле h e x V a l u e :
f u n c t i o n getResult(form)
;
parent.mainpage.document.forms[0].hexValue.value =
Глава 2. Сценарии в страницах HTML и DHTML 57
var szHexTable="0123456789ABCDEF";
var szResult = "";
var szBuf="";
var nRem = 0;
var bNegative=false;
iffnDec < 0)
{
bNegative=true;
nDec = -nDec;
}
nTmp=nDec;
while(true)
{
nRem = nTmp % 16;
nTrnp = nTmp /16;
szBuf=szHexTable. charAt(nRem);
szResult = szBuf.concat(szResult);
nTmp = Math.floor(nTmp);
szBuf=szHexTable.charAt(nflem);
szResult = szBuf .concat(szResult);
ifCMath.floor(nTmp) != 0)
{
sz8uf=szHexTable. charAt(Math.fl oo r( nTmp));
szResult = szBuf .concat(szResult);
ifCbNegative == true)
return ("-" + szResult);
else
return szResult;
!
В начале своей работы функция dec2hex проверяет знак исходного числа.
Отрицательные числа преобразуются в положительные. При этом в переменную
bNegative записывается значение t r u e .
Базы данных в Интернете. Практическое руководство
Добро пожаловать!
Вы приобрели первый выпуск автор СЕОГО компакт-
диска тешичесгзс: писателей Александра Фролова
и Григория Фролова.
i 5000 страниц
различной информации, Б том числе:
Д| My Ccinputa
появится наш логотип. Если же щелкнете кнопку Книги или Статьи, заголовок
будет выглядеть так, как это показано на рис. 2-21 и 2-22 соответственно.
ий диск Александра Фролова и Григория Фролова MICH.soil Inlemel Hill El I
Наши книги
Рис. 2-21. В результате щелчка кнопки Книги изменилось содержимое правого и верхнего фрейма
'3 Авторский диск Александра Фролова к Грш ория Фролова Miciotolt Inleinet... HI \
SJ
ИЮТН="Ширина" НЕШТ="Высота">
Здесь мы указали только три параметра. Полный список параметров тега
<IMG> с кратким их описанием Вы найдете в таблице 2-6.
Таблица 2-6. Параметры тега <IMG>
Параметр Описание
SRC Адрес URL файла с растровым графическим изображением
NAME Имя объекта, соответствующего растровому графическому изображе-
нию. Это имя может быть использовано для ссылки на объект в кли-
ентском сценарии
ALT Текстовая строка, которая отображается в тех случаях, когда браузер
не может показывать графические изображения или когда такая воз-
можность отключена
ALIGN Выравнивание текста относительно графического изображения:
LEFT — 1ЕО левой границе;
RIGHT — по правой границе;
ТОР — по верхней границе;
MIDDLE — по центру изображения;
ВОТТОН — по нижней границе;
ТЕХТТОР — выравнивание по верхней границе относительно самых вы-
соких символов в текстовой строке;
ABSMIDDLE — выравнивание середины текстовой строки относительно
середины изображения;
BASELINE — выравнивание нижней рамки изображения относительно
базовой линии текстовой строки:
ABSBOTTOM — выравнивание нижней границы изображения относитель-
но нижней границы текущей строки
62 Базы данных а Интернете. Практическое р/ководстео
в] «КЛУЁЙВШ : Ej My Computer
..
iejDone ^ My Cf«puSa
showNextlmageO;
// ->
</SCRIPT>
</body>
Она предназначена для отображения очередного кадра анимационной после-
довательности в окне бразуера. Вот исходный текст функции showNext Image;
function showNextlmageO
if(bForward)
bForward=false;
else
<:
I
bForward=true;
}
!
document. Img.src= "imgO" + i + ".gif";
setTimeout( "showNextlmageO", 100);
}
В области заголовка документа HTML находится определение функции
showNextlmage и двух глобальных переменных;
var 1=1;
var bForward=true;
Переменная i хранит номер текущего кадра, отображаемого в окне браузера.
Этот номер вначале увеличивается функцией s h o w N e x t l m a g e от 1 до 16, а затем
снова уменьшается до 1. Изменение номера происходит на 1 (в ту или иную
сторону) при каждом вызове функции showNextlmage.
В переменной bForward хранится направление изменения номера кадра. Зна-
чение true соответствует прямому направлению, а значение false — обратному.
Когда функция showNextlmage получает управление, она анализирует содер-
жимое переменной b F o r w a r d . Если в этой переменной находится значение t r u e ,
функция s h o w N e x t l m a g e увеличивает значение переменной 1, а затем сравнива-
ет результат с числом 17. Когда отображение всех кадров в прямой последова-
тельности завершено, в переменную bForward записывается false, после чего при
следующем вызове функции showNextlmage номер текущего кадра не увеличит-
ся, а уменьшится.
Для отображения очередного кадра функция s h o w N e x t l m a g e изменяет значе-
ние свойства src изображения d o c u m e n t . Img, как это показано ниже:
document. Img.src= "imgO" + i + ".gif";
Базы данных в Интернете. Практическое руководство
var i;
for(i=0; KnNumberQf Images;
var i;
for(i=0; KnNumberOf Images;
function showNextlmageC )
if(bForward)
\
i
bForward=false;
,
bForward=true;
}
document. Img. src = imgArray[i-1]. src;
setTimeout ( "showNextlmageC ) " , 1 00 ) ;
}
Она идентична описанной в предыдущем примере, за исключением того, что
для установки свойства src изображения Img используются элементы заранее
подготовленного массива imgArray:
cocument. Img. src = imgArray[i-1]. src;
</table>
Глава 2. Сценарии в страницах HTML и DHTML
Создание Cookie
Существуют два способа создания Cookie, первый из которых используется рас-
ширениями сервера Web, а второй — клиентскими сценариями. Мы рассмотрим
их оба.
Создание Cookie расширением сервера Web
Для того чтобы создать Cookie первым способом, расширение сервера Web обыч-
но добавляет в заголовок HTTP динамически создаваемого документа HTML
поле с именем Set-Cookie. В нем определяются имена и значения параметров
Cookie.
Когда расширение сервера Web вызывается из документа HTML, имеющего
Cookie, параметры Cookie предаются этому расширению через поле Cookie за-
головка HTTP и могут быть проанализированы.
Заголовок HTTP, предназначенный для создания Cookie, выглядит следую-
щим образом:
Set-Cookie: Имя=3начение; expires=flara_GMT;
[>ath=Aflpec_URL; с!ота1П=Домен; secure
Описание отдельных полей заголовка Set-Cookie приведено в таблице 2-7.
Таблица 2-7. Поля заголовка Set-Cookie
Ноле Описание
Имя Произвольное имя параметра, определенного и Cookie. Здесь Вы
можете использовать любую строку, лишь бы в ней не было пробе-
лов, занятых и двоеточий, В том случае, когда имя содержит перс-
численные выше символы, используйте кодировку URL
Значение Текстовая строка значений параметров. В этой строке не должно
быть пробелов, запятых и двоеточий, поэтому надо использовать
для нее кодировку URL
expires=flaTa_GMT Дата автоматического удаления Cookie по Гринвичу. Если эта дата
не указана, а параметр expires отсутствует, Cookie будет удален
сразу после того, как браузер закончит сеанс связи с сервером Web
йота!л=Домен Доменная часть адреса URL, для которой действует данный Cookie.
Если этот параметр не указан, то по умолчанию используется до-
менный адрес URL документа HTML, где был установлен Cookie
path=Afipec_URL Часть адреса URL, задающая путь к документу HTML, для которо-
го действует данный Cookie. Если этот параметр не указан, то по
умолчанию используется адрес URL документа HTML, где был
установлен Cookie
secure Если указано это поле, данные Cookie необходимо предавать толь-
ко с использованием защищенного протокола SSL. Такой протокол
применяется серверами HTTPS
Все поля, кроме первых двух ( И м я и З н а ч е н и е ) , не обязательны.
Дата должна быть записана в формате День_недели, ДД-Мес-ГГ ЧЧ: MM: CC GMT:
4 День_недели — английское трехбуквенное сокращение названия дня недели
(например, Моп);
f ДД — номер дня недели;
4 Зак.3571
72 Базы данных в Интернете. Практическое руководство
dtExpires.setTime(dtExpires.getTime() +
dtDaysExpires * 24 * 60 * 60 * 1000);
document.cookie =
szName + "=" + szValue + "; expires=" + dtExpiryDate;
if (szCookieString. substring(
nStartPosition, nEndPosition) == szName)
nStartPosition = nEndPosition +• 1;
nEndPosition =
document. cookie, indexOf ("; ", nStartPosition);
return "";
:
После извлечения строки из свойства document, cookie и записи этой строки
к переменную szCookieString ф у н к ц и я findCookie организует циклический про-
смотр всех символов этой строки. Условием завершения цикла является про-
смотр всех символов s z C o o k i e S t r i n g . l e n g t h .
Сравнивая имя параметра с подстрокой, извлеченной из строки szCookie-
String при помощи метода s u b s t r i n g , функция f i n d C o o k i e пытается найти нуж-
ный параметр. Если попытка оказывается успешной, функция f i n d C o o k i e про-
пускает символ присваивания, извлекая значение параметра, ограниченное точ-
кой с занятой. Это значение возвращается функцией f i n d C o o k i e .
Глава 2. Сценарии в страницах HTML и DHTML 75
}
else
{
// cookie с именем Visit не установлен
,й
JJ
в * * E . U R u s s i a n EdiMonMnteri
Добро пожаловать!
Добро пожаловать!
Рис. 2-28. Такой документ HTML создается динамически при первом посещении
Глава 2. Сценарии в страницах HTML и DHTML ,,
Рис. 2-29. Такой документ HTML создается динамически при втором посещении
Теперь здесь виден новый заголовок, а также содержимое параметров Cookie
с именами Visit, Count, info, MidiUsed и JavaUsed.
При каждом новом посещении значение параметра Count будет увеличиваться
на единицу. Если же в документе, показанном на рис. 2-27, Вы нажмете кнопку
Удалить cookie, подсчет посещений начнется заново.
Исходный текст документа HTML показан в листинге 2-21.
Листинг 2-21 Вы найдете в файле ch02/Again.html на прилагаемом к книге
компакт-диске.
Функции addCookie, f indCookie и removeCookie, определенные в этом докумен-
те. Вам уже знакомы. Они предназначены для создания Cookie, извлечения зна-
чения заданного параметра Cookie и удаления Cookie соответственно.
Функция b t n C l i c k вызывается, когда пользователь нажимает в форме кноп-
ку GO!:
f u n c t i o n btnClick()
if(findCookie("Visit") == "")
addCookieC'Visit","Alexandr_Frolov",10);
addCookie("Count","0",10);
document.write("<Н2>Добро пожаловать!</Н2>");
else
{
var szCnt = findCookie("Count");
var i=0;
ifCszCnt != "")
!
i = szCnt;
(см. след, стр.)
78 Базы данных в Интернете. Практическое руководство
i++;
szCnt = l.toStringO;
addCookie{"Count",szCnt,10);
>
document.мгНе("<Н2>Рады видеть Вас СНОВА! </Н2>"};
document.write(document.cookie);
}
Прежде всего, эта функция ищет параметр Cookie с именем Visit. Если та-
кой параметр не найден, считается, что страница посещается в первый раз. В
этом случае функция btnClick создает параметры Cookie с именами Visit и Count,
а затем формирует текст документа HTML с приглашением:
addCookieC"Visit","Alexandr_Frolov", 10);
addCookie("Count","0",10);
document.write("<H2>You are welcome!</H2>");
Когда пользователь посещает страницу повторно, параметр Cookie с именем
Visit уже существует. В этом случае функция btnClick пытается найти параметр
с именем C o u n t и получить его значение:
var szCnt = findCookie("Count");
Это значение затем увеличивается на единицу и записывается обратно в па-
раметр Cookie с именем C o u n t :
i = szCnt;
i++;
szCnt = i.toStringO;
addCookie("Count",szCnt, 10);
Завершая свою работу, функция b t n C l i c k записывает приглашение для по-
вторно посетивших страницу пользователей и отображает содержимое свойства
document.cookie:
document.write("<H2>You are welcome AGAIN!</H2>");
document.write(document.cookie);
Обработчик события o n C l i c k кнопки Удалить cookie вызывает функцию
removeCookie для параметров Cookie с именами Count и Visit, удаляя их:
<INPUT TYPE="button" VALUE="Remove All Cookies"
onClick="removeCookief'Count');removeCookie('Visit')">
Cookie Notepad
помощью cookie вы
но зге те хранить у
пользователя ела
локальные настройки
if(szMyText != "")
Count
.
~~local""/E: \! Russian Edition\InternetDB\Src\ch02\
0
24243712
29310153
1499803264
29308141
MyText
S!u0421S;20*u043FiSuCl43E!(u043CXu043EXu0449Xu044C!(u044E!i20cookieS;20Xu0432Ku044BS;20
S!u043CS:u043ES;u0436Xu0435!Su0442ii;u0435J;20Xu0445Xu0440Xu0430Xu043DXu0438Ku0442S;u044C
K20S;u0443ii;20Xu043F:?!u043EJ;u043Bieu044CJ;u0437S!u043ES!u0432Xu0430S!u0442ii;u04353!u043B
Seu0435^20Xu043DXu0430S!u044nu0442Xu0440S!u043EXu0439Xu043AXu0436
"~local"/E: \! Russian Edition\InternetDB\Src\ch02\
Глава 2. Сценарии в страницах HTML и DHTML 81
204374528
29310157
1686804080
29308145
> - Li** **
I
Посетите вашу персональную
страницу
Переход we страниц.
Цвет фона:
& Белый
<~ Желтый
f~ Зеленый
<"* Малнновыа
}
Добро пожаловать!
Бы можете настроить "цвет фона этой страницы при
помощи переключателе?:, расположенных на главной
странице.
у Щ My CompMer
if(findCookie("Count") == "")
{
addCookie("Count", "0",10);
addCookie("bgColor",szColor,10);
else
if(szCnt != "")
i = szCnt;
i++;
szCnt = i. toStringO;
ookres
J Allow cookies that are stored on you computei
О Disable
0 Enable
О Piompl
) Allow рег-session cookies |not stored]
О Disable
0 Enable
О Prompt
ф-j Downloads
.1 j File download
О Disable
© Enable
• ur
I
Основы ASP
Изучать технологию ASP проще всего на конкретных примерах. Придержива-
ясь методики известных классиков программирования на языке С, мы прежде
всего подготовим простейшую с т р а н и ц у ASR создающую документ HTML с
заголовком «Hello, ASP World!». Заголовок формируется средствами серверно-
го сценария JScript. Затем мы приведем еще несколько примеров, демонстриру-
ющих некоторые возможности технологии ASP.
Простейший пример
Итак, убедитесь, что па Вашем компьютере устанонлен сервер Internet Infor-
mation Server версии 4.0 или более новой. Затем создайте каталог с именем, на-
пример, BookStore. Средствами управляющей консоли Microsoft Management
Console создайте виртуальный каталог, отображенный на каталог BookStore, и
разрешите чтение, а также исполнение сценариев. Для этого в панели BookStore
Properties (рис. 3-1) откройте вкладку Virtual Directory и пометьте переключа-
тели Read и Script (если они были сброшены).
BoofcS(orv Properties
tooalPate p Mr-etPub\wwwpoot\Book
; SlattJngPwt (DelaultWebStei/BoohS
H*
Пример с циклом
Теперь мы рассмотрим более сложный пример страницы ASP, содержащий сер-
верный сценарий генерации таблицы квадратов целых чисел от 0 до 5. При ра-
боте с базой данных Вам часто придется отображать отчеты в табличном виде
на основе данных, полученных в результате выполнения запроса. При этом для
формирования таблицы Вы сможете воспользоваться приемами, описанными в
этом разделе.
Исходный текст страницы ASP, создающей таблицу, приводен в листинге 3-2.
Листинг 3-2 Вы найдете в файле ch03/ForASP.asp на прилагаемом к книге
компакт-диске.
Первая строка файла страницы ForASP.asp содержит оператор © L A N G U A G E ,
определяющий язык серверного сценария:
<Х@ LANGUAGE = "JScript" %>
Заголовок таблицы оформлен как обычно при помощи стандартных тегов
языка HTML. Что же касается строк со значениями, то они формируются сер-
верным сценарием в цикле:
<TABLE BQRDER="1">
<ТИ><ТН>Значение Х</ТН><ТН>Значение X*X</TH></TR>
а
var i;
for(i=0; i<=5; i++)
:
%>
<TRXTD><X=iX></TD><TDXX=i * 1XX/TDX/TR>
</TABLE>
Счетчиком ц и к л а служит переменная i, принимающая значения от 0 до 5.
При помощи конструкции <%=i%> мы вставляем в ячейки каждой строки теку-
щее значение переменной i, а с помощью конструкции <%=i * i%> — значение
квадрата этой переменной.
Ниже мы приводим исходный текст документа HTML, создаваемого при
помощи страницы ForASP.asp. Как видите, получившийся документ HTML не
содержит никаких строк сценариев:
<HTML>
<BODY>
<Н2>Табпица квадратов целых чисел от 0 до 5</Н2>
<TABLE BORDER="1">
<ТЯ><ТН>Значение Х</ТНХТН>Значение X*X</TH></TR>
<TRXTD>0</TDXTD>0</TD></TR>
<TRXTD>1</TDXTD>1</TDX/TR>
<TRXTD>2</TDXTD>4</TDX/TR>
<TRXTD>3</TDXTD>9</TDX/TR>
<TRXTD>4</TDXTD>16</TDX/TR>
<TRXTD>5</TO><TD>25</TDX/TR>
</TABLE>
(см. след, стр.)
</BODY>
</HTML>
Внешний вид получившейся таблицы после загрузки документа в окно бра-
узера показан на рис. 3-3.
и] Dorm
Обработка формы
Наше следующее приложение Web запрашивает у пользователя регистрацион-
ную информацию, а затем показывает ее пользователю в окне браузера. Цель
данного примера — показать, как с помощью серверных сценариев в страницах
ASP можно получить доступ к данным, введенным при помощи форм.
На рис. 3-4 изображена форма запроса идентификатора и пароля пользова-
теля. Она предназначена для подключения к нашему приложению Web. Пред-
полагается, что пользователь введет данные регистрации и щелкнет кнопку
Подключиться. Кнопка Отменить предназначена для удаления содержимого из
полей формы.
5 bNi. //riolov235/B<iokSlor)iSLoginFaim.hlml Microsoft I" -
Регистрация пользователя
Идентификатор; jfrolov
Пароль: р* —""-I
Г?
Данные регистрации
Идентификатор frolov
<HEAD>
<%
var sLogin=flequest.Form("Login");
war sPassword=Request.Form("Password");
!!>
</HEAD>
Мето/i. Form объекта R e q u e s t позволяет извлечь содержимое полей формы,
переданные методом POST. Наш сценарий сохраняет извлеченные таким образом
значения идентификатора пользователя и его пароль и затем записывает их в
92 Базы данных в Интернете. Практическое эуководство
<TRXTD>naponb</TDXTDxbxX=sPasswordJEx/bx/TDX/TR>
</TABLE>
<:/script>
Базы данных в Интернете. Практическое руководство
Данные регистрации
Идентификатор Frolov
Пароль My Password
Файл global.asa
Как мы только что сказали, файл global.asa имеет отношение к приложению ASP
в целом и создается для каждого такого приложения. Он должен находится в
корне виртуального каталога приложения ASP или в корне виртуального сер-
вера Web.
В этом файле определены обработчики следующих событий:
f запуска приложения ASP;
f останова приложения ASP;
ф начала сеанса пользователя, загрузившего в свой браузер главную страницу
приложения ASP;
4 завершения сеанса пользователя с приложением ASP.
Для каждого такого события Вы можете назначить индивидуальные обработ-
чики в виде серверных сценариев. Эти сценарии выполняют такие функции, как
инициализация глобальных переменных приложения и так называемых пере-
менных сеанса, создаваемых в рамках сеанса пользователей, выполнение опера-
ций с Cookie, подключение к базам данных и т. д. Словом, сценарии файла
global.asa предназначены для выполнения действий, которые затрагивают при-
ложение в целом.
Система разработки приложений Microsoft InterDev создает пустой шаблон
файла global.asa автоматически. Он имеет следующий вид:
<SCRIPT LANGUAGE=VBScript RUNAT=Server>
1
Обработчик события Session_OnStart
Sub Session_OnStart
' Тело обработчика события Session_OnStart
End Sub
</SCRIPT> *
Обращаем Ваше внимание, что в теге <SCRIPT> указан параметр RUNAT=Server.
Он свидетельствует, что описанный :тш тегом сценарий должен выполняться
к е браузером, а сервером, то есть это серверный сценарий.
Список возможных событий, обрабатываемых в файле global.asa, перечислен
в таблице 3-1.
Отслеживая события в файле global.asa, приложение ASP определяет, когда
к нему подключился очередной пользователь и когда он отключился. При этом
автоматически учитываются такие нюансы, как просмотр пользователем в окне
его браузера других серверов Web и последующий возврат пользователя на стра-
ницу Вашего сервера. Кроме того, учитывается, что при передаче данных по
96 Базы данных в Интернете. Практическое руководство
как пользователи имеют свои собственные переменные сеанса, они вправе хра-
нить в них собственные данные, доступные из всех страниц ASP приложения.
Заметим, что переменные сеанса и объект Session, предназначенный для ра-
боты с ними, доступны только в том случае, если пользователь разрешил в па-
раметрах браузера действие Cookie (приложение ASP также может запретить
применение Cookie).
Это связано с тем, что механизм сеансов основан на хранении в Cookie гло-
бальных уникальных идентификаторов сеанса. Поэтому когда клиент отключа-
ет их, все переменные сеанса сохраняют свои значения только в пределах одной
страницы ASP. В этом случае Вы не сможете их применить, например, для пе-
редачи информации из одной страницы в другую.
В качестве альтернативы передачи параметров между страницами ASP допу-
стимо использовать другие способы, не зависящие от Cookie и сеансов. Напри-
мер, передачу параметров в строке ссылки на страницу ASP или в скрытых по-
лях форм.
= ,
Мастер платежей
Мастер платежей поможет вам выбрать способ, которым
вы будете расплачиваться за приобретенный товар
Следуйте указаниям, появляющимся на экране
Выберите валюту
Бы можете оплатить покупку в рублях, в долларах или Е
немецких маркая
Выберите подходящую валюту н щелкните кнопку Вперед.
Для отмены покупки щелкните кнопку Отменить
<~ рубли
- 1* ; доллары
Г немецкие марки
•—1
»|
«Назад 0-цеш-пъ
•i
«Назад
Подтверждение платежа
Поздарвляем с покупкой!
Отменить
:
IffsCurrency == "rub")
sChkfiub="checked";
else if(sCurrency == "dollar")
sChkDollar="checked";
else iffsCurrency == "dm")
sChkDM="checked";
else
sChkRub="checked";
*>
<form id="form1" name="form1" action="Master2.asp" fflethod="POST">
<table border="0" cellPadding="0" cellSpacing="0" width="157">
<trxtd width="27">
<input id="radio1" name="Currency" type="radio" value="rub"
<X=sChkRubX>xbr>
<input id="radio2" narne="Currency" type="radio" value="dollar"
<l=sChkDollarJ(>xbr>
<input id="radio3" name="Currency" type="radio" value="dm"
<3>=sChkDM*>x/tdxtd widths 18 ">рубли<Ьг>доллары<Ьг>немецкие марки
</td></tr>
</table>
<table border="0" width="300" cellpadding="2"><trxtd width="100">
<input type="submit" value="Bnepefl »" name="B2"x/tdx/tr>
</table>
</form>
Такой подход позволил нам определять начальное положение переключате-
ля с зависимой фиксацией из серверного сценария, расположенного в странице
ASP. Причем, что важно, мы делаем это без использования клиентских сценари-
ев, применение которых черевато возникновением потенциальных проблем со-
вместимости с браузерами различных типов.
Теперь мы расскажем о том, как реализованы переходы между страницами
нашего мастера платежей.
Кнопку Вперед надо поместить в ту же форму, что и переключатели выбора
валюты, Такая необходимость вызвана тем, что, щелкнув ее, Вы передадите уп-
равление следующей странице мастера. Эта страница должна извлечь состояние
переключателя выбора валюты, передаваемое вместе с содержимым формы.
Кнопки Назад и Отмена также следует расположить в отдельных формах, так
как они предназначены для загрузки разных страниц. Предусмотрев для этих
кнопок разные формы, мы можем указать в соответствующих параметрах action
тега <FORM> различные адреса URL. В первой странице мастера в результате
щелчка кнопок Назад и Отмена загружается одна и та же страница Master0.html,
однако во второй странице Master2.asp это уже будет не так.
102 Базы данных в Интернете. Практическое эуководство
sCurrency=Request.Form("Currency")(1);
Session("sessCurrency")=sCurrency;
}
else
sCurrency=Session("sessCurrency");
var sCurrencyName="";
%>
Однако файл Master2.asp может быть загружен не только в результате щел-
чка кнопки Вперед, расположенной на предыдущей страницы мастера, но и в
результате щелчка кнопки Назад, расположенной на следующей странице Mas-
terS.asp. В последнем случае загрузка файла Master2.asp будет выполняться из
другой формы, не содержащей переключателя с именем C u r r e n c y .
Поэтому мы вначале определяем, есть ли элементы в наборе Form("Cur-
rency"), применяя следующую конструкцию:
if(Request.Form("Currency").Count != 0)
sCurrencyName = "рубли";
else if(sCurrency == "dollar")
sCurrencyName = "доллары";
else if(sCurrency == "dm")
sCurrencyName = "немецкие марки";
K>
Третья страница мастера Master2.asp содержит список типов кредитных кар-
точек. Покупатель должен выбрать из пего ту, которую он будет использовать
для оплаты. Инициализация данного списка должна выполняться с учетом того,
что пользователь может проходить страницы мастера как в прямом, так и в об-
ратном направлении.
Когда покупатель «листает» страницы в прямом направлении и впервые по-
падает на страницу Master2.asp, переменная сеанса sessWirepard с кодом кредит-
ной карточки еще не определена. Б этом случае наш сценарий выбирает по умол-
чанию кредитную карточку VISA, записывая в переменную swChkVisa строку
«selected». Эта строка вместе со строками s w C n k M a s t e r и swChkAEx применяется
(при помощи конструкции типа <^=swChkVisa5E>) при формировании списка ти-
пов кредитных карточек:
<%
var sW=Session("sessWirecard")
var swCnkVisa="";
var swChkMaster="";
var swChkAEx="";
iffsW == "VISA")
swChkVisa="selected";
else if(sW == "Mastercard")
swCnkMaster="selected";
else if(sW == "American_Express")
swChkAEx="selected";
else
swChkVisa="selected";
%>
Только и одной из переменных swChkVisa, s w C h k M a s t e r и swChkAEx может со-
держаться строка «selected», остальные будут пустыми. В результате единствен-
ный тип кредитной карточки выбирается по умолчанию в списке <SELECT> с
именем PayMethod.
Заметим, что указанный список находится в одной форме с кнопкой Вперед.
<form Id="form1" name="form1" action="Master3.asp" method="post"Xp>
<select name="PayMethod" slze="1">
<option <K=swChkVlsaS;> value="VISA">VISA</option>
<option <X=swChkMasterK> value="Mastercard">Mastercard</option>
<option <X=swChkAExX> value="American_Express">American Express</option>
</selectx/p>
<table border="0" width="300" cellpadding="2">
<tr><td width="100">
<input type="submit" value="BnepeA »" name="B2"x/tdx/tr>
</table>
</form>
5Чак. 357I
104 Базы данных в Интернете. Практическое руководство
else
sWirecard=Session("sessWirecard");
%>
Напомним, что эта переменная сеанса используется предыдущей страницей
мастера для инициализации списка типов кредитных карточек.
Если М1>! попадаем на последнюю страницу мастера с предыдущей страницы,
наш сценарий запрашивает выбранный тин кредитной карточки и сохраняет его
в переменной сеанса s e s s W i r e c a r d :
sWirecard=Request.Form("PayMethod")(1);
Session("sessWirecard")=sWirecard;
В противном случае этот код считывается в переменную s W i r e c a r d при по-
мощи объекта Session из существующей переменной сеанса:
sWirecard=Session("sessWirecard");
При формировании текстового сообщения о платеже сценарий использует
локальные переменные s C u r r e n c y N a m e и s W i r e c a r d N a m e . Они заполняются с при ь
менением операторов case:
Глава 3. Применение технологии ASP 105
var sCurrencyName="";
switch( Session ("sessCu r rency"))
{
case "rub"; sCurrencyName=" Рубли"; break;
case "dollar": зСиггепсуМате="Доллары"; break;
case "dm": зСиггепсу№те="Немецкие марки"; break;
default: sCurrencyName="OUJMBKA. Сообщите администратору сервера"; break;
}
var sWirecardName="";
switch( Session ("sessWirecard"))
{
case "VISA": $WirecardName="VISA"; break;
case "Mastercard": sWirecardName="Mastercard"; break;
case "American_Express": sWirecardName="American Express"; break;
default : sWireca r d Name =" ОШИБКА. Сообщите администратору сервера" ; break;
*>
Обратим Ваше внимание на строку «default» этого оператора. Если в рабо-
те нашего приложения Web возникла ошибка (например, неверны ссылки на
страницу), вместо названия валюты и кредитной карточки мы выводим соответ-
ствующее сообщение. Вы можете, например, добавить в него ссылку на почто-
вый адрес администратора сети или что-нибудь еще.
На последней странице нашего демонстрационного мастера нет кнопки Впе-
ред или какой-либо ссылки на другие страницы, однако в реальных приложе-
ниях стоит дополнить мастер страницами, запрашивающими такую информа-
цию, как номер кредитной карточки и срок се действия.
Подсчет количества активных сеансов
В этом примере, представляющем собой простейший шаблон приложения ASP,
мы демонстрируем использование переменных приложения для подсчета коли-
чества активных сеансов.
Session counter
Количество активных сеансов: 4
Application("sess_counter") = Application("sess_counter") + 1;
Application("sess_counter") = Application("sess_counter") - 1;
№
Go Urks
Цвет фона:
Р Белый
Г Желтый
Г Зеленый
С Малиновый
I
Добро пожаловать!
Бы были здесь 0 раз
Цвет фона страницы: White
Всего активных посетителей: 4
I
"!i, Lota1, intranet
!
Добро пожаловать СНОВА!
Вы были здесь 1 раз
Цвет фона страницы. Yellow
Всего аетквнкх посетителей: 4
Application("sess_counter") = 0;
V
Application("sess_counter") = Application("sess_counter") + 1;
function Session_OnEnd{)
Application("sess_counter") = Application("sess_counter") - 1;
.
Исходный текст главной страницы приложения с переключателем выбора
цвета фона страницы и двумя кнопками показан в листинге 3-14.
Листинг 3-14 Вы найдете в файле ch03/DemoCookie/default.asp на прилагае-
мом к книге компакт-диске.
Помимо всего прочего, в ней расположена форма, позволяющая выбрать па-
раметры персональной страницы:
<FORM NAME="TestForm" ACTION="main.asp?RESET=0" METHOD="POST">
<P>
<INPUT TYPE="submit" УА1_1)Е="Переход на страницу">
<P><HR>
<Р>Настройка параметров персональной страницы
<Р><В>Цвет фона:</В>
<PXINPUT TYPE="radio" NAME="Color" CHECKED VALUE="White"> Белый
<BR><INPUT TYPE="radio" NAME="Color" VALUE="Yellow"> Желтый
<BR><INPUT TYPE="radio" NAME="Color" VALUE="Lime"> Зеленый
<BR><INPUT TYPE="radio" NAME="Color" VALUE="Fuchsia"> Малиновый
</FORM>
Изучая листинг 3-14, нетрудно заметить, что кнопки Переход на страницу
и Параметры по умолчанию расположены в разных формах.
Щелчок кнопки Переход на страницу приводит к вызову страницы main.asp
с параметром RESET, равным 0:
<FORM NAME="TestForm" ACTION="main.asp?RESET=0" METHOD="POST">
110 Базы данных в Интернете. Практическое руководство
Response.Cookies("bgColor"}="White";
Response.Cookies("Count")="0";
nCount=0;
'
else
var sColor=Request.Form("Color")(1);
Response.Cookies("bgColor")=sColoг;
sCount=Request.Cookies("Count");
sbgr=Request,Cookies("bgColo r");
nCount=sCount;
nCount++;
sCount=nCount.toString();
Response.Cookies("Count")=sCount;
Регистрация пользователей
При создании Интернет-магазина или другого коммерческого приложения для
Интернета Вам придется решать проблему аутентификации пользователей: как
правило, для того чтобы работать со страницами Вашего приложения, пользо-
вателям придется ввести свой идентификатор и пароль.
В этом разделе мы приведем исходные тексты фрагмента приложения, реша-
ющего задачу аутентификации пользователей. По результатам аутентификации
им либо разрешается, либо запрещается работать с приложением.
Для ввода идентификатора (имени) и пароля мы используем панель, пока-
занную на рис. 3-15.
Базы данных в Интернете. Практическое эуководство
Добро пожаловать!
Емя jad-nJr
гт
Пароль I1^,
Добро пожаловать!
Вы успешно подключились к нашему приложению
Доступ запрещен
^Go
Любнкыйцьет Зеленый j
Лю Бимый р азмер TJ
Волшебное слово [Пожалуйста
il intranet
Д htlpi/j'satum/eookStore/hiddenZ.asu - Micioso*t I.
[Иван Сидоров]
.
Итог
Serve.,, -
Ty[K ^
DBSIgn-Tt.,,
Long
ActiveX C..
General '
help
Основы ADO
Напомним, что в первой главе книги уже упоминалось, что для доступа к базам
данных SQL Server можно использовать различные методы — программный
интерфейс DB Library, программный интерфейс ODBC, объектный интерфейс
RDO, объектный интерфейс OLE DB и, наконец, объектный интерфейс ADO.
ADO представляет собой интерфейс уровня приложений, созданный поверх
объектного интерфейса OLE DB. При этом интерфейс OLE DB обеспечивает
универсальный доступ к данным. Такой доступ обеспечивается в свою очередь
с помощью провайдеров, таких, как Microsoft OLE DB Provider для ODBC
(MSDASQL) или Microsoft OLE DB Provider для SQL Server (SQLOLEDB).
Программная модель ADO
Ключевыми элементами программной модели ADO является набор объектов, с
помощью которых осуществляется соединение с базами данных, выполнение
команд с параметрами, получение результата выполнения этих команд в виде
переменных или наборов записей, обработка событий и ошибок.
Рассмотрим порядок обращения приложения к базе данных с применением
программной модели ADO.
Глава 4. Связь приложений с базами данных через ADO 121
Установка соединения
Прежде чем обращаться к базе данных, приложение должно установить соеди-
нение с сервером базы данных. Эта операция выполняется с помощью объекта
Connection.
Данный объект позволяет установить соединение с источником данных по-
средством интерфейса ODBC или напрямую. В первом случае Вам надо указать
имя источника данных Data Source Name (DSN), а во втором — информацию об
источнике данных: имя драйвера, имя сервера, пароль и т, д. В примерах мы
будем использовать подключение к источнику данных с применением DSN.
После завершения работы с соединением его необходимо закрыть, вызвав
метод Close объекта Connection.
Подготовка команды и параметров
После установки соединения приложение должно подготовить объект Command,
записав в его свойства команды, необходимые для доступа к данным. Это могут
быть команды выполнения строк языка Transact-SQL (например, строки
«select * f r o m clients»), команда вызова хранимой процедуры SQL Server no
ее имени или имя таблицы.
При помощи объекта P a r a m e t e r приложение может передать вместе с коман-
дой параметры. Входные параметры позволяют передавать информацию в хра-
нимые процедуры SQL Server, а выходные — принимать информацию из храни-
мой процедуры.
Выполнение команды
Один из методов объекта Command с именем Execute предназначен для иниции-
рования выполнения команды.
В зависимости от выполняемой команды он может возвращать результат в
виде набора записей Recordset или через выходные параметры хранимой про-
цедуры (если команда запускает такую процедуру).
Обработка результатов выполнения команды
Результатом выполнения команды может быть набор записей, представляемых
объектом Recordset.
Например, в результате выполнения команды SQL «select * from clients*
создается набор записей Recordset, представляющих собой массив строк табли-
цы clients. Приложение способно просмотреть все записи из набора, сохранить
их в своей локальной памяти или использовать каким-либо другим способом.
В частности, Вы получаете право обновить полученный набор записей, если
хотите обновить источник данных.
После обработки набора записей его нужно закрыть методом Close, предус-
мотренным для этой цели в объекте Recordset.
Обработка ошибок
В процессе подготовки параметров команды и се выполнения иногда возника-
ют события, связанные с ошибками. Ваше приложение должно уметь их обра-
батывать. Заметим, что одна команда может порождать несколько сообщений об
ошибках, поэтому обработка должна выполняться в цикле.
122 Базы данных в Интернете. Практическое руководство
Объекты ADO
А сейчас мы расскажем о методах и свойствах объектов, составляющих фунда-
мент ADO. Применение этих методов иллюстрируется в серверных сценариях
ASP, составленных на JScript.
Объект Connection
При помощи объекта Connection приложение устанавливает связь с источником
данных, то есть открывает сеанс связи с источником данных.
Объект Connection связан с объектами E r r o r s , Command и Recordset, как это
показано на рис. 4-1.
Errors
Connection Command
Recordset
cmd.Parameters.Append(ParamOut);
В первой строке мы обращаемся к методу C r e a t e P a r a m e t e r , определенному в
объекте cmd класса Command (напомним, параметры имеют отношение к командам).
Command
Parameters
Parameter
Recordset
Fields
Field
connect = Server.CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "password");
cmd = Server.CreateObject("ADODB.Command");
cmd.CommandText = "ListOrders";
cmd.CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;
cmd. Parameters.Append(cmd.CreateParameter(
"ClientID", adVarChar, adParamlnput, 50, ClientID));
rs = cmd.Executef);
Глава 4. Связь приложений с базами данных через ADO 129
<td><K=rs.Field8("Author"))(>. <X=rs.Fields("Title")K>
<br><X=rs.Fields( "Publisher" )Kx/td>
<td><X=rs.Fields("Price")X> y.e.
Описанные здесь приемы работы с объектами Recordset используются во всех
примерах, приведенных в этой главе.
Объект Errors
При выполнении команд могут возникать ошибки. Ошибки попадают в ADO от
провайдера и помещаются в набор E r r o r s . Заметим, что в зависимости от ситу-
ации в результате выполнения одной команды может возникать сразу несколь-
ко ошибок. Для каждой создается объект E r r o r , который затем помещается в
набор E r r o r s .
В случае серверных сценариев JScript объект E r r o r имеет свойства n u m b e r и
description, первое из которых содержит числовой код ошибки, а второе — ее
текстовое описание (рис. 4-4).
Заметим, что в сценариях VBScript объект E r r o r имеет несколько свойств,
перечисленных в таблице 4-4. Эти же свойства доступны и в приложениях C++,
обращающихся к объектам ADO с импортированием библиотеки типов.
Таблица 4-4. Свойства объекта E r r o r , доступные сценариям VBScript
Свойство Описание
Description Текст описания ошибки
Number Численный код ошибки типа long
Source Объект, вызвавший появление ошибки
SQLState Информация об ошибке от источника данных SQL
NativeError Информация об ошибке от источника данных SQL
HelpFile^ Файл электронной справочной системы Microsoft Windows Help, ко-
торый содержит объяснение ошибки (иногда отсутствует)
HelpContext Идентификатор раздела упомянутой выше электронной справочной
системы с описанием ошибки (иногда отсутствует)
Как видите, на сегодняшний день VBScript способен предоставить более об-
ширную информацию об ошибках, чем JScript. Однако для решения большин-
ства практических задач вполне достаточно средств обработки ошибок, предус-
мотренных в JScript.
Набор E r r o r s создается в рамках объекта C o n n e c t i o n и имеет, таким образом,
отношение к конкретному соединению с базой данных. Обработка ошибок за-
ключается в том, что приложение в цикле перебирает все элементы E r r o r набо-
ра E r r o r s , выделяя из них код ошибки и текст сообщения об ошибке.
Способ обработки ошибок в серверных сценариях ASP сильно зависит от
языка, на котором этот сценарий составлен. В литературе описано множество
примеров обработки ошибок в сценариях VBScript и очень мало — в сценариях
JScript. Мы попытаемся 'восполнить этот недостаток. •
Как вы знаете, существует два принципиально разных подхода к обработке
ошибок. Первый предполагает проверку кодов завершения при выполнении тех
или иных операций, а второй основан на использовании исключений (exception).
Исключения доступны практически во всех современных системах програм-
мирования. Например, такие операторы, как t r y и catch встроены в C++ и Java.
Глава 4. Связь приложений с базами данных через ADO
cmd.Parameters.Append(cmd.СreateParameter(
"booksID", adVarChar, adParamlnput, 50, booksID»;
cmd.Parameters.Append(cmd.СreateParameterf
"ClientID", adVarCnar, adParamlnput, 50, ClientID»:
cmd. ExecuteO;
catch (ex)
<
if(ex instanceof Error)
if(connect.Errors.Count==0)
throw ex;
var errOescription="", errNumber=0,serrMessage="";
for(i=0; Kconnect. Errors. Count; i++)
{
errDescription=connect.Errors(i).description;
errNumber=connect.Errors(i).number;
serrMessage += ("["+errNumber+"]" + errDescription + "<br>");
Response.Redirect(
"error.asp?ERROR=books.asp"+"&ERRMSG="+serrMessage);
else
throw ex;
Connection
Command
Properties
Property
Recordset
Field
Проект Интернет-магазина
Теперь, когда Вы предварительно познакомились с ADO, мы предлагаем Вам
реализовать проект Интернет-магазина, предназначенного для продажи книг.
Под нашим руководством Вы создадите базу данных магазина и набор страниц
ASP, реализующих приложения покупателей и администрации магазина.
Процедуры, описанные в этом разделе, пригодятся Вам и при создании дру-
гих приложений ASP с базами данных, даже если они не имеют никакого отно-
шения к Интернет-магазинам.
Создание базы данных
Прежде чем приступать к изучению страниц ASP нашего Интернет-магазина,
нужно создать базу данных. Мы предполагаем, что Вы уже установили Microsoft
Глава 4. Связь приложений с базами данных через ADO
SQL Server версии 7.0, а также пакет обновлений Service Pack 1 для SQL Server
(или боле новый).
Сначала мы создадим базу данных и все необходимые таблицы, затем подго-
товим хранимые процедуры и установим права доступа к ним.
Подготовка таблиц
Запустите приложение SQL Server Enterprise Manager и откройте сервер базы
данных. Затем выберите из меню Action строку New Database. На экране по-
явится диалоговая панель Database Properties, показанная на рис. 4-6,
Cancel
(Лак. 3571
III Базы данных в Интернете. Практическое руководство
Таблица managers
Эта таблица содержит информацию о персонале Интернет-магазина, управля-
ющего его работой через Интернет с помощью специального административно-
го приложения. Назначение полей таблицы managers объясняется в таблице 4-6.
Таблица 4-6. Поля таблицы managers
Поле Тип Описание
ManagerlD int Ключевое поле с атрибутом IDENTITY однозначно идентифи-
цирует запись в таблице m a n a g e r s
Name v a r c h a r ( S O ) Имя сотрудника, используется в качестве идентификатора
при подключении к административному приложению Ин-
тернет-магазина
Password v a r c h a r ( S O ) Пароль сотрудника
LastLogin datetime Время, когда сотрудник подключался к системе в последний
раз
Rights v a r c h a r ( 1 6 ) Права сотрудника
Когда сотрудник подключается к административному приложению, он вво-
дит свой идентификатор и пароль. Приложение проверяет, есть ли такой пользо-
ватель в таблице managers и правильно ли указан пароль. Если все верно, при-
ложение выбирает из поля Rights текстовое описание прав сотрудника и обнов-
ляет поле LastLogin, фиксируя момент его подключения к системе.
Чтобы создать данную таблицу в базе данных Bookstore, запустите приложе-
ние SQL Server Enterprise Manager (если оно еще не работает), а затем выбери-
те из меню Tools строку SQL Server Query Analyzer. В результате будет запу-
щено приложение SQL Server Query Analyzer, главное окно которого показано
на рис. 4-7.
Далее воспользуйтесь строкой Open в меню File для выбора файла сценария
SQL с именем dbo.managers.TAB, создающего таблицу m a n a g e r s . Содержимое
этого файла показано в листинге 4-1.
Листинг 4-1 Вы найдете в файле ch4\BookShopScripts\dbo.managers.TAB на
прилагаемом к книге компакт-диске,
Загрузив сценарий SQL в окно приложения SQL Server Query Analyzer, за-
пустите его, щелкнув клавишу F5, кнопку запуска на инструментальной пане-
ли (с изображением треугольника зеленого цвета) или выбрав строку Execute
из меню Query. Если Вы не допустили ошибок при вводе сценария SQL, в ниж-
ней части окна Query появится сообщение «The command(s) completed suc-
cessfully».
Таблица clients
Таблица clients содержит сведения, предоставленные посетителями Вашего ма-
газина при регистрации, а также дополнительные данные — дату регистрации и
адрес IP, с которого выполнялась регистрация. Посетитель магазина работает с
этой и еще одной таблицей при помощи созданного нами приложения покупа-
теля Интернет-магазина.
Поля таблицы clients описаны в таблице 4-7.
Таблица 4-7. Поля таблицы clients
Поле Тин Описание
ClientID [i Идентификатор записи таблицы clients (ключевое
поле)
UserlD varchar(SO) Идентификатор, который должен указывать покупатель
при подключении к пользовательскому приложению
Интернет-магазина
F'assword varchar(50) Пароль покупателя, выбранный им при регистрации
Language varchar(50) Предпочтительный язык для отображения страниц. В
нашем демонстрационном примере это поле заполняется,
но не проверяется
Money Money Общая сумма денег, потраченная покупателем в магази-
не с момента совершения первой покупки по настоящее
время
Status char(1) Состояние покупателя (активный или неактивный). Не
используется
LastLogin Datetime Дата и время последнего посещения покупателем Интер-
нет-магазина
UserName varchar(SO) Полное имя покупателя, введенное им при регистрации
E:.mail varchar(80) Адрес электронной почты, указанный покупателем при
регистрации
mail varchar(80) Почтовый адрес покупателя
spam char{3) Флаг рассылки рекламных сообщений (н пашем прило-
жении заполняется, но не проверяется)
RegisterDate Datetime Дата и время регистрации покупателя
FegisterIP varchar(15) Адрес II* с которого выполнялась регистрация покупателя
138 Базы данных в Интернеге. Практическое руководство
Поле AddDate, хранящее дату и время выбора книги, применяют для автома-
тической очистки корзин от старых записей. Например, Вы можете принуди-
тельно удалять записи об отобранных книгах, сделанные более года назад.
Сценарий SQL, создающий таблицу orders, представлен в листинге 4-4.
Листинг 4-4 Вы найдете в файле ch4\BookShopScripts\dbo.orders.TAB на при-
лагаемом к книге компакт-диске.
Обратите внимание на ограничение CONSTRAINT поля AddDate:
CREATE TABLE [dbo].[orders] (
[ordersID] [int] IDENTITY (1, 1) NOT NULL ,
[booksID] [int] NOT NULL ,
[ClientID] [int] NOT NULL ,
[AddDate] [datetime] NOT NULL
CONSTRAINT [DF_orders_AddDate] DEFAULT (getdateO),
[BookPrice] [money] NOT NULL ,
CONSTRAINT [PK_orders] PRIMARY KEY NONCLUSTEREO
С
[ordersID]
) ON [PRIMARY]
) ON [PRIMARY]
С его помощью в данное ноле автоматически записывается время и дата до-
бавления книги в корзину покупателя.
Подготовка хранимых процедур
Для приложений нашего Интернет-магазина мы подготовили ряд хранимых
процедур. Серверные сценарии, расположенные на страницах ASP его приложе-
ний, будут обращаться к этим процедурам для выполнения всех операций с ба-
зой данных.
Для примера мы привели в листинге 4-5 исходный текст хранимой процеду-
ры C l i e n t L o g i n . предназначенной для подключения к Интернет-магазину заре-
гистрированных покупателей.
Листинг 4-5 Вы найдете в файле ch4\BookShopScripts\ dbo.ClientLogin.PRC на
прилагаемом к книге компакт-диске.
В качестве входных параметров мы передаем этой процедуре идентификатор
пользователя @User и пароль пользователя @Pass:
CREATE PROCEDURE ClientLogin @User varchar(SO), @Pass varchar(50), ©Rights
varchar(16) output AS
Owner.
Create Oate
Рис. 4-Ю. Первая страница мастера создания источника данных для SQL Server
В поле Name этой страницы введите имя создаваемого источника данных —
BookStore. В поле Description Вы также можете описать источник данных.
Далее в списке Server выберите сервер базы данных, к которому будет вы-
полняться подключение. Если серверы Web и SQL Server установлены на одном
компьютере (как у нас), нужно выбрать в этом списке строку (local). Если же
сервер SQL Server работает на другом компьютере, в этом списке укажите нуж-
ный сервер базы данных.
Заполнив поля на первой странице, щелкните кнопку Next.
На следующей странице мастера (рис. 4-11) Вы должны выбрать способ
аутентификации при подключении к SQL Server.
t New Datd Source (a SQL Server
!
I
f OII-
-:
E3
^ £ertofRS ifansWion to diaieeia
1
l.org oueqi 'пи imilliseconcil
Attempting
tiection established
Vending tjption settings
>isconrwctng from seivei
. !
Теперь вес готово для того, чтобы приступить к настройке виртуальных ка-
талогов сервера Web и к созданию страниц ASP нашего Интернет-магазина.
Подготовка виртуальных каталогов сервера Web
Наш Интернет-магазин состоит из двух приложений ASP. Первое предназначе-
но для сотрудников магазина, и мы назовем его административным. Второе
приложение будут запускать посетители Вашего магазина. Мы назовем его при-
ложением покупателя.
Виртуальный каталог приложения покупателя BookShopClient
В виртуальном каталоге приложения покупателя хранятся страницы ASP, до-
ступные покупателям. Они предназначены для просмотра списка товаров (книг),
отбора товаров в корзину и расплаты за покупку.
Этот виртуальный каталог, так же как и виртуальный каталог административ-
ного приложения, создается средствами управляющей консоли Microsoft Mana-
gement Console. Укажите имя каталога как BookShopClient, проследив за тем,
чтобы для него были разрешены чтение и исполнение сценариев. Для этого в
панели BookShopClient Properties откройте вкладку Virtual Directory и пометь-
те переключатели Read и Script (если они были выключены).
Разрешите также настройку сернерпых и клиентских сценариев на вкладке
Арр Debugging и с помощью переключателей Enable ASP server-side script
debugging и Enable ASP client-side script debugging. Для того чтобы эти изме-
нения вступили в силу, закройте панель и перезагрузите компьютер.
После завершения отладки реального проекта возможность его настройки
следует отменить, убрав отметки с упомянутых переключателей.
Виртуальный каталог административного приложения BookShop
Этот виртуальный каталог содержит страницы ASP, предназначенные для про-
смотра только сотрудниками Вашего магазина. Обычные посетители не долж-
ны иметь к нему никакого доступа.
Bookshop f roppitie:
Secure Corcrriunic
fs Windows NT Cbarlenge^Rejponse
User Наше and Password required when;
* A(aw Апогцлпоиг ts disabled
"Access is restiioted using NTFS uccess СопЦЫ Lis
(/Granted Ц 172,21.1.10
Приложение покупателя
Приложение покупателя будут запускать посетители магазина, поэтому при его
реализации мы решили обойтись без клиентских сценариев, Это позволило нам
расширить номенклатуру браузеров, способных правильно показывать страни-
цы «публичной» части Интернет-магазина.
Прежде чем приступить к рассказу о том, как реализовать страницы ASP
приложения покупателя, рассмотрим логику его работы.
Когда посетитель попадает на эту страницу, в окне его браузера появляется
форма, показанная на рис. 4-20.
Э Подключение пользователя - Microsoft Inleinel
Пароль
Пожалуйста, зарегистрируйтесь
Вам НУЖНО
Если посетитель уже зарегистрировался ранее, то, для того чтобы попасть в
магазин, ему нужно ввести в поле Имя свой идентификатор, а в поле Пароль —
свой пароль. Новые пользователи должны зарегистрироваться, щелкнув кноп-
ку Регистрация.
В тех случаях, когда указанный идентификатор пользователя отсутствует в
регистрационной базе данных или когда пароль указан неправильно, посетитель
получает сообщение о необходимости регистрации (рис. 4-21).
Щелкнув ссылку Вам нужно зарегистрироваться, посетитель попадает на
входную страницу, показанную на рис. 4-20.
Таким образом, мы не сообщаем посетителю, допустил ли он ошибку при
вводе идентификатора или пароля, а просто информируем его о необходимос-
ти регистрации.
Форма регистрации новых посетителей показана на рис. 4-22.
'3 Подключение пользователя - Miciosofl Internet Enploi... И '
1
[Иванов Иван Иванова
Идентификатор
Пароль
Подтверждение пароля
Сбросить
в] Done
Повторить регистрацию
Входите!
г
•Ц KMH*IMJH магазин - Miciosofl Internet Explorer provided by www auiamedia.iu I \
Fie £d( Vtaw Fgvwitei J.«Js help
- - - - BBeMi
№
•4= •* »* • "^jj j] :2f Д _iJ 14$ • -;V «J ^ " - -J Links i|] Вози Shop SJBookShopCIail gJBesloMheWeo
J
jydfetsj^S] hl!p.//;alurn. Bc -.STOpCk-n-'Tanasp _ _ _ J^toj
ь
Вы ::-.'. д
Сегодня в продаже
Оплатить покупки
:
А.В, Фролов. Г.В. Фролов. Сочдани* прнложеню"! г 6a~-v-.ni L,-
данных для Илгернетл и интрагетен: практическое
ПОЛОЖИТЕ 3
л к:рзяку
•Русская редакция
Аннотация:
Эта книга предстаытяет собой практическое руководство по созданию приложений Web с базами
данных для Интернет!, а также дпякорпорэтиЕНСЙннтра.сети Б ней рас смотрены современные
технологии, созданные Microsoft див работы с базами даншп
иишниянниидм ВБ
Асй^^Шр..-^^^.^!^^^-^^^, _ _ _ _ _ _ _____ _ d -*Go
Аннотация
Эта.ншгапредстаэгиет с обой практическое руководство по созданию прьшожений W&b с
базами данных для Интернета, а также для корпоративной интрасетк. Б ней рас смотрены
современные технологии, созданные Micros oft для раНсты с ба^аыидайнъи..
\ АН нотация.
Ш ТЪ В;
<tr>
<td>HMH </td>
<td><lnput SIZE="10" TYPE="EDIT" NAME="USR"> </td>
</tr>
<tr>
<td>Пapoль </td>
<tdxnobr>
<input SIZE="10" TYPE="password" NAME="PWD">
<input TYPE="submit" VALUE="Bxofl"X/nobrx/td>
</tr>
</table>
</form>
Она ссылается на страницу enter.asp. Вторая форма содержит кнопку с над-
писью Регистрация, щелкнув которую, посетитель попадает на страницу реги-
страции regdataenter.asp:
<form ACTION="regdataenter.asp" METHOD="post" TARGET="_top">
<п2>Пожалуйста, зарегистрируйтесь</^12>
<table BORDER="0" CELLPADDING="511 CELLSPACING="0">
<trxtdx/tdxtd>
<input type="submit" уа!ие="Регистрация" name="B1"x/td>
</tr>
</table>
</form> </td></tr>
Вначале мы рассмотрим исходный текст страницы enter.asp (листинг 4-8),
Листинг 4-8 Вы найдете в файле ch4\BookShopClient\enter.asp на прилагаемом
к книге компакт-диске.
При загрузке этой странице передается из формы содержимое полей с име-
нами USR и P W D (идентификатор посетителя и его пароль соответственно).
Серверный сценарий, расположенный на странице enter.asp, получает иден-
тификатор пользователя и пароль, обращаясь к объекту R e r q u e s t , а затем сохра-
няет соответствующие строки в переменных с именами sUser и sPassword:
var sUser=Request("USR")(1);
var sPassword=Request("PWD")(1);
Далее сценарий создает объект C o n n e c t i o n и открывает соединение с источ-
ником данных, вызывая для этого метод Open:
var connect;
connect = Server.CreateObjectC'ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");
С целью обработки ошибок зта и последующие операции с базами д а н н ы х
выполняются сценарием в блоке t r y .
Мы передали методу Open в качестве параметров имя источника базы данных
Интернет-магазина Bookstore, созданное нами ранее, имя владельца базы дан-
ных и пустую строку пароля. В реальном проекте мы рекомендуем определить
отдельные учетные записи для работы с источником данных и пароли.
154 Базы данных в Интернете. Практическое руководство
Session("Rights")=ParamOut.value;
Session("0k")="0k";
Session("UserID")=sUser;
Глава 4. Связь приложений с базами данных через ADO 155
Response.Red!rect("main. asp");
else
{
Session("0k")="";
Session("UserID")="";
Session("Rights")="";
Переменная сеанса с именем R i g h t s никак не используется нашим приложе-
нием, хотя Вы можете применить ее для дифференциации посетителей. В этом
случае в таблице clients Вы можете предусмотреть поле R i g h t s и хранить в нем
права пользователей, устанавливая их в зависимости от тех или иных условий.
Например, пользователи, сделавшие покупки на значительные суммы, могут
получить доступ к дополнительным страницам Вашего магазина.
Содержимое переменной сеанса Ok проверяется перед отображением всех
страниц приложения покупателя, кроме страниц аутентификации и регистрации.
В результате мы исключаем возможность прямого доступа к этим страницам для
пользователей, не прошедших аутентификацию.
И наконец, переменная сеанса UserlD идентифицирует покупателя и исполь-
зуется, например, для формирования его персональной корзины.
При неудачной аутентификации содержимое указанных переменных сеанса
обнуляется, а пользователю выдается страница с сообщением о необходимости
регистрации.
Обработка ошибок
В том случае, когда при обращении к базе данных возникли ошибки, мы их
обрабатываем в блоке catch. Использованная при этом методика была описана
ранее, поэтому здесь мы только скажем, что сообщение об ошибке передается
странице error.asp:
Response.Redirect(
"error.asp?ERROR=enter.asp"+"&ERRMSG="+serrMessage);
При этом через параметр ERROR мы передаем имя файла страницы, в которой
произошла ошибка, а в переменной ERRMSG — сообщение об ошибке, подготовлен-
ное в блоке catch.
Исходный текст страницы error.asp показан в листинге 4-9.
Листинг 4-9 Вы найдете в файле ch4\BookShopClient\error.asp на прилагаемом
к книге компакт-диске.
Здесь мы информируем посетителя о том, что произошла ошибка в приложе-
нии, для исправления которой необходимо вмешательство администратора сер-
вера Web. Далее отображается название страницы, в которой произошла ошиб-
ка, и текст сообщения об ошибке:
•;п2>0шибка в приложении</п2>
<р>0братитесь к администратору сервера Web. </p>
<p>[<X=Request ("ERROR" )X>]<br><X=Request ("ERRMSG" )Jf>
На рис. 4-27 показано сообщение, появляющееся в том случае, если Вы пы-
таетесь создать соединение с несуществующим источником данных.
IV- Базы данных в Интернете. Практическое руководство
1ГПТТ.:»=.:«.М [а:1;К
" °- --.. '
Ошибка в приложении
Обретитесь к администратор;' сервера Web.
[enter asp]
[-21^#72№][Mkrosoft][QDBCDtivsr
name not found and no default driver specified
в] Done
-I
Microsoft! OLE DB Provider for ODBC Drivers error 1 80040tQ9 1
/BookSfiopClientienler.asp, line 29
</html>
<!- «include file="footer.asp" ->
Исходные тексты этих файлов приведены в предыдущей главе (листинги 3-19
и 3-20), поэтому мы не будем здесь их повторять. Напомним только, что при
загрузке любой страницы, в начало которой вставлен файл header.asp, шлполня-
стся проверка содержимого переменной сеанса с именем Ok. Если аутентифика-
ция пользователя прошла успешно, сценарий файла header.asp вставляет в до-
кумент теги <HTML> и <BODY>, а при неудаче — тег <МЕТА>, принудительно направ-
ляющий браузер на страницу.аутентификации default.asp.
В результате все, что увидит посетитель, попытавшийся напрямую загрузить
в окно своего браузера страницу main.asp. это страница default.asp с предложе-
нием ввести идентификатор и пароль или выполнить регистрацию.
Страница меню команд
В левом фрейме страницы приложения покупателя расположено меню команд,
состоящее из двух ссылок — Выход и Оплатить покупки. Исходный текст дан-
ного фрейма Вы найдете в листинге -1-12.
Листинг 4-12 хранится в файле ch4\BookShopClient\toc.asp на прилагаемом к
книге компакт-диске.
Ссылка Выход отправляет покупателя на страницу default.asp, предлагающую
ввести идентификатор и пароль зарегистрированного покупателя или выпол-
н и т ь регистрацию нового покупателя.
<а href=". ./BookStiopClient/default.asp"
target="_top">Bbixofl</a><br>
Ссылка Оплатить покупки используется для загрузки страницы makcorder.asp,
па которой реализуется процесс оплаты.
<а href="../BookShopClient/makeorder.asp"
target="„1:ор">0ллатить покупки</а></р>
Глава 4. Связь приложений с базами данных через ADO 159
%>
</TABLE>
Для ссылки на поля набора записей мы используем имена соответствующих
столбцов, из которых формируются строки набора. Содержимое полей набора
вставляется в ячейки формируемой таблицы с помощью конструкций вида
«<X=rs. Fields("Author")K>».
Для каждой книги мы вставляем в создаваемую таблицу ссылку на страни-
цу order.asp, которая позволяет 1 добавить книгу в электронную корзину поку-
пателя:
<а href="order.asp?ID=<X=rs.Fields("booksID")K>&FIRST=0"
target="rbottom">ri(MO)KKTb в корэину</А>
Когда пользователь щелкнет ссылку, начнет загружаться страница order.asp.
При этом ей будут переданы два параметра — идентификатор книги ID и флаг
FIRST.
Параметр ID содержит идентификатор, по которому можно найти книгу в
таблице books нашей базы данных. Без него мы бы не знали, какую именно книгу
нужно добавить в корзину. Что же касается флага FIRST, то его значение, равное
О, свидетельствует, что страница order.asp загружается для добавления новой
книги в список отобранных книг, а не для простого просмотра этого списка.
Глава 4. Связь приложений с базами данных через ADO 161
var booksID=Request("ID")(1);
var connect;
var cmd;
connect = Server.CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");
cmd = Server.CreateObject("ADODB.Command");
cmd.CommandText = "AddToOrder";
cmd.CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;
cmd.Parameters,Append(cmd.СreateParameter(
"booksID", adVarChar, adParamlnput, 50, booksID));
cmd.Parameters.Append(cmd.CreateParameter(
"ClientID", adVarChar, adParamlnput, 50, ClientID));
cmd. ExecuteO;
}
Теперь нам нужно отобразить в правом нижнем фрейме главного окна при-
ложения покупателя обновленное содержимое корзины. Чтобы не устанавливать
повторное соединение с источником данных (оно уже установлено для запуска
хранимой процедуры AddToOrder), мы повторно анализируем параметр FIRST. Это
позволяет нам сэкономить время и ресурсы сервера за счет исключения повтор-
ного соединения с источником данных:
if(Request("FIRST")(1) == 1)
{
connect = Server.CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");
Глава 4. Связь приложений с базами данных через ADO 163
cmd.Parameters.Append(cmd.СreateParameter(
"ClientlD", adVarChar, adParamlnput, 50, ClientlD));
var rs;
rs=cmd. ExecuteO;
Данный метод возвращает набор записей, образовавшихся в результате вы-
полнения команды.
На следующем этапе сценарий формирует в окне фрейма таблицу с названи-
ями книг, отобранных покупателями. Она создается в цикле путем последова-
тельного просмотра записей набора rs методом M o v e N e x t .
<п2>Вы отобрали для покугжи</п2>
<TABLE BORDER=1>
а
while (!rs.EOF)
%>
<tr><td><b>a=rs.Fields("Author")!i>. <%=rs. Flelds(Title")J[x/b>
<brxixx=rs.Fields( "Publisher" )Xx/ix/td>
<tdxx=rs.Fields("Price")X> y.e.<br>
<a href="delorder.asp?ID=<X=rs.Fields("booksID")X>" target="rbottom">
Удалить из корзины</А>
</tdxtr>
<%
rs.MoveNextO;
%>
</TABLE>
Чтобы пользователь, передумавший покупать книгу, смог удалить ее из кор-
зины, мы вставляем рядом с названием каждой книги ссылку следующего вида:
<а href="delorder.asp?ID=<a;=rs. Fields("booksID")X>" target="rbottom">
Удалить из корзины</А>
Здесь выполняется вызов страницы delorder.asp, причем в качестве парамет-
ра эта страница получает идентификатор удаляемой к н и г и . Что же касается
идентификатора покупателя, то эту информацию страница delorder.asp «добы-
вает» самостоятельно из переменной сеанса с именем UserlD.
После завершения формирования таблицы сценарий закрывает набор запи-
сей и ненужное больше соединение с источником данных:
rs.CloseO;
connect.CloseC);
164 Базы данных в Интернете. Практическое руководство
что нужно показать текущее содержимое корзины, не выполняя над ней ника-
ких операций.
Исходный текст хранимой процедуры DelOrder представлен в листинге 4-19.
Листинг 4-19 находится в файле ch4\BookShopScripts\dbo.DelOrder.PRC на
прилагаемом к книге компакт-диске.
Процедура D e l O r d e r выполняется в два этапа.
На первом из таблицы clients извлекается идентификатор записи посетителя
по его идентификатору, полученному процедурой через параметр @ClientID.
CREATE PROCEDURE DelOrder @ClientID varchar(SO), @bookID varctmr(50) AS
DECLARE @nClientID INT
SELECT @nClientID=clients.ClientID FROM clients
WHERE UserID=@ClientID
На втором этапе из таблицы o r d e r s удаляются все записи о книгах с иденти-
фикатором @bookID, добавленные данным посетителем.
DELETE orders
WHERE booksID=@bookID AND ClientrD=@nClientID
Административное приложение
Теперь перейдем к описанию административного приложения, предназначенного
для управления работой магазина. Это приложение позволит Вашим сотрудни-
кам редактировать список книг, имеющихся в продаже, формировать и редакти-
ровать список персонала магазина и решать другие административные задачи.
Обычные посетители не должны иметь к этому приложению никакого доступа.
Прежде чем приступить к работе с административным приложением, Вам
надо ввести идентификатор администратора и пароль на странице аутентифи-
кации (рис. 4-29).
агеяя Micmsofl Internet E . НИН
Добро пожаловать!
ИМЯ jfrolDV
Пароль я™1*"
Igg—
*• - •• • *м я™g,™^^gg,^g^
а л j •=> -* _л * f j 'Lrtt eje^s»
AtfteN |t] rvrp /..'-^urn/Boc^hcp/Mr, ЭФ J *>Go
КнШЗ
Добро пожаловать,
ЛоЕ^ттат&гш уважаемый frolov!
Ви подключились ксистгмекак
Адпшсипратор
Угщжщошй
Список управляющего персонала
lOflSE
11ия Imua КОМАНДЫ
тмдалючвння
7 Зак. 3571
168 Базы данных в Интернете. Практическое руководство
l
Address |jJ] http://!alLWBookShcp< main.e5p
Новый администратор
Имя (admin
Пароль 123
Права [Всепрвва
Создать
Done
Address№] Htp.J'^lLir,/Bo^S-,ir;'
Редактирование записи
Книги
Пзрпттатдлк
пользователя admin
Имя jadmin
Параш, |Тгз
п .
'
Сохранить изменения
ч* Администрирование книжного ианазннл - Microsoft Internet ffuplorer provided by www auidmedr. HFSTB'
look, ,Ци|р
[Новая книга'
i_^_ 'Ь^лщщш
Новая к ннга
ЬГтпи
Автор Скотт Хилайер. Дэниэг Мизик
Название Программирование Active SetrvcE Расгеэ
Добаеигь |
[Й -Done НЁ1-веайййап«
• ПЕЭ
Ц|рна, у.е. 80
Сохренить изменения
1 >1^
^J Done 1
-I-***
s ig] http /AalurtVBookS hop/main asp
Идентификатор похупатгля:
Адрес E-Mail:
ЯнЕ-rai
' ' вй
Март Щ 6 7 'i 2 IQ И. Сентябрь MJJJT
Апрель 13 IS W 15 J£ Я И Октябрь Аяреи* 12 12 И L5 1й 1Z 1£ Октябщ
Май 1£ 2В 21 31 21 24 £5 Ноябрь Май I? 20 Я 22 25 24 25, Н оябрь
Игега ' 26 JZ 2£ 29 3U Л. Декабрь Июнь 2^ Г ЗЕ 2£ 3LI Ц Декабрь
Век Пвд БЧЗ Срд Ч1Е Птв C6i Век Пнд Втр Срд Чтв Птн С&с
Поиск
а
Рис. 4-37. Форма для поиска зарегистрированных покупателей
Управляь-.ший
]1окупатели
арегистрироБвпис! в любое греми
'днпификатор *4, ЕМай; %
Нжмпй>
Ш
™
•DB " ^"vJSU" Ajqiec E-Mail Команды
.Изменить
Апекса "ЧР n ' !
"
J ftolovTgglastiel ru Корзина
Фропш !2Q:23:43
Изменить
Q5 12 9S
John JohnSt от Корзина
120253
1 . . . •Удалить
РИД op о B
'05 12 99
liiur Сад op sid QI@ s omemail. s ome_ Sortiaul 'Кощцра
11-45-12
Сидоре ВИЧ
;
Beer* покупа-илен: 4 0 ~
-1 Ы
Й js) Loc. t intranet
Идентификатор sidor
Пароль
Язык 'Анппсюий"
Деньги "О"
Дата регистрации 03.12.99 И 45:12
Агфес IP при регистрации 172.21.1 10
Обновить
jb| Lotdrtranet
Внимание!
Учетная запись
Jolm Sniitl.
Зуд ет удалена!
Учетная запись
Joint Smith
успешно удалена
СК
Александр Фролов
ЭК
Файл global.asa
В корне виртуального каталога BookShop, созданного нами для страниц адми-
нистративного приложения, необходимо поместить файл global.asa. Вы можете
использовать здесь тот же самый файл, что и для приложения покупателя. Его
исходный текст мы уже приводили в листинге 4-6.
Страницы входа
Рассмотрим исходные тексты страниц, предназначенные для входа сотрудников
Вашего Интернет-магазина в административное приложение.
174 Базы данных в Интернете. Практическое руководство
Главная страница
Главная страница административного приложения содержит определение двух
фреймов (листинг 4-22).
Листинг 4-22 Вы найдете в файле ch4\BookShop\main.asp на прилагаемом к
книге компакт-диске.
В левом фрейме toc.asp находятся команды в виде ссылок на другие страни-
цы, а в правом (с именем m a i n ) отображается результаты выполнения этих команд:
<l- Jfinclude file="header. asp" ->
</body>
</noframes>
</f rameset>
rs = Server.CreateObject("ADQD8.Recordset")
rs.Open("ListManagers", connect,
adOpenForwardOnly, adLockReadOnly, adCmdStoredProc);
Эта процедура не имеет никаких параметров, а в результате своей работы
возвращает содержимое полей Name, R i g h t s и L a s t L o g i n всех записей файла
m a n a g e r s . Эти поля сохраняются в наборе записей rs.
Далее сценарий проверяет наличие записей и, если файл m a n a g e r s не пуст,
создает на их основе таблицу. Для этого в сценарии организован цикл:
178 Базы данных в Интернете. Практическое руководство
<TABLE BORDER=1>
<ТР><ТН>Имя</ТНХТН>Права</ТН>
<ТН>Время последнего подключения</ТН><ТНЖойанды</ТН></ТЯ>
<К
if(rs.EOF)
%>
<TR><TD COLSPAN=4 ALIGN="CENTER">[HeT записей]</ТОХ/ТИ>
<X
.
else
-
var sID, sDate="";
while (Irs. EOF)
sID=rs.Fields("Name").value;
if(rs.Fields("LastLogin").value != null)
I
sDate=rs.Fields("Last Login").value;
.
var sRights="";
if(rs.Fields("Rights").value == "Administrator")
sRights="AAMMHMCTpaTop";
else if(rs.Fields("Rights").value == "Salesjnanager")
sRights="MeHefl)Kep по продажам";
else if(rs.Fields("Rights").value == "shjnanager")
зР1дШз="Заведующий складом";
else
sRights=rs.FieldsC"Rights").value;
%>
<TR>
<TDxb><!i=rs.Fields("Name").valueJ;x/b><AD>
<TD><J6=sRightsX></TD>
<TD> <X=sDate)!X/TD>
<TD>
<a href="edtmanager. asp?ID=<!(=sID*l>" target="main">ll3MeHHTb</A>
<a href="delmanager.asp?ID=<K=sIDX>" target="main">yflaflHTb</A>
</TD>
</TR>
<l
rs.MoveNextO;
rs.CloseO;
connect,Close();
'
В каждую строку создаваемой таблицы записывается идентификатор сотруд-
ника, используемый им для подключения, его права, время последнего подклю-
чения, а также две команды — Изменить и Удалить.
Изменение учетной записи сотрудника выполняется на странице edtma-
nager.asp, а удаление — на странице delmanager.asp. И та, и другая страница по-
Глава 4. Связь приложений с базами данных через ADO 179
cmd,Parameters.Append(cmd.CreateParameter(
"User", adVarChar, adParamlnput, 50, Request("USR")(1)));
cmd.Parameters.Append(cmd.СreateParameter(
"Pwd11, adVarChar, adParamlnput, 50, Request("PWD")(1)));
cmd. ExecuteO;
connect.Close();
:
else
<
Response.Redirect{"error.азр?ЕНн(Ж=Недостаточные права");
!
Далее сценарий соединяется с базой данных и запускает хранимую процедуру
NewMgr, создающую новую запись в таблице managers. Этой процедуре передаются
три входных параметра, значение которых получено из полей формы создания
учетной записи нового сотрудника, рассмотренной нами ранее.
По завершении своей работы сценарий страницы createmgr.asp снова загру-
жает в правый фрейм главной страницы административного приложения доку-
мент managers.asp:
Response.Redi rect("managers.asp");
Теперь в нем появится новая только что добавленная запись.
Исходный текст хранимой процедуры NewMgr Вы найдете в листинге 4-29.
Листинг 4-29 находится в файле ch4\BookShopScripts\dbo.NewMgr.PRC на при-
лагаемом к книге компакт-диске.
Глава 4. Связь приложений с базами данных через ADO 181
cmd. Parameters.Append(cmd.CreateParafneter(
"User", adVarChar, adParamlnput, 50, Request("ID")(1)));
cmd. Executef);
connect. CloseC);
I
catch (ex)
!
Response. Redirect ("managers. asp");
}
else
i
Response. Redirect ("error, asp? ERROR=0&ERRMSG=HeAOCTaT04Hbie права");
182 Базы данных в Интернете. Практическое руководство
var Usr=cmd.CreateParameter(
"User", adVarChar, adParamlnput, 50, Request("ID")(1));
var Pwd=cmd.CreateParameter(
"Pwd", adVarChar, adParamOutput, 50, " ");
var Rights=cmd.CreateParameter(
"Rights", adVarChar, adParamOutput, 16, " "};
cmd.Parameters.Append(Usr);
cmd.Parameters.Append(Pwd);
cmd.Parameters.Append(Rights);
cmd. ExecuteQ;
connect. CloseO;
Глава 4. Связь приложений с базами данных через ADO 183
cmd.CommandText = "SetMgr";
cmd.ComniandType = adCmdStoredProc;
cmd.ActiveConnection = connect;
van Usr=cmd.CreateParameter(
"User", adVarChar, adParamlnput, 50, Request("ID")(D);
var NewUsr=cmd.CreateParameter(
"NewUser", adVarChar, adParamlnput, 50, Request("USR")(1));
var Pwd=cmd.CreateParameter(
"Pwd", adVarChar, adParamlnput, 50, Request("PWD")(1));
var Rights=cmd.CreateParameter(
"Rights", adVarChar, adParamlnput, 16, Request("RG")(D);
var connect;
var rs;
connect = Server. CreateObject("ADOD8. Connection");
connect. ConnectionTiflieout = 15;
connect. CommandTimeout = 10;
connect . Openf "DSN=BookStore" , "dbo" , "" ) ;
<HTML>
<BODY>
<Ь2>Список книг</п2>
<TABLE BORDER=1>
<trxth>KHnra</thxth>KoMaHfla</thx/tr>
<%
var i=0, sID, sDate="";
while (Irs. EOF)
cmd.Parameters.Append(cmd.CreateParameter(
"Author", adVarChar, adParamlnput, 50, Request("Author")(1)));
cmd.Parameters.Append(cmd.СreateParameter(
"Title", adVarChar, adParamlnput, 200, Request("Title"){1)));
cmd.Parameters.Append(cmd.СreateParameter(
"Publisher", adVarChar, adParamlnput, 50,
Request("Publisher")(1)));
cmd.Parameters.Append(cmd.СreateParameter(
"Price", adCurrency, adParamlnput, 50, Request("Price")(1)));
cmd.Parameters.Append(cmd.CreateParameter(
"Annotation", adVarChar, adParamlnput, 2048,
Request("Annotation")(1)));
cmd. ExecuteQ;
connect.Close();
Исходный текст хранимой процедуры NewBook приведен в листинге 4-39.
Листинг 4-39 Вы найдете в файле ch4\BookShopScripts\dbo,NewBook.PRC на
прилагаемом к книге компакт-диске.
Хранимая процедура NewBook имеет одну особенность — она записывает ан-
нотацию к книге в поле A n n o t a t i o n , размером 2 048 байт;
188 Базы данных в Интернете. Практическое руководство
cmd.Parameters.Append(cmd.СreateParameter(
"bookID", adVarChar, adParamlnput, 50, Request("ID")(1)));
cmd. ExecuteO;
connect,Closef);
Этот идентификатор извлекается из параметра запуска страницы с именем ID.
Исходный текст хранимой процедуры DelBook Вы найдете в листинге 4-41.
Листинг 4-41 находится в файле ch4\BookShopScnpts\dbo.DelBook.PRC на при-
лагаемом к книге компакт-диске.
Эта процедура удаляет из файла books записи с идентификатором, передан-
ным ей через параметр ibookID:
CREATE PROCEDURE DelBook @bookID varchar(SO) AS
DELETE books WHERE DOOksID=@bookID
var bookID=cmd.CreateParameter(
"bookID", adVarChar, adParamlnput, 50, Request("ID")(1));
var Author=cmd.CreateParameter(
"Author", adVarChar, adParamOutput, 50, " ");
var Title=cmd.CreateParameter(
"Title", adVarChar, adParamOutput, 200, " ");
var Publisher=cmd.CreateParameter(
"Publisher", adVarChar, adParamOutput, 50, " ");
var Price=0;
Price=cmd.CreateParameter(
"Price", adCurrency, adParamOutput, 8, 0);
var AddDate=cmd.CreateParameter(
"AddDate", adVarChar, adParamOutput, 50, " ");
var Annotation=cmd.CreateParameter(
"Annotation", adVarChar, adParamOutput, 2048, " ");
cmd.Parameters.Append(bookID);
cmd.Parameters.Append(Author);
cmd.Parameters.Append(Title);
cmd.Parameters.Append С Publisher);
cmd.Parameters.Append(Price);
cmd.Parameters.Append(AddDate);
cmd.Parameters.Append(Annotation);
cmd. ExecuteO;
Значения выходных параметров, полученные в результате работы хранимой
процедуры, :шписываются в поля формы, предназначенной для редактирования
сведений о книге:
<FORH ACTION="updatebook,asp" METHOD="POST">
<Н2>Редактирование сведений о книге</Н2>
<TABLE BORDER=0 CELLPADDING=5 CELLSPACING=0>
<tr>
(см. след, стр.)
190 Базы данных в Интернете. Практическое руководство
var bookID=cmd.CreateParameterC
"bookID", adVarChar, adParamlnput, 50, Request("bookID")(1));
var Author=cmd.CreateParameterC
"Author", adVarChar, adParamlnput, 50, Request("Author")(1));
var Title=cmd.CreateParameterC
"Title", adVarChar, adParamlnput, 200, Request("Title")(1));
var Publisher=cmd.CreateParameterC
"Publisher", adVarChar, adParamlnput, 50,
Request("Publisher")CD);
var Price=cmd.CreateParameterC
"Price", adVarChar,' adParamlnput, 50, RequestC"Price")(1));
var Annotation=cmd.CreateParameterC
"Annotation", adVarChar, adParamlnput, 2048,
Request С"Annotation")(1));
else
\
dDateCheck=Session("dSearchUsersDateCheck");
dLogName=Session("dSearchUsersLogName");
dEMail=Session("dSearchllsersEMail");
Х>
<body onload="buildmapEx(<K=Ses$ion("dSearchUsersFromFY")U> f
<X=Session("dSearchUsersFromFM")X>, <X=Session("dSearchU$ersFromFD")X>,
<SS=Session("dSearchUsersToTY")X>,
<X=Session("dSearchUsersToTM")X>, <X=Session("dSearchUsersToTD")!f>,
'<X=(dLogName == "") ? " ; dLogNameX>' ,
1
'<X=(dEMail == "") ? ": dEHailJO ,
r
'<X=dDateGheckS;> )">
sllseDataCheckBox="yes";
}
else
{
sUseDataCheckBox="no";
"&FY="+byear.toString()+"&FM="+bmonth.toString()+
"&FD="+bday.toString()+
"&TY="+eyear.toString()+"&TM="+emonth.toString()+
"&TD="+eday.toString()+ "&MODE=first"+
"&LOGNAME="+sLogName+"&EMAIL="+sEMail+
"&FRCE="+Math.random().toString();
var vParm;
var rVal;
vParm=new Array(2);
vParm[0]=sUserID;
vParm[1]=sUserLogin;
rval=showModalDialog("trydeleteuserdlg.asp", vParm, "dialogHeight:16Qpx;
Глава 4. Связь приложений с базами данных через ADO 1 95
// Удаление записи
var sASP="";
sASP="trydeleteacc.asp?ID=" + sUserlD;
1
rVal=showModalDialog(sASP, vParm, "dialogHeignt: 160px;
dialogWidth:350px; status :0");
if(rVal==false) return;
</SCRIPT>
Когда страница GetSearchResults.asp получает управление в первый раз, рас-
положенный на ней серверный сценарий записывает в переменную сеанса с
именем S e a r c h U s e r s F i r s t U s e строку «used»;
Se5sion("SearchUsersFirstUse")="used";
Далее сценарий вызывает хранимую процедуру SearchUsers, возвращающую
список найденных учетных записей посетителей:
connect = Server. CreateObject("ADODB. Connection");
connect. ConnectionTimeout = 15;
connect. CommandTimeout = 10;
connect . Open( "DStJ=BookStore" , "dbo" , "") ;
+trim(Request("FD")(1), 2);
Session("dSearchUsersFrom")=dFroiri;
Session("dSearchUsersFromFY")=trim(Request("FY")(1), 4);
Session("dSearchUsersFromFM")=trim(Request("FM")(1), 2);
Session("dSearchUsersFromFD")=trim(Request("FD")(1), 2);
dTo*triB(Request("TY'l)(1), 4)+trim(Request("TM")(1),2)
+trim(Request("TD")(1), 2);
Session ("dSearchllsersTo")=dTo;
Session("dSearchUsersToTY")=trim(Request("TY")(1), 4);
Session("dSearchUsersToTM")=trim(Request("TM")(1), 2);
SessionC "dSearchllsersToTD")=trim(Request("TD")(1), 2);
dDateCheck=Request("DATECHECK")(1);
Session("dSearchUsersDateCheck")=dDateCheck;
dLogName=Request("LOGNAME")(1);
if(dLogName == "")
dLogName="!i";
SessionC"dSearchUsersLogName"):::dLogName;
dEMail=Request("EHAIL")(1);
if(dEHail == "")
<JEHall="X";
SessionC"dSearchUsersEMail")=dEMail;
!
rs = cmd. ExecuteO;
В зависимости от того, попали ли мы на страницу GetScarchResults.asp по-
сле обновления учетной записи или со страницы поиска этих записей, выпол-
няется извлечение параметров поиска из переменных сеанса или запись их в эти
переменные соответственно. Все это делается для того, чтобы пользователь мог
повторно не вводить параметры поиска, если ему потребовалось их изменить.
Далее параметры поиска передаются хранимой процедуре SearchUsers, выпол-
няющей запрос к базе данных. Результаты этого запроса оображаются в виде
набора записей rs.
На следующем этапе серверный сценарий страницы GetSearchResults.asp
формирует документ HTML, записывая в него параметры поиска и таблицу с
найденными учетными записями. При этом в локальной переменной n C o u n t e r
Глава 4. Связь приложений с базами данных через ADO 1 97
else
I
1=0;
while (Irs. EOF)
{
nSumm += (rs.Fields("Money").value)*100;
nCounter++;
var sID=rs. FieldsC'ClientID"). value;
%>
<Tfl ID="A<!£=sID!6>">
<TD><b><X=rs. Fieldsf "UserlD") . valueKX/TD>
<TDx«=rs . Fields( "UserName" ) . valueXx/TD>
<TD><)(=rs . Flelds( "Money" ) . valueX></TD>
<TD><X=rs.Fields("RegisterDate").valueXx/TD>
<TD><X=rs.Fields( "Email" ).valueXx/TD>
<TD>
<a href="EditUser.asp?ID=<!(=sIDX>" target="main">H3MeHMTb</A>
<a href="order.asp?ID=<X=rs. Fields("UserID"). valueX>" target="main">Kop3MHa</A>
<a href="javascript:trydeluser(<!S=sIDX>, '<X=rs. Fields( "UserName" ).valueX>')"
target="main">yflanMTb</A>
</TD>
<AR>
<x
rs.MoveNextO;
rs.CloseO;
connect.Close();
%>
<тп>
T
<TD COLSPAN="2"><b>Bcero покупателей: <X=nCounterK></b><AD>< D><X=nSumm/100?!>
</TD><TD COLSPAN="3"> </TD>
</TR>
</TABLE>
Для обеспечения возможности работы с дробными значениями денежных
сумм мы вначале умножаем на 100 сумму, полученную от каждого покупателя,
а затем делим на 100 общую сумму.
В каждой строке создаваемой таблицы серверный сценарий располагает ссыл-
ки для изменения или удаления учетной записи, а также для просмотра содер-
жимого корзины покупателя. Операции редактирования учетной записи и про-
смотра корзины выполняются при помощи страниц EditUscr.asp и ordcr.asp. Что
же касается удаления, то для этого мы вызываем функцию клиентского сцена-
рия с именем t r y d e l u s e r . В качестве параметров данной функции передаются
идентификатор пользователя и его имя.
Рассмотрим исходный текст хранимой процедуры SearchUsers (листинг 4-48).
Листинг 4-48 Вы найдете в файле ch4\BookShopScripts\dbo.SearchUsers.PRC
на прилагаемом к книге компакт-диске.
Она выполняет поиск учетных записей покупателей в таблице clients в со-
ответствии с указанными параметрами.
Параметр поиска @DateCheck задает необходимость отбора учетных записей
покупателей по дате их регистрации в базе данных магазина. Если этот параметр
указан как по (это будет в том случае, если в форме поиска снята отметка с пе-
реключателя искать по дате регистрации:), дата регистрации не проверяется.
Если же это не так, то для удовлетворения условиям поиска дата регистрации
должна находится в пределах, заданных параметрами @ f r o m и @to:
CREATE PROCEDURE SearchUsers @from datetime =null,
@>to datetime =null, @Login varchar(20), ©Email varchar(80),
@DateCheck varchar(20) AS
IF ©from IS NULL
BEGIN
SELECT @to=GET[)ATE()
SELECT @from=DATEADD(dd, -7, ^to)
END
Для отбора данных по имени пользователя и его адресу электронной почты
мы применили оператор LIKE, допускающий применение символов шаблона:
SELECT ClientID, USerlD, Password, Language, clients.Money, Status, LastLogin,
LoginCount, UserName, Email, mail, spam, RegisterDate, RegisterIP
FROM clients
WHERE
(@DateCheck='rm' OR (RegisterDate>=@from AND RegisterDate<=®to))
AND
UserlD LIKE @Login
Глава 4. Связь приложений с базами данных через ADO 199
AND
Email LIKE @Email
<а onclick="cancelDeleteAcc()">
<font color="red">OTMeHHTb</fontx/a>
Функция tryDeleteAcc завершает работу модельной диалоговой панели, воз-
вращая значение true:
function tryDeleteAccO
window.returnValue=true;
event.returnValue=false;
window. closeQ;
function cancelDeleteAccO
!
window,returnValue=false;
event.returnValue=false;
window.closeC);
•
Теперь мы снова вернемся к функции t r y d e l u s e r .
Если сотрудник магазина решил продолжить удаление учетной записи, эта
функция создает еще одну модальную диалоговую панель, загружая в нее стра-
ницу trydeleteacc.asp:
var sASP="";
sASP="trydeleteacc.asp?ID=" + sUserlD;
rVal=showModalDialog(sASP, vParm,
"dialogHeight:160px;dialogWidth:350px; status:0");
if(rVal==false)
return;
Страница trydeleteacc.asp пытается удалить учетную запись посетителя из
базы данных. Если это ей удается, мы удаляем средствами DHTML строку с этой
записью из общего списка:
sNode="A"+sUserID;
var oDeletedRow=document.getElementByld(sNode);
oDeletedRow.removeNode(true);
Эта операция выполняется без повторной загрузки списка учетных записей
посетителей в правый фрейм страницы административного приложения, и по-
тому она протекает быстро.
Рассмотрим исходный текст страницы trydeleteacc.asp со сценарием, выпол-
няющим попытку удаления учетной записи из.базы данных (листинг 4-50).
Листинг 4-50 Вы найдете в файле ch4\BookShop\trydeleteacc.asp на прилагае-
мом к книге компакт-диске.
Получив управление, сценарий проверяет текущие права сотрудника, разре-
шая удалять учетные записи посетителей только администраторам. Далее вызы-
вается хранимая процедура DeleteUser, выполняющая попытку удаления. Ей
передается один входной параметр ID (идентификатор пользователя) и один
выходной — DelOK, сообщающий о результате выполнения операции удаления
(для примера мы в этом и некоторых других сценариях передаем методу Append
числовые, а не символьные значения):
connect = Server.CreateObject("ADQDB.Connection"};
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", " " ) ;
cmd.Parameters.Append(DelOk=cmd.CreateParameterf
"DelOk", 3, 3, 4. 0));
cmd. ExecuteO;
После выполнения хранимой процедуры DeleteUser серверный сценарий
анализирует содержимое выходного параметра DelOK и формирует соответству-
ющее сообщение. Кроме того, в зависимости от результата операции создается
один из двух вариантов функции клиентского сценария closeDialogOK, закры-
вающей окно модальной диалоговой панели. Если удаление прошло успешно, эта
функция возвращает значение t r u e , а если нет — false:
DelOk = DelOk.Value;
if(DelOk == 0)
;
x>
<script LANGUAGE="javascript">
<!-
function closeDialogOKC)
;
window.returnValue=true;
event.returnValue=false;
window. closeQ;
;
//->
</script>
etable width="100JT height="100r>
<tr height="50!T>
<td width="100:T align="middle" valign="bottom" colspan="2">
<р>Учетная запись <br><b><script LANGUAGE="javascript">
<!-
document.write(window.dialogArguments[1]);
//->
</scгipt></b><bг>ycпeшнo yдaлaнa</td>
</tr>
<tr height="50S!">
<td widtn="45X" align="middle" valign="top">
<a onclick="closeDialogOK()">
<font color="red">OK</fontx/a></td>
</tr>
</table>
<X
:
else
I
K>
<script LANGUAGE="javascript">
<!-
function closeDialogOKC)
(см. след, стр.)
202 Базы данных в Интернете. Практическое руководство
I
window.returnValue=false;
event.returnValue=false;
window.closet);
;
//->
</script>
<table width="100X" height="100r>
<tr height="50X">
<td width="100V align="middle" valign="bottom" colspan="2">
<р>Невозможно удалить активную учетную запись:<p><b><script
LANGUAGE=="javascript">
document.write(window.dialogArguments[1] j;
//->
</scriptx/b></td>
</tr>
<tr height="50r>
<td width="45J>" align="middle" valign="top">
<a onclick="closeDialogOK(}">
<font color="red">OK</fontx/aX/td>
</tr>
</table>
<%
cmd. Parameters.Append(cmd.CreateParameter(
"ClientID", adVarChar, adParamlnput, 50, ClientID));
var rs;
rs=cmd. ExecuteO;
Идентификатор покупателя (корзину которого мы будем просматривать)
извлекается сценарием из параметра ID и затем передается хранимой процеду-
ре с использованием локальной переменной C l i e n t I D .
Далее сценарий формирует таблицу, содержащую список книг, отобранных
покупателем:
<п2>Содержимое корзины покупателя <X=ClientID!!x/h2>
<TABLE BORDER=1>
<%
while (Irs. EOF)
var ID = cmd.CreateParameter(
"ID", adlnteger, adParamlnput, 4, Request("ID")(1));
cmd.Parameters.Append(ID);
cmd.Executef);
Обратите внимание, как мы инициализируем параметры хранимой процеду-
ры, имеющие отношение к данным различных типов.
Для целых чисел мы указываем константу adlnteger, текстовые строки пере-
даются с использованием константы a d V a r C h a r , а для передачи денежных дан-
ных применяется константа adCurrency. И наконец, отметка о времени переда-
ется с использованием константы adDBTimeStamp.
Значения, полученные от хранимой процедуры, нужны для инициализации
полей формы, предназначенной для редактирования учетной записи посетите-
ля:
<FORM ACTION="UpdateUser.asp" METHOD="post">
<Н2>Редактирование учетной записи посетителя "<S>=UserID.ValueX>"</H2>
<TABLE BORDER=0 CELLPADDING=5 CELLSPACING=0>
<ТР><ТО>Идентификатор</ТОХТО>
<INPUT SIZE=20 TYPE="hidderT NAME="ID" VALUE="a=ID.ValueX>">
<INPUT SIZE=20 TYPE="EDIT" NAME="USR" VALUE="<X=UserID.ValueSi>">
</TDX/TR>
<ТВ><ТО>Пароль</ТОхТО>
<INPUT TYPE="EDIT" NAHE="PWD" VALUE="<!(=Pass.ValueX>">
</TDX/TR><TR><TD>fl3biK</TDXTD>"<X=Language,ValueS>"</TDx^R>
<TRXTD>fleHbrH</TD><TD>"<!«=Money.Valije!(>"</TD></TR>
<TR><TD>flara регистрации</ТО><ТО>
<X
var sRegDate="";
sRegDate=RegisterDate.Value;
if(sRegDate==null)
{ sRegDate="He известна" }
J!>
<K=sRegOateK>
<ADx/TR><TRxTD>Aflpec IP при регистрации</ТО><ТО>
<X
sIP=RegisterIP.Value;
if(sIP==null)
{з1Р="не известен"}
%>
<S;=SIPXX/TDX/TR>
<TRXTD>Aflpec Email</TD><TD>
<x
var sEmail="";
sEmail=Email.Value;
if(sEmail==null)
{sEmail="He известен"}
x>
<INPUT TYPE="EDIT" NAME="EMAIL" VALUE="<X=sEmail!(>">
</TDX/TRXTRXTD> </TDXTD>
<INPUTTYPE="submit" VALUE="06HOBHTb">
</TDX/TRx/TABLE></FORMx/TDx/TRx/TABLE>
206 Базы данных в Интернете. Практическое руководство
var ID=cnid.CreateParameter(
"ID", 3, 1, 4, Request<"ID")(1));
var Login=cmd.CreateParameterC
"Name", 200, 1, 20, Request("USR")(1));
var Pass=cmd.CreateParameterC
"Pass", 200, 1, 20, Request("PWD")(1));
var Email=cmd.CreateParameter(
"Email", 200, 1, 80, Request("EMAIL")(1));
cmd.Parameters.Append(IO);
cmd.Parameters.AppendCLogin);
cmd,Parameters.Append(Pass);
cmd.Parameters.Append(Email);
cmd,Execute();
connect.Close();
После обновления записи мы вновь попадаем на страницу GetSearch-
Results.asp, которая теперь вызывается с параметром MODE, имеющим значение
restart:
Response.Redirect("GetSearchResults.asp?MODE=restart");
Исходный текст хранимой процедуры SetUse rOata представлен в листинге 4-56.
Листинг 4-56 Вы найдете в файле ch4\BookShopScripts\dbo.SetUserData.PRC
на прилагаемом к книге компакт-диске.
Она обновляет поля таблицы clients значениями, полученными через свои
параметры:
CREATE PROCEDURE SetUserData @ID int, @Name varchar(50),
©Pass varchar(SO), @Email varchar(80) AS
UPDATE clients SET UserID=@Name, Password=@Pass, Email=@>Email
WHERE ClientID=@ID
"Comlnit()
::CoUninitialize();
}
} com_init;
Установка соединения с источником данных
Прежде чем обращаться к базе данных, приложение должно установить соеди-
нение с источником данных. Для этого потребуется объект Connection.
В следующем фрагменте кода мы создаем объект C o n n e c t i o n и записываем в
переменную с именем сп указатель на интерфейс объекта:
ADODB;:_ConnectionPtr en = NULL;
HRESULT hr = S_OK;
hr = cn.Create!nstance(__uuidof(ADODB::Connection));
Обратите внимание на то, как мы объявляем переменную сп. Здесь мы ссы-
лаемся на пространство имен ADODB, определенное в результате импорта библио-
теки типов ADO оператором #import. Тип _ C o n n e c t i o n P t r определен как указа-
тель на интерфейс объекта Connection.
Глава 4. Связь приложений с базами данных через АОО 211
ры или имя таблицы базы данных. В нашем случае выполняется оператор SELECT,
выбирающий все данные из таблицы m a n a g e r s .
Второй параметр метода Execute определяет параметры команды. В нашем
случае параметры не используется, поэтому здесь мы указываем специальное
значение vtHissing, отмечающее отсутствующий параметр.
И наконец, третий параметр метода Execute указан как константа A D O D B : : ad-
CmdText. Эта константа определяет, что в первом параметре мы передали мето-
ду Execute строку SQL (а не имя хранимой процедуры или таблицы).
После выполнения метод Execute возвращает указатель на интерфейс набо-
ра записей ADODB: :_RecordsetPtr. Набор записей представляет собой таблицу,
созданную в результате выполнения команды.
Работа с набором записей
Извлечение отдельных записей из набора необходимо выполнять в цикле. Здесь
используется техника, аналогичная той, что применялась нами в сценариях
JScript.
Для проверки условия завершения цикла программа должна анализировать
содержимое свойство EOF объекта Recordset:
while(rs->adoEOF == VARIANT.FALSE)
i
i
Здесь мы, однако, ссылаемся не на свойство EOF, а на переименованное при
импорте библиотеки типов ADO свойство adoEOF. При достижении конца набо-
ра записей это свойство будет содержать значение VARIANT_FALSE.
Извлечение содержимого полей набора записей выполняется следующим
образом:
_variant_t vManagerlD;
_variant_t vNanie;
vManagerlD = rs->Fields->GetItem(_variant_t<"ManagerID"))->Value;
vName = rs->Fields->GetItem(_variant_t("Name"))->Value;
Здесь мы ссылаемся на элемент набора Fields при помощи метода Getltem.
передавая ему в качестве параметра имя столбца, в котором находится нужное
нам поле (можно также задавать номер столбца, начиная с нуля),
После извлечения очередной записи надо передвинуть курсор на следующую
запись набора Recordset, вызвав для этого метод M o v e N e x t :
hr = rs->MoveNext();
if(!SUCCEEDED(hr))
break;
Извлеченные значения размещаются в переменных класса _variant_t. Далее
мы расскажем о том, как выполнить преобразование из этого типа в более при-
вычные числовые и строчные типы данных.
Завершив операции над набором записей Recordset, его следует закрыть при
помощи метода Close:
rs->Close();
Глава 4. Связь приложений с базами данных через ADO 213
_variant_t vPassword(szPassword);
param->Value = vPassword;
cmd->Parameters->Append(param);
При создании выходного параметра мы используем константу A D O D B : : adPa-
ramOutput:
_bstr_t bsParamName2(L"Rights");
param = cmd->CreateParameter(
bsParamName2, ADODB::adVarChar, ADODB::adParamOutput,
16, vtMissing);
param->Value = _variant_t("");
cmd->Parameters->Append(param);
Далее команда выполняется (в нашем случае происходит запуск хранимой
процедуры):
cmd->Execute(&vtMissing, &vtMissing, ADODB::adCmdStoredProc);
После ее завершения значение выходного параметра можно взять из свойства
param->Value:
_variant_t ok = param->Value;
Обработка ошибок
В начале этой главы мы рассказывали о том, как выполняется обработка оши-
бок, возникающих при использовании объектов ADO. Как Вы знаете, ошибки
попадают в ADO от провайдера и помещаются в набор E r r o r s , причем в резуль-
тате выполнения одной команды может возникать сразу несколько ошибок. Для
каждой ошибки создается объект E r r o r , который затем помещается в набор
Errors.
Серверным сценариям JScript доступны только два свойства объекта E r r o r -
number и description, первое из которых содержит числовой код ошибки, авто-
рое — ее текстовое описание.
Программы C++ (так же как сценарии VB Script и программы, написанные
на языке Microsoft Visual Basic) получают доступ ко всем свойствам объекта
E r r o r , перечисленным в таблице 4-4. Это Description (текст сообщения об ошиб-
ке), N u m b e r (код ошибки), S o u r c e (объект, вызвавший появление ошибки),
SQLState (информация об ошибке от источника данных SQL), N a t i v e E r r o r (ана-
логично SQLState), H e l p F i l e (ссылка на файл справочной системы с объяснени-
ем ошибки) и HelpContext (идентификатор раздела справочной системы с опи-
санием ошибки).
Если программа C++ обращается к объектам ADO с применением импорти-
рования библиотеки типов ADO посредством оператора tfimport, то при возник-
новении ошибочной ситуации возникает исключение класса _сот_еггог. Мы об-
рабатываем его с помощью конструкции try-catch следующего вида:
Глава 4. Связь приложений с базами данных через ADO 215
try
hr = en.Createlnstance{ uuidof(ADODB::Connection));
I
catch(_com_error ex)
I
AdoErrHandler(cn);
return;
}
Обработка ошибок выполняется функцией A d o E r r H a n d l e r , которой в качестве
параметра сп передается указатель на интерфейс A D O D B : : Connection.
В начале своей работы функция AdoErrHandler получает набор Errors, содер-
жащий объекты E r r o r , создаваемые для каждой ошибки:
ADODB;;ErrorsPtr Errors = NULL;
Errors= cn->GetErrors();
Этот набор, извлекаемый средствами метода G e t E r r o r s , мы будем обрабаты-
вать в цикле, так как объектов E r r o r может быть создано несколько. Для опре-
деления количества элементов в наборе E r r o r s нужно использовать метод
GetCount:
long nErrCount;
nErrCount = Errors->GetCount();
Далее извлечение объектов E r r o r можно выполнить следующим образом:
ADODB::ErrorPtr Error = NULL;
for(long i = 0; i < nErrCount; i++)
!
Error = Errors->GetItem((_variant_t)((long)i));
Error->Release();
Error = NULL;
}
Здесь переменная цикла i принимает значения от 0 до числа элементов в
наборе E r r o r s . С помощью метода Getltem мы по очереди извлекаем указатели
на интерфейсы объектов Errors, сохраняя их в переменной Error типа ADODB: : Ег-
rorPtr. После использования указатель Error освобождается методом Release,
Затем мы присваиваем ему значение NULL.
Теперь для каждой ошибки (то есть для каждого извлеченного объекта E r r o r )
мы должны получить значения свойств. Эта операция выполняется с использо-
ванием методов, предусмотренных в объекте E r r o r :
CString strNumber;
strNumber.Format("Number:Kx", Error->GetNumber());
Здесь мы извлекли номер ошибки методом G e t N u m b e r , преобразовали его в
текстовую строку и записали в строчную переменную s t r N u m b e r класса CString.
216 Базы данных в Интернете. Практическое руководство
strSource. Format("Source:Xs",
(LPCTSTR)_bstr_t(Error->GetSource())>;
strHelpFile.Formatf"HelpFile: Xs",
(LPCTSTR)_bstr_t(Error->GetHelpFileC)));
Manager list:
Access denied
Рассмотрим исходные тексты программы.
Листинг файла stdafx.h, создаваемого автоматически системой разработки
Microsoft Visual C++, мы не будем приводить ради экономии места. В этот файл
Глава 4. Связь приложений с базами данных через ADO 21 7
мы добавили вручную строки импорта библиотеки типов ADO версии 2.0, как
это показано ниже:
ffimport "d:\program files\common files\system\ado\Msado20.tlb" \
rename ("EOF", "adoEOF")
В листинге 4-57 приведен исходный текст самой программы.
Листинг 4-57 хранится в файле ch4\CPPADOVCPPADO.cpp на прилагаемом к
книге компакт-диске.
Глобальная переменная com_init класса Comlnit предназначена для инициа-
лизации системы СОМ, а также для освобождения ее ресурсов, связанных с
данной программой после завершения ее работы:
struct Comlnit
{
ComlnitQ
;:CoInitialize(NULL);
"ComlnltO
1
; :CoUninitialize();
}
} com_init;
Функция _tmain, получающая управление после запуска приложения, вызы-
вает функцию login, определенную в нашей программе:
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
int nfietCode = 0;
if(!AfxWinInit(::GetModuleHandle(NULL), NULL,
: :GetCommandLine(). 0»
{
cerr « _Т("Ошибка инициализации MFC") « endl;
nRetCode = 1;
I
else
I
if(loginO)
I
cout « _T("\nManager list: \n\rT);
getManagersO;
I
else
coiit « _T("\nAccess denied\n");
I
return nRetCode;
try
hr = cn.CreateInstance(__uuidof(ADODB: :Connection));
if(!SUCCEEDED(hr))
return false;
_bstr_t bsConnString(L"DSN=BookStore");
_bstr_t bsUserID(L"dbo");
_t bsUserPwd(L"");
Затем она запускает хранимую процедуру Manage rLogin, имеющую три пара-
метра. В качестве двух входных параметров процедуре передается идентифика-
тор пользователя и его пароль, а в качестве выходного возвращается строка с
обозначением прав пользователя:
ADQDB::_CommandPtr cmd = NULL;
cmd.CreateInstance(__uuidof(ADODB; : Command));
cmd->ActiveConnection = en;
cmd->CommandType = ADODB: :adCradStoredProc;
_bstr_t bsParamName(L"User'T);
param = cmd->CreateParameter(
bsParamName, ADODB: :adVarChar, ADODB: ladParamlnput,
-1, vtMissing);
CHAR szName[100];
cout « "\nLogin name: ";
Глава 4. Связь приложений с базами данных через ADO 219
cin » szName;
_variant_t vName(szName);
param->Value = vName;
cmd->Parameters->Append(param);
_bstr_t bsParamName1(L"Pass");
param = cmd->CreateParameter(
bsParamNamel, ADOD6: :adVarChar, ADODB: :adParamInput,
-1, vtMissing);
CHAR szPassword[100];
cout « "Password: ";
cin » szPassword;
_variant_t vPassword С szPassword);
param->Value = vPassword;
cmd->Parameters->Append(param);
param = cmd->CreatePararneter(
bsParamName2, ADODB: :adVarChar, ADODB: : adParamOutput,
16, vtMissing);
param->Value = _variant_t("");
cmd->Parameters->Append<param);
if(ok == okAdmin)
return true;
else
return false;
Заметим, что оператор сравнения перегружен в классе _variant_t, поэтому
такая операция выполняется очень просто.
При возникновении ошибочных ситуаций управление передается в блок
catch, где выполняется вызов функции обработки ошибок A d o E r r H a n d l e r :
try
catch(_com_error ex)
{
AdoErrHandler(cn);
return false;
>
Так как ошибки «привязаны» к соединению с источником данных, в качестве
параметра этой функции передается указатель на интерфейс нашего источника
данных сп.
Функция getManagers
Эта функция получает содержимое таблицы m a n a g e r s не с помощью хранимой
процедуры, а выполняя строку SQL с оператором SELECT:
ADODB::_ConnectionPtr en = NULL;
ADODB::_RecordsetPtr rs = NULL;
try
!
HflESULT hr = SJ)K;
hr = cn.CreateInstance( uuidof(ADODB::Connection)};
if(!SUCCEEDED(hr})
return;
_bstr_t bsConnString(L"DSN=BookStore");
_bstr_t bsUserID(L"dbo");
_bstr_t bsUserPwd(L"");
:
В результате выполнения этого оператора создается набор записей класса
Recordset. Он обрабатывается в цикле с использованием приемов, описанных
нами ранее:
_variant_t vManagerlD;
_variant_t vName;
_variant_t vPassword;
_variant_t vLastLogin;
_variant_t vRights;
while(rs->adoEOF == VARIANT_FALSE)
{
vHanagerlD =
rs->Fields->GetItem(_variant_t("ManagerID"))->Value;
vName =
Глава 4. Связь приложений с базами данных через ADO 221
rs->Fields->GetItem(_variant_t("Name"))->Value;
vPassword =
rs->Fields->GetItem(_variant_t("Password"))->Value;
vLastLogin =
rs->Fields->GetItem(_variant_t ("Last Login "))->Value;
vRights =
rs->Fields->Get It em(_variant_t( "Right s"))->Value;
hr = rs->MoveNext();
if<!SUCCEEDED(hr))
break;
CString strRet;
strRet = _T("");
switch(var. vt)
<
(см. след, стр.)
222 Базы данных в Интернете. Практическое руководство
case VT_EHPTY:
case VT_NULL:
strRet = _T("NULL");
break;
case VT_I2:
strRet. Format(_T(''Xhd"),V_I2(&var));
break;
case VT_CY:
strRet = C01eCurrency(var).Format();
break;
case VT_DATE:
strRet = C01eOateTime(var).Format(_T("Xd.)[in.J(y XH:!(M:XS"));
break;
case VT_BSTH:
strRet = V_BSTR( &var );
break;
default:
strRet = _T("not supported");
break;
}
return strRet;
}
Эти функции доступны при использовании библиотеки классов MFC. Но при
необходимости Вы сможете построить функцию, подобную v2str, и без приме-
нения средств MFC.
Вызов ADO через функции Win32
Если Вам больше нравится вызывать интерфейсы элементов ActiveX средства-
ми программного интерфейса \\ in32, то Вы можете обойтись без импортирова-
ния библиотеки типов (хотя этот способ предпочтительнее). В этом разделе мы
расскажем о том, как обращаться к методам и свойствам объектов ADO без при-
менения оператора Jfimport.
Обращение к интерфейсам и методам ADO
Рассмотрим некоторые приемы обращения к интерфейсам и методам ADO, ос-
нованные на использовании функций программного интерфейса Win32, предназ-
наченных для работы с объектами СОМ (таких, как CoCreatelnstance).
Инициализация СОМ и переменных BSTR
Как и в предыдущем случае, для работы с объектами ADO Вам необходимо про-
инициализировать систему СОМ вызовом функции Colnitialize, а перед за-
вершением программы надо освободить ресурсы СОМ при помощи функции
CoUninitialize.
Отказавшись от импортирования библиотеки типов, мы не сможем восполь-
зоваться для создания строк типа BSTR классом _bstr_t. Поэтому нам придется
позаботиться об инициализации и освобождении ресурсов, связанных с такими
строками. Эти операции удобно выполнять вместе с инициализацией и освобож-
Глава А. Связь приложений с базами данных через ADO 223
ADC-Connection* en = NULL;
HRESULT hr = S_OK;
hr = CoCreateInstance(CLSID_CADOConnection, NULL,
CLSCTX_INPROC_SERVER, IID_IADOConnection, (LPVOID*)&cn);
Обратите внимание, что для определения глобальных уникальных идентифи-
каторов ADO, его классов и констант мы включили в исходный текст нашей
программы файлы adoid.h и adoint.h. Файл initguid.h должен быть включен толь-
ко в один файл Вашего проекта,
Создавая объект Connection, функция CoCreatelnstance записывает указатель
на интерфейс этого объекта в переменную сп типа ADC-Connection*. Результат
выполнения операции сохраняется в переменной пг типа HRESULT. Так как при
отказе от импортирования библиотеки типов ADO механизм обработки исклю-
чений _com_error от объектов ADO не используется, Ваше приложение должно
проверять коды завершения вызываемых функций и методов.
После создания объекта C o n n e c t i o n необходимо открыть соединение. Для
этого вначале нужно вызвать метод p u t _ C o n n e c t i o n S t r i n g для записи строки
параметров соединения, а затем вызвать метод Open, определенный в объекте
Connection:
if(SUCCEEDED(hr)}
hr = cn->put_ConnectionString(bstrAccessConnect);
if(SUCCEEDED(hr))
hr = cn->Open(bstrEmpty, bstrEmpty, bstrEmpty,
adConnectUnspecified);
224 Базы данных в Интернеге. Практическое руководство
hr = rs->get_EOF(&bEOF);
if(!SUCCEEDED(hr))
break;
I
Здесь мы проверяем содержимое переменной bEOF, хранящей признак дости-
жения конца набора записей, в начале тела цикла, а обновляем его в конце тела
цикла после получения и обработки очередной записи. Проверку необходимо
выполнить также и перед началом цикла (если, например, в полученном набо-
ре нет ни одной записи).
Извлечение и обработка отдельных записей выполняется следующим образом.
Вначале при помощи метода get_Fields мы получаем указатель на интерфейс
набора Fields:
ADOFields* adoFields = NULL;
hr = rs->get_Fields(&adoFields);
if(!SUCCEEDED(hr))
break;
На следующем этапе мы вызываем через этот интерфейс методы get_Item и
get J/alue для каждого поля обрабатываемой записи:
ADOField* fldManagerlO = NULL;
ADOField* fldName = NULL;
hr = adoFields->get_Item(C01eVariant("ManagerID"),
&fldManagerID);
if{!SUCCEEDED(hr})
break;
hr = fldManagerID->get_.Valiie(&vManagerID);
if(!SUCCEEDED(hr))
break;
hr = adoFields->get_Itern(CQleVariant("Name"), &fldName);
if(!SUCCEEDED(hr))
break;
hr = fldName->get_Value(&vName);
IfOSUCCEEDED(hr))
break;
Здесь мы извлекли содержимое полей идентификатора сотрудника ManagerlD
и его имени Name.
Базы данных в Интернете. Практическое руководство
CString strEmptyC" 1 );
BSTR bstrEmpty;
ComlnitO
:
: :CoInitialize(NUI_L);
bstrEmpty - strEmpty.AllocSysStringQ;
bstrCommand = strCommand.AllocSysString();
vtEmpty.vt = VT_ERROR;
vtEmpty. scode = DISP_E_PARAMNOTFOUND;
vtEmpty2.vt = VT_ERROR;
vtEmpty2. scode = DISP_E_PARAMNOTFOUND;
:
"ComlnitO
{
: :CoUninitialize();
SysFreeString(bstrAccessConnect);
SysFreeSt ring (bstrEmpty);
SysFreeSt ring (bstrCommand);
}
} com_init;
В задачу конструктора класса входит вызов уже знакомой Вам функции
Colnitialize, а также инициализация перечисленных выше переменных классов
BSTR и VARIANT. Деструктор вызывает функцию C o U n i n i t i a l i z e , а затем при по-
мощи функции SysFreeString освобождает ресурсы, выделенные для строк клас-
са BSTR.
Отображение содержимого таблицы managers выполняется функцией getMa-
n a g e r s , получающей управление от функции _tmain после запуска нашей про-
граммы и инициализации библиотеки классов M F C .
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
I
int nRetCode = 0;
if ( ! AfxWinInit( : : GetHoduleHandle(NULL) , NULL,
: ;GetCommandLine(), 0))
else
getManagers();
return nRetCode;
'
В области локальных переменных функции getManagers определены указате-
ли на интерфейсы en, rs и end:
AOOConnection*cn = NULL;
ADQRecordset* rs = NULL;
ADOCommand* cmd= NULL;
В переменную сп записывается указатель на интерфейс C o n n e c t i o n , для чего
используется функция CoCreatelnstance, создающая объект указанного класса:
HRESULT hr = S_OK;
hr = CoCreateInstance(CLSID_CADOConnection, NULL,
CLSCTX_INPROC_SERVER, IID_IAOOConnection, (LPVOID*)&cn);
Базы данных в Интернете. Практическое руководство
hr = adoFields->get_Item(C01eVariant("ManagerID"),
&fldManagerID);
if(!SUCCEEDED(hr))
break;
hr = fldManagerID->get_Value(&vManagerID);
if(!SUCCEEDED(hr))
break;
hr = adoFields->get_Item(C01eVariant("Name"), &fldName);
tf(!SUCCEEDED(hr))
break;
hr = fldName->get_Value(&vName);
if(!SUCCEEDED(hr))
break;
hr = adoFields->get_Item(C01eVariant("Password"), &fldPassword);
if(!SUCCEEDED(hr))
break;
(см. след, стр.)
Базы данных в Интернете. Практическое руководство
hr = fldPassword->get_Value(&vPassword);
if(!SUCCEEDED(hr)>
break;
hr = adoFields->get_Item(C01eVariant(
"LastLogin"), SfldLastLogin);
if(!SUCCEEDED(hr))
break;
hr = fldLastLogin->get_Value(&vLastLogin);
if(!SUCCEEDED(hr)}
break;
hr = adoFields->get^Item(C01eVariant("Rights"), &fldRights);
if(!SUCCEEDED(hr))
break;
hr = fldRights->get_Value<&vRights);
if(!SUCCEEDED(hr))
break;
hr = rs->MoveNext();
if(!SUCCEEDED(hr)>
break;
hr = rs->get_EOF(&bEOF);
if(!SUCCEEDED(hr))
break;
93ак. 3571
232 Базы данных в Интернете. Практическое руководство
Выполнение команды
Для выполнения команды программа должна вызвать метод Execute интерфей-
са ICommandText.
После этого необходимо освободить объект, использованный для выдачи
команды.
В результате выполнения команды может быть создан набор записей, состо-
ящий из строк. По своему назначению этот набор записей аналогичен набору
класса Recordset, создаваемый в приложениях ADO.
Обработка ошибок
Обработка ошибок, возникающих при применении методов OLE DB, намного
сложнее, чем обработка ошибок, связанных с использованием ADO.
234 Базы данных в Интернете. Практическое руководство
Объекты OLE DB
Для работы с OLE DB Ваше приложение должно создать ряд объектов OLE DB,
а затем обращаться к методам и свойствам этих объектов.
Ниже мы рассмотрим некоторые методы и свойства важнейших объектов
OLE DB, необходимые для создания автономных приложений C++, выполня-
ющих запросы к базам данных.
Объект SQLOLEDB
Для установки соединения с источником данных Ваша программа должна со-
здать объект SQLOLEDB, предоставляющий интерфейсы источника данных, а за-
тем выполнить его инициализацию.
Создание объекта
Для создания этого объекта необходимо воспользоваться функцией CoCreate-
Instance, вызвав ее следующим образом:
IDBInitialize* pIDBInitialize = NULL;
HRESULT hr;
hr = CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
IID_IDBInitialize, (void**)&PlQBInitialize);
Глава 5. Связь приложений с базами данных через OLE DB 235
rgInitProperties[1].dwOptions = DBPROPOPTIONS_REQUIRED;
rg!nitProperties[1].colid = DBJIULLID;
Таблица 5-4. Значения свойства DBPROP_INIT_PROMPT
Значение Описание
DBPROMPT_PROHPT Выводит панель приглашения, с: помощью которой
пользователь должен ввести информацию, необходимую
для инициализации источника данных
DBPROHPT_COMPLETE Выводит указанную панель только в том случае, если
требуется внести дополнительные параметры инициали-
зации
DBPROMPT_COMPLETEREQUIRED Аналогично предыдущему, но пользователю не разреша-
ется вводить дополнительную информацию
DBPROMPT__NOPROMPT Панель приглашения не отображается
Что же касается собственно имени источника данных, то оно записывается
в ноле v V a l u e как строка типа BSTR:
VariantInit(&rgInitProperties[1].vValue);
rgInitProperties[1].vValue.vt = VT_BSTR;
rgInitProperties[1].vValue.bstrVal =
SysAllocString(OLESTR("BookStore"));
Аналогичным образом заполняются элементы массива r g l n i t P r o p e r t l e s , за-
дающие имя пользователя и его пароль:
rgInitProperties[2].dwPropertylD -= DBPROP.AUTHJJSERID;
VariantInit(&rgInitProperties[2].vValue};
rg!nitProperties[2].vValue.vt = VT.BSTR;
rg!nitProperties[2].vValue.bstrVal = SysAllocString(OLESTR("dbo"));
rgInitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;
VariantInit(&rgInitProperties[3].vValue);
rgInitProperties[3].vValue.vt = VT_BSTR;
rg!nitProperties[3].vValue.bstrVal = SysAllocString(OLESTR(""));
Свойство, задающее имя пользователя, имеет идентификатор DBPROP_AUTH_-
U S E R I D , а свойство, определяющее пароль пользователя, — идентификатор
DBPROPJUITH_PASSWORD.
Здесь для простоты мы опустили инициализацию полой dwOptions и colid.
Она выполняется аналогично тому, как это делается для элементов массива,
определяющих уровень приглашения и имя источника данных.
На данном этапе мы подготовили массив r g l n i t P r o p e r t i e s структур DBPROP,
содержащий параметры для установки свойств объекта источника данных. Те-
перь нужно задать свойства.
Установка свойств
Задание свойств выполняется методом SetProperties интерфейса IDBProperties.
Этому методу передается указатель па структуру DBPROPSET, который, в свою
очередь, используется для ссылки па только что подготовленный нами массив
структур DBPRQP.
Структура DBPROPSET определена следующим образом:
Глава 5. Связь приложений с базами данных через OLE DB 239
DBPROP * rgProperties;
ULONG cProperties;
QUID guidPropertySet;
} DBPROPSET;
Ее поля описаны в таблице 5-5.
Таблица 5-5. Поля структуры DBPROPSET
Поле Описание
rgProperties Указатель на массив структур DBPROP. Содержимое этого поля иг-
норируется, если в поле cProperties находится нулевое значение
cProperties Количество свойств, для которых выполняется операция записи
или чтения (размер массива структур DBPROP)
guidPropertySet Глобальный уникальный идентификатор GUID набора свойств
Структура DBPROPSET создается и инициализируется очень просто:
DBPROPSET rglnitPropSet;
rglnitPropSet.guidPropertySet = DBPROPSET_DBINIT;
rglnitPropSet.cProperties = 4;
rglnitPropSet.rgProperties = rglnitProperties;
В поле guidPropertySet мы записали константу DBPROPSET_DBINIT, так как наш
набор свойств отвечает за инициализацию источника данных. Массив rglnit-
Properties состоит из четырех элементов, поэтому в поле cProperties записы-
вается значение 4. Что же касается указателя на массив, то мы помещаем его н
иоле rgProperties.
Теперь у нас есть структура, описывающая массив свойств. Чтобы задать
свойства, нам необходимо вызнать метод SetProperties интерфейса IDBProper-
ties. А для этого, в свою очередь, нам потребуется указатель на интерфейс
IDBProperties.
Необходимый указатель мы получаем с помощью функции Q u e r y l n t e r f a c e и
записываем в переменную p I D B P r o p e r t i e s типа IDBProperties*:
IDBProperties* pIDBProperties;
pIDBInitialize->QueryInterface(IID_ID8Properties,
(void**)&pIDBProperties);
Через первый параметр мы передаем функции Q u e r y l n t e r f a c e глобальный
уникальный идентификатор интерфейса. В н а ш е м случае идентификатором
интерфейса I D B P r o p e r t i e s служит значение константы I I D _ I D B P r o p e r t i e s .
Второй параметр служит для передачи указателя на переменную, в которую
будет записан полученный указатель на интерфейс.
Теперь мы можем устанавливать свойства:
HRESULT пг;
hr = pIDBProperties->SetProperties(1, ArglnitPropSet);
Метод S e t P r o p e r t i e s интерфейса I D B P r o p e r t i e s имеет два параметра. Через
второй параметр передается указатель на массив структур типа DBPROPSET, а че-
рез первый — количество таких структур в массиве. В нашем случае подготов-
240 Базы данных в Интернете. Практическое руководство
plDBPraperties->SetProperties(3, &rglnitPropSet);
DBPROPSET
rgProperties
cProperties
guidPropertySet
(Improperly ID
dwOptions
dwStatus
vValiie
Инициализация объекта
Последний этап в инициализации объекта провайдера источника данных — вы-
зов метода Initialize интерфейса IDBInitialize:
if(FAILED((pIDBInitialize)->Initialize()))
,
Объект Session
Прежде чем выдавать команды, нам необходимо создать сеанс как объект Ses-
sion. Этот объект обеспечивает методы для создания команд, наборов записей,
для создания и изменения таблиц и индексов. Он также применяется для пост-
роения объектов транзакций, однако в этой книге мы этот случай не рассматри-
ваем.
Для создания объекта Session нужно получить указатель на интерфейс
I D B C r e a t e S e s s i o n . Для этого применяют метод Q u e r y l n t e r f a c e интерфейса
IDBInitialize, вызвав его следующим образом:
IDBCreateSession* pIDBCreateSession;
pIDBInitialize->QueryInterface(IID_IDBCreateSession,
(void**)&pIDBCreateSession));
В качестве первого параметра мы передаем методу Q u e r y l n t e r f a c e идентифи-
катор интерфейса IDBCreateSession в виде константы IID_IDBCreateSession. В
случае успеха метол Q u e r y l n t e r f a c e записывает указатель на интерфейс
I D B C r e a t e S e s s i o n в переменную, расположенную по адресу, заданному вторым
параметром. В нашем случае указатель на интерфейс IDBCreateSession будет
храниться в переменной pIDBCreateSession типа IDBCreateSession*.
С помощью интерфейса IDBCreateSession и метода CreateSession мы созда-
ем сеанс:
IDBCreateCommand* pIDBCreateCommand;
HRESULT hr;
hr = pIDBCreateSession->CreateSession(NULL, IID^IDBCreateCommand,
(IUnknown**)&pIDBCreateCommand);
Если новый сеанс создается в рамках афетированного объекта, через первый
параметр методу CreateSession передается указатель на управляющий интерфейс
l U n k n o w n . Если же сеанс не является составной частью агрегированного объекта
(как в нашем случае), Вы можете указать здесь значение N U L L .
Второй параметр метода CreateSession предназначен для передачи иденти-
фикатора интерфейса. При создании сеанса мы получаем интерфейс, средства-
ми которого можно создавать команды. Идентификатор этого интерфейса зада-
ется константой IID_IDBCreateCommand.
И наконец, через третий параметр методу CreateSession передается адрес
переменной, в которой будет сохранен указатель на интерфейс создания команд.
После получения интерфейса создания команд IDBCreateCommand мы освобо-
дим ненужный нам более указатель на интерфейс IDBCreateSession с помощью
метода Release:
pIDBCreateSession->Release();
Объект Command
Объект Command необходим для выдачи команд. Он создается при помощи мето-
да CreateCommand интерфейса I D B C r e a t e C o m m a n d .
Создание объекта
Ниже показан фрагмент кода, создающего объект Command:
242 Базы данных в Интернете. Практическое руководство
ICommandText* pICommandText;
hr = plDBCreateCommand->CreateCommand(NULL, IID_ICommandText,
(lUnknown**)&pICommandText);
Рассмотрим параметры использованного здесь метода CreateCommand.
Первый параметр предназначен для передачи управляющего интерфейса
lUnknown при агрегации. Мы не применяем агрегацию, поэтому указываем здесь
значение NULL.
Через второй параметр передается идентификатор интерфейса ICommandText,
необходимого для определения команды.
Третий параметр передает указатель па переменную, в которую будет запи-
сан указатель на только что упомянутый интерфейс ICommandText.
После успешного получения указатель на интерфейс ICommandText мы можем
освободить указатель на интерфейс IDBCreateCommand, который нам больше не
потребуется:
pIDBCreateCommand->Release();
Эта операция выполняется как обычно, при помощи метода Release.
Определение команды
Для определения команды мы используем метод S e t C o m m a n d T e x t интерфейса
ICommandText:
LPCTSTR wSQLString =
OLESTFK"SELECT ClientID, UserlD, Password, RegisterDate, Email FROM clients");
pICommandText->SetCommandText(DBGUID_DBSQL, wSQLString);
Первый параметр метода S e t C o m m a n d T e x t указывает синтаксис команды и
общие правила, которые должен использовать провайдер источника данных в
процессе разбора строки команды. Текст команды задается вторым параметром.
Если первый параметр метода S e t C o m m a n d T e x t задан в виде константы
DBGUIO_SQL (как в нашем примере), то команда интерпретируется в соответствии
с правилами языка SQL.
В том случае, когда этот параметр задан как DBGUID_DEFAULT, интерпретация
выполняется способом, заданным для провайдера источника данных по умолча-
нию. В частности, провайдер OLE DB может по умолчанию выполнять коман-
ды, не имеющие отношения к SQL.
Выполнение команды
После определения команды можно отправить ее на выполнение при помощи
метода Execute интерфейса ICommandText:
LONG cRowsCounter;
hr = pICommandText->Execute(NULL, IID_IRowset, NULL,
&cRowsCounter, (lUnknown**}&pIRowset);
Первый параметр метода Execute используется для агрегирования. Мы зада-
ем его в виде константы N U L L .
Второй параметр задает идентификатор интерфейса IRowset, необходимого
для извлечения результата работы команды в виде набора записей. Метод
Execute записывает указатель на интерфейс IRowset в переменную, адрес кото-
рой определен в последнем параметре.
Глава 5. Связь приложений с базами данных через OLE DB 243
HRESULT hr;
ULONG nColsCount;
DBCOLUMNINFO* pColInfo = NULL;
OLECHAR* pColStringsBuffer = NULL;
hr = pIColumnsInfo->GetColumnInfo(&nColsCount, SpColInfo,
&pCo!StringsBuffer);
Методу GetColurnnlnfo через параметры передаются три указателя на перемен-
ные, в которые будет записана информация о столбцах набора записей.
В переменную nColsCount, указатель на которую передается через первый
параметр, метод G e t C o l u m n l n f o запишет количество столбцов, имеющихся в на-
боре записей, или нулевое значение, если в результате выполнения команды
набор записей не был создан.
Через второй параметр методу G e t C o l u m n l n f o передается адрес переменной,
в которую будет записан адрес массива структур DBCOLUMNINFO, описывающих
столбцы набора.
И наконец, через третий параметр передается указатель на переменную, в
которую будет записан указатель на память для всех строковых значений. Бу-
фер может содержать одну или более строк, закрытых двоичным нулем.
Структура DBCOLUMNINFO определена так:
typedef struct tagDBCOLUMNINFO
{
LPOLESTR pwszName;
ITypelnfo * pTypelnfo;
ULONG iOrdinal;
DBCOLUMNFLAGS dwFlags;
ULONG iJlColumnSize;
DBTYPE wType;
BYTE bPrecision;
BYTE bScale;
DBID columnid;
} DBCOLUMNINFO;
Поля этой структуры описаны в таблице 5-6.
Таблица 5-6. Поля структуры DBCOLUMNINFO
Поле Описание
pwszName Указатель на имя столбца или N U L L , если имя столбца определить не
удалось
pTypelnfo Зарезервировано
iOrdinal Порядковый номер столбца. Нумерация начинается с 1, причем стол-
бец закладки типа bookmark имеет нулевой помер
dwFlags Флаги характеристик столбца. Набор флагов определен в рамках пе-
речисления D B C O L U M N F L A G S
ulColumnSize Максимальная длина значения в столбце
wType Тип данных, расположенных в столбце
bPrecision Максимальная точность численных данных или длина строки, в кото-
рой представлены данные других типов
Глава 5. Связь приложений с базами данных через OLE OB 245
Тип в Microsoft
Тип данных OLE DB SQL Server Описание
DBTYPE STR char Строка символов ANSI, закрытая
varchar двоичным нулем
text
DBTYPE BYTES binary Двоичные данные
varbinary
timestamp
image
DBTYPE_NUMERIC numeric Численное значение, имеющее
decimal фиксированную точность и масштаб.
Описывается структурой DB_NLIMERIC:
typedef struct tagDB.NUMERIC
{
BYTE precision;
BYTE scale;
BYTE sign;
BYTE val[16];
} DB_NUMERIC;
Здесь precision определяет количество
десятичных цифр, scale — количество
цифр справа от десятичной точки,
sign — знак (1 дли положительных чи-
сел и 0 для отрицательных), v a l — ч и с -
ло, занимающее в памяти 16 байт
DBTYPEJJI1 tinyint Целое без знака, занимающее в памяти
1 байт
DBTYPE.I2 smallint Двухбайтовое целое со знаком
DBTYPE.I4 int Целое со знаком размером в -1 байта
DBTYPE.R4 real Значение с плавающей десятичной точ-
кой одинарной точности
DBTYPE.RB float Аналогично предыдущему, по двойной
точ п ости
DBTYPE DATE smalldatetiroe Значение типа double. Целая часть этого
datetime значения содержит количество дней.
прошедших с 30 декабря 1899 года, а
дробная — прошедшую часть текущего
дня
Глава 5. Связь приложений с базами данных через OLE
SHORT year;
LJSHORT month;
USHORT day;
USHORT hour;
USHORT minute;
USHORT second;
ULONG fraction;
} DBTIHESTAMP;
Поля этой структуры хранят значения
года, месяца, дня, количество часов,
минут и секунд, а также дробную часть
секунды соответствен11о
DBTYPE CY smallmoney Значение типа LARGE^INTEGER. Это число
money с фиксированной десятичной точкой,
причем справа от точки указаны четыре
цифры. Хранится как знаковое целое,
занимающее в памяти 8 байт, с масшта-
бом 10 000
OBTYPE UDT Тип данных, Тип данных пользователя может иметь
определенный переменную длину
пользователем
Продолжим процесс извлечения значений из полей набора записей, образо-
ванного в результате выполнения команды.
На данный момент мы извлекли характеристики столбцов набора данных и
готовы выполнить привязку данных.
Подготовка информации для привязки данных
Для выполнения привязки данных мы должны создать массив структур DB8IN-
DING. содержащий информацию о привязке для всех столбцов набора.
Массив создается следующим образом:
DBBINDING* pDBBind = NULL;
pDBBind = new DBBINDING[nColsCount];
Размер массива равен количеству строк в полученном наборе записей, кото-
рое мы определили на предыдущем этапе при помощи метода G e t C o l u m n l n f o
интерфейса I C o l u m n s I n f o .
Определение структуры DBBINDING показано ниже:
typedef struct tagDBBINDING
:
ULONG iOrdinal;
ULONG obValue;
ULONG obLength;
(см. след, стр.)
248 Базы данных в Интернете. Практическое руководство
LJLONG obStatus;
Itypelnfo * pTypelnfo;
DBOBJECT * pObject;
DBBINDEXT * pBindExt;
DBPART dwPart;
DBMEMOWNER dwMemOwner;
DBPARAMIO eParamlO;
U LONG cbMaxLen;
DWORD dwFlags;
DBTYPE wType;
BYTE bPrecision;
BYTE bScale;
DBBINDING;
Как видите, некоторые поля этой структуры называются также как и поля
структуры DBCOLUMNINFO. Они имеют аналогичное назначение. Полное описание
полей данной структуры Вы найдете в таблице 5-9.
Таблица 5-9. Поля структуры DBBINDING
Поле Описание
iOrdinal Порядковый номер столбца. Нумерация начинается с единицы, при-
чем столбец закладки типа bookmark имеет нулевой номер
obValue Смещение значения в буфере потребителя данных. Если буфер дан-
ных содержит строку набора записей, это смещение должно указывать
на поле строки, соответствующее столбцу, заданному в поле i O r d i n a l .
Применяется только в том случае, если в поле d w P a r t установлен фла-
жок DEPART. V A L U E
obLength Смещение значения размера поля в буфере потребителя данных.
Используется только в том случае, если в поле d w P a r t установлен
флажок DBPART_LENGTH
obStatus Смешение значения слова состояния поля в буфере потребителя дан-
ных.
Используется только в том случае, если в поле d w P a r t установлен
флажок DBPART^STATUS
pTypelnfo Зарезервировано
pObject Указатель на структуру типа DBOBJECT. Применяется при работе с по-
лями типа BLOB или с полями, содержащими объекты СОМ. В нашей
книге не рассматривается
pBindExt Указатель на структуру DBBINDEXT, которую можно использовать для
дальнейших расширений структуры привязки данных
dwPart Набор флагов, объединяемых логической операцией ИЛИ и определя-
ющих, какая область буфера пользователя привязана к столбцам или
параметрам. Может принимать значения DBPART_VALUE, DBPART_LENGTH
и DBPART_STATUS
dwMemOwner Способ получения памяти для хранения данных. Может принимать
значения DBMEMOWNER_CLIENTOWNED и DBMEMOWNER_PROVIDEROWNED. В пер-
вом случае за работу с памятью отвечает приложение потребителя
данных, а во втором — провайдер источника данных
Глава 5. Связь приложений с базами данных через OLE DB 249
pDBBind[nCurrentCol].cbMaxLen =
pColInfo[nCurrentCol].ulColumnSize;
cbRow += pDBBind[nCurrentCol].cbMaxLen;
}
На каждой итерации цикла помимо увеличения на единицу номера текущей
строки n C u r r e n t C o l мы извлекаем длину данных текущего столбца из соответ-
ствующего элемента массива с информацией о наборе данных p C o l I n f o . Эта
информация записывается в поле cbMaxLen структуры pDBBind и используется для
увеличения содержимого переменной cbRow, определяющей смещение данных
текущего столбца в буфере.
В теле этого цикла выполняется инициализация других полей массива струк-
тур привязки:
pDBBind[nCurrentCol].iOrdinal = nCurrentCol + 1;
pDBBind[nCurrentCol].obValue = cbRow;
(см. след, стр.)
250 Базы данных в Интернете. Практическое руководство
pDBBind[nCurrentCol].dwPart = DBPART_VALUE;
pDBBind[nCurrentCol3.dwMernOwner = DBMEMOWNER_CLIENTOWNED;
pDBBind[nCurrentCol].eParam!0 = OBPARAMIO_NOTPARAM;
pDBBind[nCurrentCol].bPrecision = pColInfo[nCurrentCol].bPrecision;
pDBBind[nCurrentCol].bScale = pCo!Info[nCurrentCol].bScale;
pDBBind[nCurrentCol].wType = pColInfo[nCurrentCol].wType;
В поле i O r d i n a l записывается номер текущего столбца, увеличенный на еди-
ницу. Это увеличение необходимо потому, что столбцы нумеруются, начиная с
единицы, а начальное значение переменной цикла n C u r r e n t C o l равно нулю.
В поле o b V a l u e записывается текущее смещение данных столбца в буфере
потребителя данных, вычисляемое на каждой итерации цикла с учетом размера
данных, хранящихся в столбце. Чтобы использовать поле o b V a l u e подобным
образом, мы записали в поле d w P a r t константу DBPART_VALUE.
Для того чтобы возложить задачу управления памятью на потребителя дан-
н ы х , мы записываем в поле dwMemOwner константу DBMEMOWNEFLCLIENTOWNED.
Так как наша привязка предназначена для работы с набором данных, а не для
передачи параметров команде, в поле еРагатЮ необходимо записать значение
DBPARAMIO_NOTPARAM.
Инициализация полей bPrecision, bScale и wType массива pDBBind выполня-
ется путем переписывания соответствующих значений из массива pColInfo, со-
держащего информацию о столбцах набора записей, полученного в результате
выполнения команды.
// Обработка строк
// pColInfo[nCurrentCol].wType
Программа OLEDB
В качестве примера программы, написанной на языке C++ и обращающейся к
базе данных средствами OLE DB, приведем исходные тексты простой утилиты
OLEDB, отображающей на консольном экране информацию из таблицы посе-
тителей clients нашего Интернет-магазина.
Программа получает из базы данных и выводит на экран идентификатор за-
писи покупателя, его имя, пароль, дату регистрации и электронный почтовый
адрес E-Mail;
1 frolov 123 01.12.1999 2 0 : 2 3 : 4 2 alexandre@frolov.pp.ru
10 petrov 111 05.12.1999 12:02:12 petrov@some_mail.ru
12 sidorov 1aa 06.12.1999 13:14:44 sidorov@some_mail.ru
Полные исходные тексты утилиты OLEDB приведены в листинге 5-1.
Листинг 5-1 Вы найдете в файле ch5\oledb\oledb.cpp на прилагаемом к книге
компакт-диске.
Рассмотрим эти исходные тексты в деталях.
Глобальные определения
В самом начале файла исходных текстов oledb.cpp мы определили несколько
макросов и включили некоторые include-файлы.
Для того чтобы все строки и символы представлялись в кодировке UNI-
CODE, мы ввели следующие определения:
«define UNICODE
«define .UNICODE
Инициализация констант OLE DB выполняется при помощи определения
макроса DBINITCONSTANTS:
«define DBINITCONSTANTS
И наконец, для работы с глобальными уникальными идентификаторами в
приложении определен макрос INITGUID:
«define INITGUID
254 Базы данных в Интернете. Практическое руководство
"Comlnitf)
: :CoUninitialize();
}
} .com_init;
До начала работы выполняется инициализация СОМ методом Colnitialize,
а до ее завершения — освобождение ресурсен СОМ методом C o U n i n i t i a l i z e ,
В области глобальных переменных мы также определили три указателя на
интерфейсы IMalloc, IDBInitialize и IRowset:
IMalloc* рГМаИос = NULL;
IDBInitialize* pIDBInitialize = NULL;
IRowset* pIRowset = NULL;
Первый из них используется для управления памятью, второй — для иници-
ализации объекта провайдера источника данных, а третий — для работы с извле-
ченным набором записей.
Функция main
Исходный текст этой функции, получающей управление при запуске програм-
мы, представлен ниже:
int main(int агдс, TCHAR* argv[J, TCHAR* envp[]>
if(initO)
if(startCommandO)
get_records();
else
Глава 5. Связь приложений с базами данных через OLE DB 255
if(pIRowset != NULL)
pIRowset->Release();
ifCpIDBInitialize != NULL)
pIDBInitialize->Uninitialize();
pIDBInitialize->Release();
iffpIHalloc != NULL)
pIMalloc->Release();
}
return 0;
VariantInit(&rgInitProperties[3].vValue);
rg!nitProperties[3].vValue.vt = VT_BSTR;
rg!nitProperties[3].vValue.bstrVal = SysAllocString(QLESTR(""));
rg!nitProperties[3].dwPropertyID = DBPROPJMJTH_PASSWORD;
rglnitPropSet.guidPropertySet = DBPROPSET_DBINIT;
rglnitPropSet.cProperties = 4;
rglnitPropSet.rgProperties = rglnitProperties;
pIDBInitialize->QueryInterface(IID_IDBProperties,
(void**}&pIDBProperties);
• • •
hr = pIDBProperties->SetProperties(1, UglnitPropSet);
tf(FAILEO((pIOBInitializ9)->Initialize()))
return false;
Здесь мы привели только фрагмент кода, выполняющий установку свойств,
необходимых для инициализации.
Обратите внимание, что перед вызовом метода инициализации наша програм-
ма освобождает память, заказанную для переменных типа BSTR:
SysFreeString(rglnitPгореrties[1].vValue.bstrVal);
SysFreeString(rgInitProperties[2].vValue.bstrVal);
SysFreeString(rgInitProperties[3].vValue,bstrVal);
После установки значений свойств эти переменные уже не нужны.
Функция startCommand
Функция s t a r t C o m m a n d предназначена для создания сеанса, а также создания и
запуска команды.
Так как мы уже достаточно подробно описали этот процесс, то не будем по-
вторяться. Отметим только, что данная функция запускает команду SELECT, вы-
бирающую из таблицы покупателей clients поля с именами ClientID, UserlD,
Password, RegisterDate и Email:
LPCTSTR wSQLString = OLESTR("SELECT ClientID, UserlD, Password, RegisterDate,
Email FROM clients");
Глава 5. Связь приложений с базами данных через OLE DB 257
Функция get_records
Методику, использованную нами в этой функции для извлечения и обработки
записей набора, полученного в результате выполнения команды SELECT, мы опи-
сали очень подробно.
Здесь же мы остановимся только на процессе получения данных из полей
текущей записи и на выполнении преобразования типов для вывода на консоль.
Обработка полей текущей строки выполняется в цикле:
for(nCurrentCol = 0; nCurrentCol < nColsCount; nCurrentCol++)
{
if(pColInfo[nCurrentCol].wType == DBTYPE_STR)
{
printf("*10s ", &pRowValues[pDBBind[nCurrentCol].obValue]);
!
else if(pColInfo[nCurrentCol].wType == DBTYPE_I4)
I
printf("X5d ", pRowValues[pDBBind[nCurrentCol].obValue]);
I
else if(pColInfo[nCurrentCol].wType == DBTYPE_DBTIMESTAMP)
I
DBTIMESTAMP- ts = (DBTIMESTAMP*)
C&pRowValu9s[pD8BInd[nCurrentCol].obValue]>;
1
prlntf(' X02d.X02d.X04d X02d: K02d: J!02d ",
ts->day, ts->month, ts->year,
ts->hour, ts->minute, ts->second);
printf("\n");
Программа выводит на консоль данные в текстовом виде, однако извлекае-
мые данные могут иметь различный тип в зависимости от столбца. Прежде чем
выполнять преобразование, мы определяем тип данных в текущем столбце, ана-
лизируя соответствующий элемент массива описания столбцов pColInfo. Тип
данных записан в поле wType.
Для разработки кода, выполняющего преобразование, Вам помогут данные из
таблицы 5-8. Анализируя содержимое поля wType, Вы сможете выбрать подхо-
дящий способ преобразования.
Для столбцов таблицы, содержащих текстовые значения, тип данных будет
DBTYPE_STR. В этом случае наша программа передает функции printf , выполня-
ющей преобразование данных и вывод результата на консоль, указатель на дан-
ные.
Если же столбец таблицы содержит числовое значение DBTYPE_I4, мы исполь-
зуем в функции p r i n t f другой спецификатор формата и передаем данные не
через указатель, а непосредственно.
Сложнее дело обстоит с данными типа DBTYPE_DBTIMESTAMP, содержащими
отметку о времени. Для того чтобы получить отдельные составляющие такой
отметки, мы определили в своей программе указатель ts типа DBTIMESTAMP. Да-
лее мы записываем в него значение адреса поля данных с временной отметкой
258 Базы данных в Интернете, Практическое руководство
pIMalloc->Free(pColStringsBuffer);
pIMalloc->Free(pColInfo);
Класс CDataSource
Этот класс инкапсулирует в себе соединение с объектом источника данных OLE
DB. В рамках этого соединения приложение может создавать один или несколь-
ко сеансов.
С применением класса CDataSource инициализация источника данных выпол-
няется так же легко, как и в серверных сценариях, обращающихся к объектам
ADO:
Глава 5. Связь приложений с базами данных через OLE DB 259
CDataSource dsDSN;
HRESULT hr;
hr = dsDSN.Open(_T("MSDASQL"), "Bookstore", "dbo", ""};
if(FAILEDChr))
// Обработка ошибки
Все, что Вам нужно сделать для создания соединения, — вызвать метод Open
класса CDataSource. В данном классе имеется несколько перегруженных определе-
ний метода Open, позволяющих указывать вес или только некоторые параметры.
В нашем случае через первый параметр мы передаем методу Open идентифи-
катор провайдера данных в виде текстовой строки. Другие перегруженные оп-
ределения этого метода позволяют ссылаться на глобальный уникальный иден-
тификатор провайдера CLSID.
Через второй, третий и четвертый параметры методу Open передаются имя
источника данных, имя пользователя и.пароль пользователя соответственно.
При необходимости Вы сможете передать эти параметры и через структуру
DBPROPSET, применяя другой вариант определения метода Open.
Когда работа с источником д а н н ы х закончена, его нужно закрыть, вызвав
метод Close класса CDataSource:
dsDSN.Closed;
В классе C D a t a S o u r c e определено еще несколько методов, которые мы не
используем в нашей книге. К ним относятся методы GetProperties и G e t P r o p e r t y ,
предназначенные для определения свойств соединения с провайдером, метод
G e t l n i t i a l i z a t i o n s t r i n g , позволяющий получить строку инициализации источ-
ника данных (включая пароль) и другие методы, предназначенные для соедине-
ния с источником данных.
Класс CSession
Для создания сеанса, необходимого для работы с командами и наборами запи-
сей, Вы должны использовать класс CSession.
Это очень просто:
CSession sSession;
hr = sSession.Open(dsDSN);
if(FAILEO(hr))
:
dsDSN.Closef);
// Обработка ошибки
\
Вам необходимо вызвать метод Open класса CSession» передав ему в качестве
параметра ссылку па предварительно открытый объект класса C D a t a S o u r c e .
По завершении работы с источником данных приложение должно закрыть все
открытые сеансы методом Close:
sSession.Close();
Помимо методов Open и Close, в классе CSession определено несколько мето-
дов для работы с транзакциями. Это S t a r t T r a n s a c t i o n (начало транзакции),
260 Базы данных в Интернете. Практическое руководство
BEGIN_COLUMN_MAP(tabClients)
COLUMfLENTRY(1, m_ClientID)
COLUMN_ENTRY(2, m_UserID)
COLUMN_ENTRY(3, m_Password)
COLUMN_ENTRY(4, m_RegisterDate)
COLUMN_ENTRY(5, m_Email)
END_COLUMN_MAP()
};
Этот класс определяет всю информацию, необходимую для выполнения при-
вязки. Фактически он содержит описание полей набора данных, который будет
образован после выполнения запроса к базе данных. В нашем случае это табли-
ца clients, входящая в состав базы данных Интернет-магазина, о которой мы уже
рассказывали ранее (поэтому, кстати, мы и выбрали для класса привязки имя
tabClients), Заметьте, мы не создаем объектов класса tabClients, нам нужно
только его определение.
Глава 5. Связь приложений с базами данных через OLE DB 261
hr = cmd.Qpen(sSession, mySQL);
ifCFAILED(hr))
{
sSession.Closet);
dsDSN.CloseO;
// Обработка ошибки
}
Этот метод очень прост в применении. Достаточно передать ему в качестве
первого параметра ссылку на открытый сеанс, а в качестве второго — адрес тек-
стовой строки команды, подлежащей выполнению.
В результате выполнения команды создается набор записей, доступ к кото-
рому осуществляется с помощью все того же класса CCommand. Для этого в про-
грамме необходимо организовать цикл:
while(cmd.MoveNext() == S_OK)
I
// cmd.m_ClientID;
// cmd.m_UserID;
// cmd.m_Password;
// cmd.m_Email;
// cmd.m_RegisterOate;
>
Здесь мы перебираем записи образованного набора с помощью метода MoveNext,
определенного в классе CCommand.
Для доступа к значениям полей достаточно просто сослаться на соответству-
ющие поля класса CCommand. Это возможно благодаря применению библиотеки
шаблонов ATL.
По завершении цикла обработки записей приложение должно закрыть объект
класса CCommand, сеанс и соединение с источником данных. Все это делается ме-
юдами Close соответствующих классов:
cmd.CloseO;
sSession.CloseC);
dsDSN.CloseO;
262 Базы данных в Интернете. Практическое руководство
Программа ATLOLEDB
Для демонстрации простоты использования объектного интерфейса OLE DB с
применением библиотеки шаблонов ATL мы подготовили консольную програм-
му ATLOLEDB, Она решает ту же задачу, что и предыдущая, рассмотренная в
этой'главе — отображает содержимое нескольких полей таблицы регистрации
посетителей Интернет-магазина clients.
Полный исходный текст программы ATLOLEDB Вы найдете в листинге 5-2.
Листинг 5-2 хранится в файле ch5\atloledb\atloledb.cpp на прилагаемом к кни-
ге компакт-диске.
Глобальные определения
Для того чтобы использовать шаблоны ATL, предназначенные для работы с
объектами OLE DB, мы включили в исходный текст нашего приложения файл
atldbcli.h:
«include <atldbcli.h>
Так как наша программа выводит результаты на консоль и при этом пользу-
ется манипуляторами ввода/вывода, мы включили файлы iostream и iomanip:
flinclude <iostream>
ttinclude <iomanip>
Кроме того, для применения ATL необходимо подключить пространство имен
std:
using namespace std;
В области глобальных определений мы расположили определение класса
привязки переменных к набору записей tabClients:
class tabClients
{
public:
TCHAR m_ClientID[6];
TCHAR m_UserID[50];
TCHAR m_Password[50];
DBTIMESTAMP m_RegisterDate;
TCHAR m_Email[80];
BEGIN_COLUMN_MAP(tabClients)
COLUHN_ENTRY(1. m_ClientIO)
COLUMN_ENTRY(2, mJJserlD)
COLUMN_ENTRY(3. m_Password)
COLUMN_ENTRY(4, m_RegisterDate)
COLUMN_ENTRY(5, m_Email)
END_COLUMN_MAP()
};
О назначении и внутреннем устройстве этого класса мы рассказывали ранее.
Помимо этого, в области глобальных определений мы разместили три объекта
классов CDataSource, CSession и CCommand:
Глава 5. Связь приложений с базами данных через OLE DB 263
CDataSource dsOSN;
CSession sSession;
CCommand <CAccessor<tabClients> > cmd;
Объект dsDSN используется для создания соединения с источником данных,
объект sSession нужен для образования сеанса, а объект cmd — для выдачи ко-
манды и обработки результата се выполнения.
Функция main
Наша программа настолько проста, что все свои действия она выполняет в рам-
ках единственной функции main.
В начале своей работы программа выполняет инициализацию СОМ, вызы-
вая для этого функцию Colnitialize:
CoInitialize(NULL);
Заметим, что освобождение ресурсов выполняется ав-пшатически, поэтому
при использовании шаблонов ATL функцию CoUninitialize вызывать не надо
(и нельзя).
После инициализации пата программа открывает источник данных и сеанс:
HRESULT hr;
Пг = dsDSN.Open(JVMSDASQL"), "Bookstore", "dbo", "");
iffFAILED(nr)}
return 1;
hr = sSession.Open(dsDSN);
if(FAILED(hr))
{
dsDSN.CloseO;
return 1;
hr = cmd.OpenfsSession, mySQL);
if(FAILED(nr)}
{
sSession.Close();
dsDSN.CloseO;
return 1;
}
В качестве команды мы используем строку SQL, выполняющую запрос к таб-
лице покупателей clients.
Записи набора, образованного в результате выполнения этой команды, обра-
батываются в цикле:
Ю З а к . 3571
264 Базы данных в Интернета. Практическое руководство
while(cmd.MoveNext() == S_OK)
:
cout « setw(4) « setiosflags(ios::left) « cmd.m_ClientID;
cout « setw(IO) « cmd.m_UserID;
cout « setw(IO) « cmd.m_Password;
cout « setw(20) « cmd.m_Email;
DBTIMESTAMP ts == cmd.m_RegisterDate;
char szBuf[256];
wsprintf(szBuf, "K02d.!(02d.Xu4d X02d:X02d:X02d ",
ts.day, ts.month, ts.year, ts.hour, ts.minute, ts.second);
Инициализация
Для выполнения инициализации среды выполнения приложение ODBC исполь-
зует такие функции, как SQLAllocHandle и SQLSetEnvAttr. Первая из них отвеча-
ет за собственно инициализацию, а вторая позволяет устанавливать параметры
среды исполнения.
Вот прототип функции S Q L A l l o c H a n d l e :
SQLRETURN SOLAllocHandle(SQLSMALLINT hType,
SQLHANDLE inpHandle, SQLHANDLE* outpHandlePtr);
Глава 6. Связь приложений с базами данных через ODBC 267
Установка соединения
Для установки соединения с источником данных Вам придется воспользовать-
ся функцией SQLConnect, прототип которой приведен ниже:
SQLRETURN SQLConnectCSQLHDBC hConnection,
SQLCHAR * ServerName, SQLSMALLINT ServerNameLength,
SQLCHAR * UserName, SQLSMALLINT UserNameLength,
SQLCHAR * Password, SQLSMALLINT PasswLength);
Через первый параметр h C o n n e c t i o n функции SQLConnect передается иденти-
фикатор соединения типа SQL_HANDLE_DBC, полученный на втором этапе иници-
ализации,
Второй параметр ServerName определяет имя сервера, заданное в виде тексто-
вой строки, а третий (с названием ServerNameLength) — длину этой строки. Ана-
логично параметры U s e r N a m e и Password задают имя пользователя и его пароль,
а параметры U s e r N a m e L e n g t h и PasswLength — размер строк имени пользователя
и пароля.
Вот фрагмент текста программы, выполняющий соединение с источником
данных BookStore:
UCHAR szDSN[SQL_MAX_DSN_LENGTH + 1] = "BookStore";
UCHAR szUserName[MAXNAME] = "dbo";
UCHAR szPasswordfMAXNAME] = "";
re = SQLConnect(hObc,
szDSN, (SWORD)strlen((const char*)szDSN),
Глава 6. Связь приложений с базами данных через ODBC 269
struct tagTIME_STRUCT
'
SQLUSMALLINT hour; // чась
SQLUSMALLINT minute; // минуты
SQLUSMALLINT second; // секунды
} TIME^STRUCT;
Struct tagTIMESTAMP_STRUCT
I
SQLSMALLINT year; // год
SQLUSMALLINT month; // месяц
SQLUSMALLINTday; // день
SQLUSMALLINT hour; // часы
SQLUSMALLINT minute; // минуты
SQLUSMALLINT second; // секунды
SQLUINTEGER fraction; // доли секунды
} TIMESTAMP_STRUCT;
Struct tagSQL_NUMERIC_STRUCT
struct tagSQLGUID
<
DWORD Datal;
WORD Data2;
WORD Data3;
BYTE Data4[8];
} SQLGUID;
Обработка ошибок
Как мы уже говорили, в случае возникновения ошибок функции ODBC возвра-
щают значения, такие, как SQL_INVALID_HANDLE или SQL_ERROR. Однако в большин-
стве случаев такой информации недостаточно.
Для того чтобы получить развернутое описание ошибки, необходимо запро-
сить у драйвера ODBC диагностические записи. Эта операция выполняется с
применением функции SQLGetDiagRec. Отдельные поля диагностических запи-
сей извлекаются при помощи функции SQLGetDiagField.
while(rc != SQL_NO_.DATA_FOUNO)
Глава 6. Связь приложений с базами данных через ODBC 275
гс = SQLGetOiagRecCnHandleType, sqlhHandle,
nRecordNuinber, szSQLState, &sdwNativeError,
szErrorMessage, 255, SpcbErrorMessage);
nRecorcfNumber++;
:
Записи состояния
Указанным выше методом можно получить первый из двух типов диагностичес-
ких записей, а именно записи заголовка. Они создаются при возникновении
любых ошибок, кроме тех, что вызывают появление кода завершения SQL_IN-
VALID_HANDLE.
При использовании драйверов ODBC версии 3.x становятся доступными
диагностические записи второго типа, называемые записями состояния. Они
генерируются отдельно для каждой записи заголовка и могут быть получены
посредством функции S Q L G e t D i a g F i e l d :
SQLRETURN SQLGetDiagField(
SQLSMALLINT hType,
SQLHANDLE hHandle,
SQLSMALLINT nRecord,
SQLSMALLINT nDiagld,
SQLPOINTER pDiaglnfo,
SQLSMALLINT nBufferSize,
SQLSMALLINT* pStringSize);
Через первый параметр hType Вы должны передать функции SQLGetDiagField
•сип идентификатора, для которого нужно получить диагностику. Это констан-
ты SQL_HANDLE_ENV, SQL_HANDLE_DBC, SQL_HANDLE_STMT и SQL_HANDLE_DESC.
Параметр h H a n d l e используется для передачи идентификатора.
Через параметр функции SQLGetDiagField передают номер записи заголовка,
для которой извлекаются записи состояния. Это тот самый номер, что переда-
ется через одноименный параметр функции SQLGetDiagRec.
Параметр n D i a g l d позволяет указать идентификатор извлекаемой записи со-
стояния и задастся в виде одной из предопределенных констант.
С помощью параметра p D i a g l n f o Вы должны передать функции указатель на
буфер, к которую будет записана диагностическая информация. Размер этого
буфера задается параметром n B u f f e r S i z e . Значение n B u f f e r S i z e игнорируется в
том случае, если указатель p D i a g l n f o ссылается на буфер данных фиксирован-
ного размера.
Для текстовых строк допускается также указывать в параметре n B u f f e r S i z e
константу SQL_NTS. Она означает, что буфер содержит текстовую строку, закры-
тую двоичным нулем.
И наконец, в переменную, адрес которой задается параметром pStringSize,
записывается количество символов в извлеченном диагностическом сообщении
(без учета двоичного нуля, закрывающего текстовую строку).
Вот фрагмент программы, в котором для записи заголовка извлекаются за-
писи состояния:
276 Базы данных в Интернете. Практическое руководство
гс = SQLGetDiagRec(nHandleType, sqlhHandle,
nRecordNumber, szSQLState, SsdwNativeError,
szErrorMessage, 255, &pcbErrorMessage);
if(re != SQL_NO_DATA_FOUND)
<
re = SQLGetDiagField(nHandleType, sqlhHandle, nRecordNumber,
SQ[__DIAG_ROW_NUMBER, &nRowNumber, SQL_IS_INTEGER, NULL);
re = SQLGetDiagField(nHandleType,.sqlhHandle, nRecordNumber,
SQLJ)IAG_SS_SEVERITY, &sdwSeverity, SQL_IS_INTEGER, NULL);
Программа ODBCAPP
Программа ODBCAPP, исходные тексты которой мы описываем в этом разде-
ле, выводит на консольный экран все записи из файла m a n a g e r s базы данных
нашего Интернет-магазина:
1 | frolov | 123 | A d m i n i s t r a t o r | 21.12.1999 11:24:44
2| Petrov I 123 | sh_manager | (not logged yet)
3 | Sidoroff | 123 | Sales_manager | (not logged yet)
5 | TestAdmins j 1234 | Sales_manager | (not logged yet)
4| admin | 123 | Sales.manager | 05.12.1999 09:50:09
Глава 6. Связь приложений с базами данных через ODBC 277
Функция main
Функция m a i n выполняет инициализацию, установку соединения, создание и
запуск команды, извлечение и вывод на экран результатов ее выполнения.
В области локальных переменных функции main мы определили переменную
гс, предназначенную для храпения кода возврата функций программного интер-
фейса ODBC, три строки, необходимые для подключения к источнику данных
(содержащих имя источника данных, им пользователя и пароль), а также стро-
ку класса s t r i n g для хранения сообщения об ошибке:
RETCODE гс;
UCHAR szDSN[SQL_MAX_OSN_LENGTH + 1] = "BoOkStore";
LJCHAR szUserNameCMAXNAME] = "dbo";
UCHAR szPasswordtMAXNAME] = "";
string sErrMsg;
Получив управление, функция main инициализирует среду выполнения про-
граммы, получая соответствующие идентификаторы:
гс = SQLAllocHandle(SQL_HANDLE_ENV, N U L L , &hEnv);
re = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
else
I
printf("Ud | X10s | K10s | 3S15s | 5602d.i(02d.X04d X02d: K02d:
nManagerlD, szName, szPass, szRights,
tsLastLogin.day, tsLastLogin.month, tsLastLogin.year,
tsLastLogin.hour, tsLastLogin.minute, tsLastLogin.second);
.
280 Базы данных в Интернете. Практическое руководство
if(re != SQL_NO_DATA_FOUND)
{
re = SQLGetDiagField(nHandleType, sqlhHandle, nRecordNumber,
SQL_DIAG_ROW_NUMBEfl, &nRowNumber, SQL_IS_INTEGER, NULL);
nRecordNumber++;
Ргос Name:
Server Name: FROLOV
Если же ошибка — в имени столбца, текст сообщения будет другим:
SQLExecDirect Error
SQL State: 42S22
Native Error: 207
Error Message: [Microsoft][ODBC SQL Server Driver][SQL Server]Invalid column
name 'ManagerlDv'.
ODBC Row Number: 1
Message State: 3
Severity: 16
Proc Name:
Server Name: FROLOV
Как видите, диагностика ошибки достаточно полная.
Функция GetErrorMsg
Она вызывается для обработки ошибок, возникающих на ранней стадии рабо-
ты программы до момента установления соединения с источником данных.
Внутри этой функции находится цикл, н котором выполняется извлечение
заголовки диагностических записей при помощи функции SQLGetDiagRec:
while(rc != SQL_NO DATA_FOUND)
re = SQLGetDiagRec(nHandleType, sqlhHandle,
nRecordNumber, szSQLState, SsdwNativeError,
szErrorMessage, 255, SpcbErrorMessage);
nRecordNumber++;
}
Эта функция также формирует в переменной s E r r M s g итоговое сообщение об
ошибке.
Бог, например, какое сообщение появится на экране, если указать неправиль-
ное имя источника данных:
SQLConnect error
SQL State: IM002
Native Error: 0
Error Message: [Microsoft][ODBC Driver Manager] Data source name not found
and no default driver specified
Глава 6. Связь приложений с базами данных через ODBC 283
Привязка параметров
Запуск процедур выполняется примерно таким же образом, что и запуск команд
при помощи функции SQLExecDirect. Вы должны сформировать команду в виде
шаблона хранимой процедуры и затем запустить ее.
Однако прежде чем рассказать о шаблонах х р а н и м ы х процедур, мы займем-
ся привязкой входных и выходных параметров, передаваемых процедуре при
запуске. В ходе этой операции параметры процедуры привязываются к локаль-
ным переменным, определенным в программе.
Для привязки параметров хранимых процедур Вы должны использовать
функцию S Q L B i n d P a r a m e t e r , прототип которой приведен ниже:
SQLRETURN SQLBindParameter(
SQLHSTMT hStatement,
SQLUSMALLINTnParameter,
SQLSMALLINT nlnputOutputType,
SQLSMALLINT nValueType,
SQLSMALLINT nParameterType,
SQLUINTEGER nColumnSize,
SQLSMALLINT nDecimalDigits,
SQLPOINTER pParameterValue,
SQLINTEGER cbBufferSize,
SQLINTEGER* pcbParamSize);
Через параметр hStatement программа должна передать функции SQLBind-
Parameter предварительно созданный идентификатор команды запуска процедуры.
Параметр n P a r a m e t e r задает последовательный номер параметра процедуры,
который будет привязан к локальной переменной. Нумерация параметров начи-
нается с единицы.
С помощью параметра n l n p u t O u t p u t T y p e функции S Q L B i n d P a r a m e t e r переда-
ется одно из следующих значений, определяющих направление передачи дан-
ных: SQL_PARAM_INPUT (входной параметр), SQL_PARAM_OUTPUT (выходной параметр)
или SQL_PARAM_INPUT_0'UTPUT (входной и выходной параметр).
284 Базы данных в Интернете. Практическое руководство
гс = SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, 50, 0,
szAdminName, 51, &cbAdminName);
SQLCHAR szAdminPass[51];
SQLINTEGER cbAdminPass = SQL_NTS;
re = SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, 50, 0,
szAdminPass, 51, ScbAdminPass);
SQLCHAR szAdminRights[51];
SQLINTEGER cbAdminRights = SQL_NTS;
re = SQLBindParameter(hStmt, 3, SOL_PARAM_OUTPUT,
SQL_C_CHAR, SQL..CHAR, 16, 0,
szAdminRigtits, 51, ScbAdminRights);
Первый и второй параметры — входные. Они привязываются функцией
S Q L B i n d P a r a m e t e r с помощью константы SQL_PARAM_INPUT. Для третьего, выход-
ного параметра мы указали Константу SQL_PARAM_OUTPUT.
Все наши параметры являются текстовыми строками. Локальные перемен-
ные, которые мы будем к ним привязывать, расположены в массивах символов
типа SQLCHAR. Соответственно через четвертый и пятый параметры мы переда-
ем функции S Q L B i n d P a r a m e t e r константы SQL_C_CHAR и SQL_CHAR.
Обратите внимание, как мы указываем количество символов в параметре
(значение nColumnSize функции SQLBindParameter). Входные параметры пред паз-
Глава 6. Связь приложений с базами данных через ODBC 285
Запуск процедуры
Как мы уже говорили, для запуска на выполнение хранимых процедур можно
воспользоваться уже знакомой Вам функцией SQLExecDirect.
Ниже показан фрагмент кода, в котором запускается процедура с тремя па-
раметрами:
re = SQLExecDirect(hStmt,
(unsigned спагО'ЧсаП ManagerLogin(?, ?,?)}", SQL_NTS);
Обратите внимание, что через второй параметр мы передали функции SQLExec-
Direct строку шаблона хранимой процедуры, в которой параметры отмечены
символом <<?».
Помимо параметров процедура может возвращать значение. Для такой про-
цедуры следует подготовить шаблон следующего вида:
{? = call ProcName(?,?,?)>
Напомним, что при выполнении привязки параметров хранимой процедуры
функции SQLBindParameter необходимо указать номер привязываемого параметра
nParameter. Если хранимая процедура возвращает значение, то для привязки
локальной переменной к этому значению необходимо указать номер 1. При этом
остальные параметры процедуры нумеруются, начиная со значения 2.
При успешном запуске хранимой процедуры функция SQLExecDirect возвра-
щает не только значения SQL_SUCCESS и SQL_SUCCESS_WITH_INFO, но и SQLJJO_DATA.
Код завершения SQL_NO_DATA означает, что при выполнении процедуры не был
создан набор записей. В зависимости от того, какие действия выполняет храни-
мая процедура, данную ситуацию можно считать нормальной или ошибочной.
Например, если процедура обновляет или удаляет записи в базе данных или в
зависимости от тех или иных условий возвращает выходные параметры, не из-
меняя базу данных, набор записей может и не создаваться.
Программа ODBCPARAM
Применение описанных выше методик вызова хранимых процедур с параметра-
ми демонстрируется на примере консольной программы ODBCPARAM.
Программа ODBCPARAM запрашивает у пользователя идентификатор и
пароль, выполняет запрос к таблице m a n a g e r s базы данных Bookstore и затем
отображает права пользователя:
Login name: frolov
Password: 123
гс = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION.
(SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
re = SQLConnect(hDbc,
szOSN, (SWORD)strlen((const char*)szDSN),
szUserName, (SWORD)strlen({const char*)szUserName),
szPassword, (SWORD)strlen((const char*)szPassword));
Обработка ошибок опущена для краткости. Она выполняется точно таким же
образом, как и в программе ODBCAPP.
На следующем этапе мы запустим хранимую процедуру M a n a g e r L o g i r t , с ко-
торой Вы познакомились в четвертой главе нашей книги. Для удобства мы по-
вторим исходный текст этой процедур],!, немного изменив его:
CREATE PROCEDURE ManagerLogin @User varchar(50), ©Pass varchar(50), ©Rights
varchar(16) output AS
SELECT §Rights="nothing"
SELECT @Rights=Rights FROM managers WHERE Name=@User AND Password=@Pass
UPDATE managers SET LastLogin=GETDATE() WHERE Name=@User
Здесь откорректировано значение, возвращаемое в случае отсутствия запи-
си сотрудника в базе данных. Вместо пустой строки теперь возвращается стро-
ка « n o t h i n g » .
Как видите, процедура M a n a g e r L o g i n имеет два входных и один выходной
параметры. Получив управление, она проверяет существование записей в таб-
лице m a n a g e r s . Если таблица пуста, в ней создается новая запись с правами адми-
нистратора. Это нужно для начальной настройки системы при первом запуске.
В ходе дальнейшей работы процедура M a n a g e r L o g i n ищет в таблице m a n a g e r s
запись для сотрудника магазина, идентификатор и пароль которого был передан
ей через параметры @User и @Pass. Если такая запись найдена, права сотрудни-
ка переписываются в выходной параметр ©Rights. Если же записи нет, в этой па-
раметр записывается строка « n o t h i n g » .
Перед тем как запустить процедуру, нам нужно создать идентификатор ко-
манды и привязать три параметра, предназначенные для передачи имени, паро-
ля и прав сотрудника.
Создание идентификатора команды выполняется, как и раньше, функцией
SQLAllocHandle:
гс = SQLAUocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
Далее программа вводит с консоли и привязывает к соответствующему па-
раметру хранимой процедуры ManagerLogin строку szAdminName, содержащую имя
сотрудника:
SQLCHAR szAdminName[51];
SQLINTEGER cbAdminName = SQL_NTS;
re = SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, 50, 0,
szAdminName, 50, ScbAdminName);
Аналогичным образом выполняется ввод пароля и привязка переменной
szAdminPass, предназначенной для хранения пароля:
SQLCHAR szAdminPass[51];
SQLINTEGER cbAdminPass = SQLJITS;
printf("\nPassword: ");
gets((char*)szAdminPass);
re = SQLBindParameter(hStmt, 3, SQL_PARAM_OUTPUT,
SQL_C_CHAR, SQL_CHAR, 16, 0,
szAdminRights, 16, ScbAdminRights);
К нему привязывается массив s z A d m i n R i g h t s . Именно сюда будет записан
результат работы хранимой процедуры M a n a g e r L o g i n .
Для запуска хранимой процедуры на выполнение мы вызываем функцию
S O L E x e c D i r e c t , передавая ей идентификатор команды и шаблон процедуры
ManagerLogin:
гс = SQLExecDirect(hStmt,
(unsigned char*)"{call ManagerLogin{?,?,?)}", SQL_NTS);
if( re == -1)
break;
.
После того как программа убедится в отсутствии наборов записей (что в на-
шем случае не требуется делать, так как процедура M a n a g e r L o g i n не создает ни-
каких наборов), она выводит консоль полученные права сотрудника:
p r i n t f ( " \ n Y o u r rights: Ks\n", szAdminRights);
Перед завершением работы программа освобождает идентификатор команды,
отключается от источника данных, а затем освобождает идентификаторы источ-
ника данных и среды исполнения;
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
SQLDisconnect(hDbc);
SQLFreeHandle(SQL_HANDLE_DBC, hObc);
SQLFreeHandle(SQL_HAMDLE_ENV, hEnv);
Г Л А В А 7
Заметим, что для отправки формы на сервер Web можно использовать гра-
фическую кнопку типа IMAGE. Изображение такой кнопки задается парамет-
ром SRC.
Когда пользователь щелкает графическую кнопку, программа CGI получает
от нее координаты точки, в которой находился курсор мыши в момент щелчка.
Таким образом, возможно создание кнопки в виде сегментированного графичес-
кого изображения. Программа OGI при этом сумеет определить, в какой облас-
ти изображения был сделан щелчок мышью при отправке заполненной формы
на обработку.
Метод GET
Метод GET предполагает передаму данных программе CGI через переменные сре-
ды (environment variable). Это те самые переменные среды, которые устанавли-
ваются в операционной системе MS-DOS командой SET.
Сервер Web создает для программы CGI довольно много переменных среды.
Имена и назначение всех этих переменных Вы узнаете позже, а пока мы расска-
жем только о самых необходимых.
Прежде всего, метод GET предполагает использование переменной среды с
именем QUERY_STRING. Именно сюда попадают данные из полей формы. Эти дан-
ные находятся в следующем формате:
"Имя1=Значение1&Имя2=Значение2&ИмяЗ=ЗначениеЗ"
Здесь в качестве имен используются значения параметров NAME, задающих
имена полей формы. Вместо значений подставляются данные из соответствую-
щих полей.
Сканируя содержимое текстовой строки переменной среды Q U E R Y _ S T R I N G ,
программа CGI найдет в ней имя любого нужного поля и соответствующее это-
му имени значение. Заметим, что если переключатель не отмечен, то никакие
данные от него не передаются. Поэтому не следует думать, что в полученной
строке Вы обязательно встретите имена всех полей, расположенных в форме.
Адрес строки любой переменной среды в программе, составленной на С, легко
получить с помощью функции getenv:
char * szQueryString;
szQueryString = getenv("QUERY_STRING");
Заметим, что если Вы собираетесь модифицировать строку переменной сре-
ды, то ее следует скопировать во внутренний буфер. Операционная система сер-
вера Web иногда не допускает прямого редактирования блока памяти, содержа-
щего переменные среды.
Глава 7. Расширения CGI и ISAPI сервера Web 293
Сразу за ней необходимо вывести еще одну пустую строку, которая послужит
разделителем между заголовком HTTP и данными документа HTML.
Ниже мы привели фрагмент кода, в котором программа CGI динамически
формирует документ HTML и выводит его в стандартный поток вывода:
printf("Content-type: text/html\n\n");
printf("<!DOCTYPE HTML PUBLIC V-//W3C//DTD HTML 3.2//EN\">");
printf("<HTML><HEAD><TITLE>XYZ Incorporation</TITLE></HEAD>"
•XBODY BGCOLOR=#FFFFFF>");
рг1п1т"("<Н1>Результаты обработки формы</Н1>");
printf("</BODYX/HTML>");
Обратите внимание на символы перевода строки «\п\п». Первый из них за-
крывает строку заголовка HTTP, а второй нужен для создания пустой раздели-
тельной строки.
• НТТР_АССЕРТ_LANGUAGE
Переменная HTTP_ACCEPT_LANGUAGE содержит идентификатор предпочтитель-
ного национального языка для получения ответа от сервера Web.
• HTTP_UA_PIXELS
Разрешение видеоадаптера, установленное в компьютере пользователя.
• HTTP_UA_COLOP
Допустимое количество цветов в системе пользователя.
ф HTTP_UA_OS
Операционная система, под управлением которой работает браузер.
ф HTTP_UA_CPU
Тип центрального процессора, установленного в компьютере пользователя,
• HTTP_USER_AGENT
В эту переменную записывается имя браузера, с помощью которого запраши-
вается документ HTML. Анализируя это имя, программа CGI может прини-
мать решение об использовании тех или иных расширений стандарта языка
HTML, допустимого для конкретного браузера.
ф НТТР_Н08Т
Имя узла, на котором работает сервер Web.
ф HTTP_CONNECTION
Тип соединения.
ф HTTP_ACCEPT_ENCODING
Метод кодирования, который может быть использован браузером для фор-
мирования ответа серверу Web.
ф HTTPJMITHOHIZATION
Информация авторизации от браузера. Используется браузером для соб-
ственной аутентификации в сервере Web.
ф HTTP_FROM
Имя пользователя в виде, как оно было зарегистрировано при настройке
браузера. Используется формат адресов электронной почты.
ф HTTP_PRAGMA
Специальные команды серверу Web.
ф CONTENT_LENGTH
Количество байт данных, которые программа CGI должна получить от бра-
узера.
ф CONTENTJTYPE
Тип данных, присланных браузером.
ф PATH_INFO
Путь к виртуальному каталогу, в котором находится программа CGI.
Как правило, при настройке сервера Web администратор выделяет один или
несколько каталогов для хранения расширений сервера в виде программ CGI
или ISAPI. Для файлов, записанных в такие каталоги, устанавливается доступ
на запуск.
[ I Зак. 3571
296 Базы данных в Интернете. Практическое руководство
Результат работы
программы CGI
Эта страница создана динамически! результате работы
программы ССЧ
Send Resei
Beak Shop
REQUESTJJ4ETHOD - POST
CONTENT Jl-ENGTH - 127
Принятые данные
hid-HiddenUteitl =3 unple+cfH«xtl &pw*-Sunple4Ti|4p is swotd&t ext2=S amp] e+
printf("<HTHLXHEAD><TTTLE>nporpaMMbi CGI"
"</TITLE></HEADXBODY BGCOLOR=#FFFFFF>");
szMethod = getenv("REQUEST_METHOD");
Далее функция m a i n определяет использованный метод передачи данных,
анализируя содержимое переменной среды REQUEST_METHOD:
if(!strcmp(szMethod, "POST"))
I
!
Это необходимо, так как при разных методах передачи нужно использовать
различные методы получения входных данных. Значение переменной среды
программа получает при помощи функции getenv.
Если данные передаются методом POST, программа будет считывать их из
стандартного потока ввода. Размер д а н н ы х находится в переменной среды CON-
TENT_LENGTH. Соответствующая текстовая строка получается функцией getenv и
преобразуется в численное значение функцией atoi.
Чтение данных из входного потока выполняется за один вызов функции
f read:
ISize = atoi(getenv("CONTENT_LENGTH"));
fread(szBuf, ISize, 1, stdin);
Этой функции мы передаем адрес буфера для записи принятых данных, раз-
мер данных, количество буферов, которые нужно считать, и входной поток.
Программа CGI сохраняет принятые данные в файле для дальнейшей обра-
ботки. Наша программа создает в текущем каталоге файл с названием recei-
ved, dat:
fileReceived = fopen("received.dat", "w");
fwrite(szBuf, ISize, 1, fileReceived);
fclose(fileReceived);
Текущим каталогом при запуске этой программы в среде сервера Microsoft
Information Server будет каталог с загрузочным модулем программы CONT-
Глава 7. Расширения CGI и ISAPI сервера Web 301
ROLS. Заметим, что, для того чтобы программа CGI могла создать файл в ката-
логе, необходимо соответствующим образом настроить права доступа.
После сохранения принятых данных в файле программа CONTROLS выво-
дит в стандартный поток вывода содержимое некоторых переменных среды:
REQUEST_METHOD, CONTENT_LENGTH и CONTENT_TYPE:
printf("<H2>flepeMeHHbie среды</Н2>"};
szBufflSize] = '\0';
strcpy(szSrcBuf, szBuf);
printf("<H2>npHHflTbie данные</Н2>");
printf("<P>JSs", szSrcBuf);
Далее наша программа перекодирует данные, полученные в кодировке URL.
Перед этим принятые данные копируются в буфер, где они будут обновляться
по месту. Для копирования мы закрываем буфер двоичным нулем, после чего
копирование можно выполнить функцией копирования строки strcpy.
Перекодироввание выполняется функцией DecodeSt г, определенной в пашем
приложении. Эту функцию мы рассмотрим позже. Результат сохраняется в бу-
фере s z S r c B u f , откуда он и берется для отображения:
OecodeStr(szSrcBuf);
printf("<H2>flaHHwe после перекодировки</Н2>");
printf("<P>Xs", szSrcBuf);
На завершающем этапе обработки данных, полученных от формы, програм-
ма CONTROLS записывает в выходной документ HTML значения отдельных
полей. Напомним, что эти значения имеют формат &<Имя_полн>=<Значение>, при
этом символ «&» используется как разделитель.
Наша программа закрывает исходный буфер с принятыми данными допол-
нительным символом «&» (для простоты сканирования), после чего запускает
цикл по полям формы.
szBuf[!Size] = '&';
szBuf[lSize + 1] = '\0Т;
for(szParam = szBuf;;)
if(szPtr != NULL)
szParam = szPtr + 1;
if(szParam >= (szBuf + ISize))
(см. след, стр.)
302 Базы данных в Интернете. Практическое руководство
break;
!
else
break;
}
В этом цикле во входной строке с помощью функции strchr ищется символ-
разделитель. Если этот символ найден, мы его заменяем на двоичный нуль, после
чего полученная текстовая строка значения параметра перекодируется функцией
DecodeStr и выводится в стандартный выходной поток.
Цикл завершается, когда в процессе сканирования указатель текущей пози-
ции выходит за границы буфера данных.
В конце программа CONTROLS закрывает документ HTML, записывая в
него теги </BODY> и </HTML>:
printf {"</BOOYX/HTML>");
Если данные передаются в программу CONTROLS методом GET, входные
данные находятся в переменной среды QUERY_STRING, которую мы получаем сле-
дующим образом:
szQueryString = getenv("QUERY_STRING");
Размер строки определяется при помощи функции strlen:
ISize = strlen(szQueryString);
Обработка принятых данных выполняется аналогично тому, как это делает-
ся методом POST. Разница заключается лишь в том, что в документе мы отобра-
жаем содержимое только переменных REQUEST_METHOD и QUERY_STRING.
Теперь займемся перекодировкой принятых данных. Она выполняется фун-
кцией DecodeStr, исходный текст которой приведен ниже:
void DecodeStr(char *szString)
int src;
int dst;
char ch;
if(ch == T)
szString[dst] = '\0';
ch «= 4;
return ch;
'
Программа AREF
В примерах, приведенных выше, мы использовали программы CGI только для
обработки данных из полей форм. При этом адрес URL загрузочного файла про-
граммы указывался в параметре ACTION тега <FORM>.
Однако есть и другая возможность вызова программ CGI: указать их адрес в
параметре HREF тега ссылки <А>. В этом случае Вы можете передать программе
CGI параметры, указав их после имени файла загрузочного модуля через раз-
делительный символ «?». Программа CGI получит строку параметров методом
GET и сможет извлечь ее из переменной среды с именем QUERY_STRING.
Все это можно использовать для того, чтобы программа CGI загружала в окно
браузера тот или иной документ в зависимости от параметров, с которыми она
была вызвана.
Пример документа HTML, в котором демонстрируется вызов программы CGI
указанным выше способом, приведен в листинге 7-5.
Листинг 7-5 Вы найдете в файле ch7\aref\aref.html на прилагаемом к книге
компакт-диске.
В этом документе есть три ссылки на программу CGI с именем aref.exe, при-
чем каждый раз ей передаются разные параметры:
<а HREF="http://saturn/cgi-bin/aref.exe?page1">
<р>Издательство "Русская Редакция&дио1:;</а><Ьг>
<а HREF="http://saturn/cgi-bin/aref. exe?page2">Microsoft</axbr>
<а HREF="http://saturn/cgi-bin/aref.ехе?радеЗ">Наш сервер</а><Ьг>
Программа CGI принимает параметр и в зависимости от его значения отобра-
жает один из документов HTML. Например, при выборе первой строки в окне
304 Базы данных в Интернете, Практическое руководство
else
printf("Location: error.html\n\n");
При совпадении программа возвращает навигатору адрес URL соответству-
ющего документа HTML, формируя заголовок HTTP специального вида:
Location: "Адрес URL"\n\n
Когда браузер получает от сервера Web такой заголовок, он отображает в
своем окне документ или файл графического изображения, адрес URL которо-
го указан в заголовке. В случае ошибки посетителю отправляется документ с
именем error.html
Таким образом, программа CGI анализирует параметры, поступающие от
навигатора через ссылку или поля формы, а затем не только динамически фор-
мирует документ HTML или ASP для отображения в окне браузера, но и воз-
вращает ссылки на уже существующие документы в виде их адресов L'RL.
Эта возможность пригодится Вам, например, для организации ссылок на
документы через списки, создаваемые тегом <SELECT>, находящимся в форме.
Программа CGI определит, какая строка выбрана в списке в момент посылки
заполненной формы серверу Web, и в зависимости от этого либо возвратит ссыл-
ку на тот или иной существующий документ, либо сформирует новый документ
динамически.
,
г
Просмотр списка сотрудников
jfrolov
rfjj Computer
„J
Список сотрудников магазина
1NULL jSahs_maiMg*r
TestAdimns 1234 MULL iSales_manag«.
ictmin "~ ~ \123~ |05 129909:50:10 jSal
else
cout « "<п2>Можно использовать только метод POST</h2>";
Если в форме по ошибке применен метод GET, программа запишет сообщение
об ошибке в создаваемый документ HTML и завершит свою работу.
В том случае, если использован метод POST, программа определит размер дан-
ных, отправленной формой, а затем сохранит их н буфер s z B u f :
int ISize;
char szBuf[8196J;
ISize = atoi(getenv("CONTENT_LENGTH"));
fread(szBuf, ISize, 1, stdin);
szBuf[lSize] = '\0';
На следующем этапе принятые данные копируются в буфер szSrcBuf и там
перекодируются:
char szSrcBuf[8196];
strcpy(szSrcBuf, szBuf);
DecodeStr(szSrcBuf );
Далее программа запускает цикл сканирования принятых данных, чтобы из-
влечь из них содержимое полей формы (идентификатор пользователя и его па-
роль):
szBuf[lSize] = '&';
szBuf[lSize + 1] = '\0';
char * szPtr;
char * szParam;
for(szParam = szBuf ; ; )
(см. след, стр.)
308 Базы данных в Интернете. Практическое руководство
I
szPtr = strchr(szParam, ' & ' ) ;
if(szPtr != NULL)
<
*szPtr = '\0';
DecodeSt r(szParam) ;
GetParam(szParam);
szParam = szPtr + 1;
if(szParam >= (szBuf + ISize))
break;
}
else
break;
strcpy(szBuf , szString);
szPtr = strchr(szBuf , ' = ' ) ;
if(szPtr != NULL)
{
-szPtr = ' \ 0 ' ;
szPtr++;
else
cout « "<п2>Доступ запрещен</М2>";
В качестве параметров этой функции передаются указатели на соответству-
ющие глобальные переменные с идентификатором и паролем.
Функция l o g i n обращается к базе данных для проверки идентификатора,
пароля, а также прав пользователя. Если запрос сделал пользователь с правами
администратора, функция login возвращает значение true, а если нет — false.
После успешной проверки прав пользователя вызывается функция getMana-
gers. Она записывает в динамически формируемый документ HTML сведения
о содержимом таблицы m a n a g e r s .
Перед завершением своей работы программа CGICPPADO записывает в
стандартный выходной поток теги, завершающие формирование документа
HTML:
cout « "</BODYX/HTML>";
Функция l o g i n практически совпадает с одноименной функцией из прило-
жения CPPADO, описанной нами в четвертой главе. Однако есть и отличия —
новый вариант функции принимает через свои параметры имя и пароль пользо-
вателя, а не вводит их из стандартного потока:
bool l o g i n f c h a r * szUsername, char * szPassword)
{
strTmp.Format(
xtd>Xs</tdxtd>JMOs</tdxtd>ms</tdxtd>%20$</tdxtd>X10s</td>",
v2str(vHanagerID), v2str(vName),
v2str(vPassword), v2str(vLastLogin),
v2str(vRights));
cout « "<tr>";
(см. след, стр.)
310 Базы данных в Интернете. Практическое руководство
hr = rs->MoveNext();
if(!SUCCEEDED(hr))
break;
При этом для каждой строки набора записей мы формируем одну строку таб-
лицы в выходном документе HTML.
Перед завершением своей работы функция getManagers записывает в выход-
н о й поток закрывающий тег таблицы:
cout « "</table>";
Обработка ошибок, возникающих при обращении к базе данных, выполняется
при помощи функции A d o E r r H a n d l e r :
catch(_com_error ex)
.
AdoErrHandler(cn);
return;
lstrcpyn(pVersion->lpszExtensionDesc,
"My ISAPI Application Name", HSE_MAX_EXT_DLL_NAHE_LEN);
return TRUE;
ф IpszLogData
Поле IpszLogData предназначено для записи сообщения о выполнении тран-
закции в журнал сервера Web. Это сообщение указывается в виде текстовой
строки, закрытой нулем. Размер строки в байтах не должен превышать зна-
чения HSE_LOG_BUFFER_LEN.
^ IpszMethod
Поле I p s z M e t h o d заполняется сервером и содержит название метода переда-
чи данных от удаленного пользователя серверу в виде текстовой строки, за-
крытой двоичным нулем. Расширения ISAPI используют те же самые мето-
ды, что и программы CGI — метод GET и метод POST. Проводя аналогию с про-
граммами CGI дальше, скажем, что поле I p s z M e t h o d эквивалентно перемен-
ной среды с именем REQUEST_METHOD, создаваемой для программы CGI.
ф IpszQueryString
Аналогично поле I p s z Q u e r y S t r i n g соответствует переменной среды с именем
Q U E R Y _ S T R I N G . В это поле записываются данные, принятые от удаленного
пользователя методом GET.
ф IpszPathlnfo
В поле I p s z P a t h l n f o записывается виртуальный путь к программному фай-
лу библиотеки DLL расширения ISAPI. Напомним, что аналогичная инфор-
мация для программ CGI передавалась через переменную среды с именем
PATONFO,
ф IpszPathTranslated
Это поле содержит физический путь к программному файлу библиотеки DLL
расширения ISAPI. Оно соответствует переменной среды с именем PATH_TRANS-
LATED, создаваемой для программ CGI.
ф cbTotalBytes
В поле cbTotalBytes записывается общее количество байт данных, которое
необходимо получить от удаленного пользователя. Часть этих данных (раз-
мером не более 48 кб) считывается сервером автоматически. Эти д а н н ы е
будут сразу доступны, после того как функция HttpExtensionProc получит уп-
равление. Остальные данные необходимо дочитать в цикле при помощи фун-
кции ReadClient, о которой мы еще будем говорить.
ф cbAvailable
В поле c b A v a i l a b l e записывается размер блока данных, полученных автома-
тически от браузера посетителя сервера. Как мы только что сказали, размер
этого блока не может превышать 48 кб. Этого, однако, вполне достаточно для
обработки данных, полученных от форм обычного размера.
ф IpbData
Указатель на область памяти, в которую записан сервером полученный от
удаленного пользователя блок данных размером c b A v a i l a b l e байт.
| IpszContentType
Поле IpszContentType содержит тип принятых данных, например «text/html».
Глава 7. Расширения CGI и ISAPI сервера Web 315
ф GetServerVariable
Помимо полей данных, структура EXTENSION_CONTROL_BLOCK содержит указа-
тели на функции. С помощью этих функций расширение ISAPI может выпол-
нять различные операции, такие, как прием данных от удаленного пользова-
теля .
Поле G e t S e r v e r V a r i a b l e содержит указатель на функцию, средствами кото-
рой расширение ISAPI может получить информацию, доступную программам
CGI через переменные среды.
• WriteClient
В поле W r i t e C l i e n t находится адрес функции, которую расширение ISAPI
должно использовать для отправки данных удаленному пользователю. Таким
образом, вместо того чтобы записывать данные в стандартный поток вывода,
как это делает программа CG1, приложение 1SAPI посылает данные с помо-
щью функции W r i t e C l i e n t .
ф ReadClient
Посредством функции, адрес которой передается в поле R e a d C l i e n t , прило-
жение может дочитать дополнительные данные, не поместившиеся в буфер
предварительного чтения, имеющий адрес IpbData и размер, не превышающий
48 кб. Аналогичную операцию приема данных от пользователя выполняет
программа CGI в случае применения метода передачи данных POST. Отличие
заключается в том, что программа CGI получает данные через стандартный
поток ввода, а расширение ISAPI берет эти данные из буфера предваритель-
ного чтения и при необходимости дочитывает данные при помощи функции
ReadClient.
ф ServerSupportFunction
Посредством функции, адрес которой передается через поле S e r v e r S u p p o r t -
Function, расширение ISAPI может выполнять различные действия, такие, как
посылка стандартного заголовка протокола HTTP и некоторые другие.
При успешном завершении функция HttpExtensionProc должна вернуть зна-
чение HSE_STATUS_SUCCESS, а при ошибке - значение HSE_STATUS_ERROR. Соответ-
ствующие константы определены в файле httpext.h.
Получение данных расширением ISAPI
Программа CGI получает данные из переменных среды и стандартного потока
квода (в случае применения метода доступа POST). Расширение ISAPI делает это
по-другому.
Функция H t t p E x t e n s i o n P r o c получает указатель на структуру типа EXTEN-
SION_CONTROL_BLOCK. Некоторые поля этой 1 структуры заполняются сервером и
должны использоваться для получения входных данных. Прежде всего это поле
IpszMethod, через которое передается метод, использованный для посылки дан-
ных (GET или POST), поле IpszQueryString, в котором передаются параметры за-
пуска расширения или данные при использовании метода GET, а также другие
поля, описанные выше,
Через структуру EXTENSION_CONTROL_BLOCK передаются адреса функций GetSer-
v e r V a r i a b l e и ReadClient, специально предназначенных для получения данных
от браузера посетителя.
316 Базы данных в Интернете. Практическое руководство
Функция GetServerVariable
Прототип функции G e t S e r v e r V a r i a b l e определен в структуре EXTENSION_CONT-
ROL_BLOCK, описанной нами ранее:
BOOL (WINAPI * GetServerVariable)(HCONN hConn,
LPSTR IpszVariableName, LPVOID IpvBuffer, LPDWORD IpdwSize);
Через параметр h C o n n Вы должны передать этой функции идентификатор
канала, полученный через поле ConnID структуры EXTENSION_CONTROL_BLQCK.
Параметр I p s z V a r i a b l e N a m e должен содержать указатель на строку имени
переменной, содержимое которой необходимо получить. Это содержимое будет
записано функцией в буфер, адрес которого передается через параметр I p v B u f f e r ,
а размер — через параметр IpdwSize.
Ниже перечислены возможные значения строк, передаваемых через параметр
IpszVariableName.
+ AUTH_TYPE
Переменная среды AUTH_TYPE содержит тип идентификации, который приме-
няется сервером.
• НТТР_АССЕРТ
В этой переменной перечислены типы данных MIME, которые могут быть
приняты браузером от сервера Web.
ф CONTENT_LENGTH
Количество байт данных, которые расширение ISAPI должно получить от
браузера.
+ CONTENT_TYPE
Тип данных, присланных браузером.
Ф PATH^INFO
Путь к виртуальному каталогу, в котором находится библиотека DLL расши-
рения ISAPI.
• PATH.TRANSLATED
Физический путь к библиотеке DLL расширения ISAPI.
ф QUERY_STRING
Строка параметров, указанная в форме или теге ссылки <А>. Эта строка ука-
зывается после адреса URL библиотеки DLL расширения ISAPI вслед за
разделительным символом «?».
• REMOTE_ADDR
Адрес IP узла, на котором работает браузер посетителя.
ф REMOTE_HOST
Доменное имя узла, на котором работает браузер посетителя. Если эта инфор-
мация недоступна (например, для узла не определен доменный адрес), то вме-
сто доменного имени указывается адрес IP, как в переменной REMOTE_ADDR.
ф REMOTE_USER
Имя пользователя, которое применяет браузер для аутентификации.
Глава 7. Расширения CGI и ISAPI сервера Web 317
+ UNMAPPED_REMOTE_USER
Имя пользователя до обработки фильтром ISAPI, которое применяет брау-
зер для аутентификации.
• REQUEST_METHOD
Метод доступа, который используется для передачи данных от браузера сер-
веру Web.
• SCRIPT_NAME
В эту переменную записывается путь к виртуальному каталогу и имя библио-
теки DLL расширения ISAPI. Анализируя эту переменную, расширение
ISAPI определит путь к своему загрузочному файлу.
+ SERVER_NAME
Домепно.е имя сервера Web или адрес IP сервера Web, если доменное имя
недоступно или не определено.
+ SERVER_PROTOCOL
Имя и версия протокола, который применяется для выполнения запроса к
расширению ISAPI.
+ SERVER_PORT
Номер порта, на котором браузер посылает запросы серверу Web.
• SERVER_PORT_SECURE
Если обработка запроса выполняется через защищенный порт, в этой строке
записано значение 1, а если через незащищенный — значение 0.
+ SERVER.SOFTWARE
Название и версия программного обеспечения сервера Web. Версия следует
после названия и отделяется символом <•/».
ф ALL_HTTP
Строка, закрытая двоичным нулем, в которую записаны значения всех пере-
менных, имеющих отношение к протоколу HTTP. Это, например, такие пе-
ременные, как НТТР_АССЕРТ, HTTP_CONNECTION, HTTP_USER_AGENT и т. д.
Извлекать содержимое отдельных переменных Ваша программа должна са-
мостоятельно. При этом следует учесть, что названия переменных отделены от
их значений символом двоеточия <<: », а поля переменных разделены символом
перевода строки.
Обратите внимание, что названия этих строк почти совпадают с названиями
переменных среды, создаваемых для программ CGI, однако совпадение все же
не полное.
В случае успешного завершения функция GetServerVariable возвращает зна-
чение TRUE, а при возникновении ошибки — значение FALSE. Код ошибки можно
определить с помощью функции GetLastError, вызвав се сразу после функции
GetServerVariable.
Возможные коды ошибок для функции G e t S e r v e r V a r i a b l e приведены в таб-
лице 7-2.
318 Базы данных в Интернете. Практическое р/коаодство
Функция ReadClient
Прототип функции ReadClient записан в определении структуры EXTENSION_CONT-
ROL_BLOCK и выглядит следующим образом:
BOOL (WINAPI * ReadClient) (HCONN ConnID,
LPVOID IpvBuffer, LPDWORD IpdwSize);
Через параметр h C o n n этой функции надо передать идентификатор канала,
полученный через поле C o n n I D структуры EXTENSION_CONTROL_BLQCK.
Функция ReadClient читает данные в буфер, адрес которого передается че-
рез параметр I p v B u f f e r , а размер — через параметр IpdwSize. В случае успеха
функция возвращает значение TRUE, а при ошибке — значение FALSE. Код ошиб-
ки можно получить посредством функции G e t L a s t E r r o r .
Работа с функцией R e a d C l i e n t имеет некоторые особенности.
Когда расширение ISAPI получает управление, через структуру типа EXTEN-
SION_CONTROL_BLOCK передается адрес предварительно прочитанного блока дан-
ных, полученного от браузера посетителя. Как Вы знаете, адрес и размер этого
блока данных указаны соответственно в полях IpbData и c b A v a i l a b l e структу-
ры EXTENSION_CONTROL_BLOCK.
Однако размер предварительно прочитанных данных не может превышать
48 кб. Если все данные не поместились в буфер предварительного чтения, их не-
обходимо дочитать функцией R e a d C l i e n t . При этом следует использовать сле-
дующий алгоритм.
Прежде всего следует сравнить размер предварительно считанных данных с
полным размером данных, которые нужно считать (этот размер передается в
поле cbTotalBytes структуры EXTENSION_CONTROL_BLOCK).
Глава 7. Расширения CGI и ISAPI сервера Web 319
Если все данные уже считаны, функцию ReadClient вызывать не нужно. В том
случае, когда значение, передаваемое через поле cbTotalBytes, превышает зна-
чение cbAvailable. Вы должны воспользоваться функцией ReadClient для того
чтобы прочесть cbTotalBytes-cbAvailable байт данных от пользователя.
Заметим, что функция ReadClient не будет читать заново данные, записанные
в буфер IpbData. Она займется оставшимися данными, причем не исключено, что
для их прочтения эту функцию придется вызывать в цикле несколько раз. Дело
в том, что функция ReadClient не всегда может прочитать все оставшиеся дан-
ные за один прием.
После успешного завершения чтения функция ReadClient записывает размер
прочитанного блока данных в переменную, адрес которой передается через па-
раметр IpdwSize. Если при первом вызове этот размер меньше величины cbTotal-
Bytes-cbAvailable. Вы должны вызвать функцию R e a d C l i e n t еще один или не-
сколько раз для чтения оставшихся данных.
Отправка данных расширением ISAPI
Вместо того чтобы записывать выходные данные в стандартный поток вывода,
как это делает программа CGI, расширение ISAPI пользуется для пересылки
данных функциями WriteCilent и ServerSupportFunction. Указатели на эти фун-
кции передаются расширению ISAPI через структуру типа EXTENSION_CONT-
ROL_BLOCK.
Функция WriteCilent
Прототип функции W r i t e C l i e n t , взятый из определения структуры E X T E N -
SION_CONTROL_BLOCK, приведен ниже:
BOOL (WINAPI * WriteClient){HCONN ConnlD,
LPVOID Buffer, LPDWORD IpdwBytes, DWORD dwReserved);
Через параметр hConn функции WriteClient передается идентификатор канала,
полученный через поле ConnlD структуры EXTENSION_CONTROL_BLOCK.
Функция WriteClient посылает удаленному пользователю данные из буфе-
ра B u f f e r , причем размер передаваемого блока данных должен быть записан в
переменную типа DWORD, адрес которой передается через параметр IpdwBytes.
Параметр dwReserved зарезервирован для дальнейших расширений возможнос-
тей функции.
В случае успеха функция возвращает значение TRUE, а при ошибке — значе-
ние FALSE. Код ошибки можно получить посредством функции G e t L a s t E r r o r .
Заметим, что после отпрвки данных функция WriteClient записывает в пе-
ременную, адрес которой был ей передан через параметр IpdwBytes, количество
успешно переданных байт данных. В отличие от функции ReadClient, функция
W r i t e C l i e n t посылает данные за один прием, поэтому нет необходимости вызы-
вать ее в цикле. Если же эта функция смогла передать только часть данных, то
это означает, что произошла ошибка.
Функция ServerSupportFunction
Прототип функции S e r v e r S u p p o r t F u n c t i o n , определенный в структуре типа
EXTENSION_CONTROL_BLOCK, приведен ниже:
BOOL (WINAPI * ServerSupportFunction)(HCONN hConn,
DWORD dwHSERRequest, LPVOID IpvBuffer,
LPDWORD IpdwSize, LPDWORD IpdwDataType);
320 Базы данных в Интернете. Практическое руководство
lpECB->ServerSupportFunction(lpECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff);
| HSE_REQ_SEND_URL
Используя операцию HSE_REQ_SEND_URL, расширение ISAPI может послать
удаленному пользователю данные, хранящиеся по определенному адресу
URL, как будто бы эти данные были запрошены непосредственно пользова-
телем по этому адресу URL. Это удобно для отправки либо динамически со-
зданных данных, либо предварительно подготовленных данных.
Адрес URL должен быть указан в виде текстовой строки, закрытой двоичным
нулем, через параметр IpvBuffer. Размер строки задают в параметре IpdwSize.
Что же касается параметра IpdwDataType, то при выполнении данной опера-
ции этот параметр игнорируется.
• HSE_R£Q_SEND_URL_REDIRECT_RESP
Отправка сообщения с номером 302 (URL Redirect). Адрес URL указывает-
ся аналогично тому, как это делается при выполнении операции HSE_REQ_-
SEND_URL
•
+ HSE_REQ_MAP_URL_TO_PATH
Преобразование логического адреса URL в физический. Адрес логического
пути передается через параметр I p v B u f f e r . По этому же адресу записывает-
ся результат преобразования. Размер буфера при вызове функции задается,
как обычно, с помощью параметра IpdwSize.
После выполнения преобразования в переменную типа DWORD, адрес которой
указан параметром IpdwSize, будет записана длина строки результата преоб-
разования.
ф HSE_REQ_DONE_WITH_SESSION
В том случае, когда расширение ISAPI оставляет канал открытым для выпол-
нения длительной обработки данных, этой командой можно сообщить серве-
ру о завершении обработки. Все параметры ф у н к ц и и S e r v e r S u p p o r t F u n c t i o n
при указании этой команды игнорируются.
Приложение ISHELLO
В качестве нашего первого расширения 1SAPI мы предлагаем приложение
ISHELLO, выполняющее простейшие функции.
Вызов расширения ishello.dll выполняется из формы, исходный текст кото-
рой приведен в листинге 7-9.
Листинг 7-9 Вы найдете в файле ch7\ISHELLO\ishetlo.html на прилагаемом к
книге компакт-диске.
Расширение вызывается в параметре ACTION тега <FORM> аналогично тому, как
это делается для программ CGI:
<form METHOD="POST"
ACTION="http: //saturn/cgi-bin/ishello, dll?Parain11 Param21 Param3">
<p><input TYPE="subfflit" VALUE="Send"> </p>
</form>
После вызова наше расширение ishello.dll динамически создает документ
HTML, представленный на рис. 7-7.
322 Базы данных в Интернете. Практическое р/ководство
Server Variables;
HTTP_ACCEPTmiage/gif, image fe-xbrtmap, image/jpeg, image/pjpeg,
appkcabofi/fflsword, applicabon/vnd ms -excel, */*
HTTP_ACCEPT_ENCODING^ap, deflate
I
g Local intr
dwSize = 4096;
(см. след, стр.)
324 Базы данных в Интернете. Практическое руководство
lpECB->GetServerVariable(lpECB->ConnID,
(LPSTR)"ALL_HTTP", (LPVOID)szTempBuf, SdwSize);
strcat(szBuff, szTempBuf);
В завершение в документ записывается финальная строка:
s t r c a t f s z B u f f , "</BODYX/HTML>");
Сформированный таким образом документ отправляется посетителю серве-
ра Web функцией S e r v e r S u p p o r t F u n c t i o n , как это показано ниже:
iff!lpECB->ServerSupportFunction(lpECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff))
{
return HSE_STATUS_ERROR;
lpECB->dwHttpStatusCode = 200;
return HSE_STATUS_SUCCESS;
Если при пересылке данных произошла ошибка, расширение завершает свою
работу с кодом HSE_STATUS_ERROR. В случае успеха в ноле состояния dwHttpSta-
tusCode записывается код 200. Вслед за этим расширение завершает свою рабо-
ту с кодом HSE_STATUS_SUCCESS.
Создавая проект расширения ISAPI, Вы должны подготовить файл опреде-
ления модуля для соответствующей библиотеки DLL (листинг 7-И).
Листинг 7-11 хранится в файле ch7\ISHELLO\ishello.def на прилагаемом к кни-
ге компакт-диске.
В разделе EXORT этого файла нужно указать имена функций G e t E x t e n s i o n -
Version и HttpExtensionProc:
LIBRARY ishello
DESCRIPTION 'Simple ISAPI DLL'
EXPORTS
GetExtensionVersion
HttpExtensionProc
Идентификатор \Ша\
Пароль р"Р
12} My C-jnputa
h(tp/Aeluin/cgi-birv'isfoini dll
Права сотрудника
User: frolw
Pas word 123
. Administrator
for(szParam = szTempBuf;;)
\
szPtr = strcMszParam, ' & ' } ;
if(szPtr != NULL)
I
*szPtr = '\0';
DecodeStr(szParam);
GetParam(szParam);
szParam = szPtr + 1;
if(szParam >= (szTempBuf + lpECB->cbAvailable))
break;
.
else
break;
;
Здесь используется техника, с которой мы познакомили Вас в исходных тек-
стах программ CGI. Она предполагает применение функций D e c o d e S t r и
GetParam.
Далее наша программа добавляет в буфер выходного документа HTML иден-
тификатор пользователя и пароль:
strcat(szBuff, "User: ");
strcat(szBuff, szUserlD);
strcatfszBuff, "<br>Pasword: ");
strcatCszBuff, szUserPassword);
strcatCszBuff, "<BR>");
Соответствующие строки извлечены из данных, отправленных формой, при
помощи функции GetParam.
Затем наше расширение ISFORM вызывает функцию g e t _ m a n a g e r _ t a b l e ,
передавая ей идентификатор, пароль, а также указатель s z U s e r R i g h t s на буфер,
в который эта функция должна записать права пользователя:
if С!get_manager_table(szUserID, szUserPassword, szUserRights))
:
strcat(szBuff, "<br>Rights: ");
strcatCszBuff, szUserRights);
strcat(szBuff, "<BR>");
else
{
strcatCszBuff, "<Ь2>Произошла ошибка</Ь2>");
strcat(szBuff, sErrMsg);
.
Если функция getjnanagerjtable в ы п о л н и л а обращение к базе данных без
ошибок, она возвращает нулевое значение. Наше приложение при этом добав-
ляет в выходной буфер права пользователя, извлеченные из таблицы m a n a g e r s
базы данных Bookstore.
При возникновении ошибок в выходной буфер копируется содержимое стро-
ки сообщения об ошибке s E r r M s g .
!23ак. 3571
328 Базы данных б Интернете. Практическое руководство
Lf(!lpECB->ServerSupportFunction(lpECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL,
(LPDWORD)szBuff))
{
return HSE_STATUS_ERROR;
lpECB->dwHttpStatusCode = 200;
return HSE_STATUS_SUCCESS;
Исходный текст функции g e t _ m a n a g e r _ t a b l e определен в файле odbcparam.c,
исходный текст которого приведен в листинге 7-14.
Листинг 7-14 хранится в файле ch7\ISFORM\odbcparam.c на прилагаемом к
книге компакт-диске.
Функция get_manager_table во многом аналогична функции main консольной
программы ODBCPARAM, о которой мы рассказывали в предыдущей главе на-
шей книги, поэтому мы поговорим об отличиях.
Приложение ISFORM написано на языке С, а не C++, поэтому мы отказались
от использования библиотеки шаблонов STL и типа данных string.
Сообщение об ошибке записывается в переменную s E r r M s g , объявленную в
файле odbcparam.c как extern:
extern char sErrMsg[8000];
Прототип ф у н к ц и и g e t _ m a n a g e r _ t a b l e приведен ниже:
int get_manager_table(char* szUserlD, char* szUserPassword,
char* szRights);
Через первый параметр ей передается указатель на строку идентификатора
пользователя, через второй — указатель на строку пароля, а через третий — ука-
затель на буфер, куда функция g e t _ m a n a g e r _ t a b l e должна записать результат
своей работы (права пользователя).
Далее функция g e t _ m a n a g e r _ t a b l e выполняет инициализирующие действия,
необходимые для работы с источником данных через интерфейс ODBC и создает
соединение с этим источником:
re = SQLAllocHandle(SQL_HANDLE_ENV, NULL, ShEnv);
гс = SQLSetEnvAttrfhEnv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
re = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hObc);
re = SQLConnectfhDbc,
szDSN, (SWORD)strlen((const char*)szDSN),
szUserName, (SWORD)strlen((const char*)szUserName),
szPassword, (SWORD)strlen((const char*)szPassword));
Здесь мы убрали обработку ошибок, выполняемую функциями G e t E r r o r M s g
и GetErrorMsgConn.
Глава 7. Расширения CGI и ISAPI сервера Web 329
strcpy((char*)szAdminPass, szUserPassword);
re = SQLExecDirect(hStmt,
(unsigned char*)"{call Manageri_ogin(?,?,?)}", SQL_NTS);
Значения входных параметров при этом копируются из параметров szUserlD
и szUserPassword функции g e t _ m a n a g e r _ t a b l e .
Результат работы хранимой процедуры записывается по адресу, который был
передан функции get^manage ratable через последний параметр:
strcpyCszRights, szAdminRights);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
SQLDisconnect(hDbc);
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
SQLFreeHandle(SQL_HANOLE_ENV, hEnv);
Далее функция освобождает полученные ей ресурсы и возвращает управление.
Исходные тексты функций GetErrorMsgConn и GetErrorMsg аналогичны
исходным текстам, использованным в программе ODBCPARAM. Мы внесли в
них небольшие изменения; теперь сообщение об ошибке будет отображаться не
в окне консольной программы, а в документе HTML. Кроме того, вместо пере-
менной класса string, определенного в библиотеке шаблонов STL, мы исполь-
зовали здесь обычные функции стандартной библиотеки С.
success = lpECB->ServerSupportFunction(
lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX,
&header_ex_info, NULL, NULL);
Далее остается только отправить браузеру содержимое буфера выходного
документа HTML, что можно сделать при помощи функции W r i t e C l i e n t :
dwSize = strlen(szBuff);
lpECB->WriteClient(lpECB->ConnID, szBuff, &dwSize, 0);
Теперь мы можем завершить сеанс средствами функции S e r v e r S u p p o r t -
Function, передав ей во втором параметре константу HSE_REQ_OONE_WITH_SESSION:
lpECB->ServerSupportFunction(lpECB->Conn!D,
HSE_REQ_DONE_WITH_SESSION, NULL, NULL, NULL );
'ЛАВА 8
Создание проекта
Запустите Microsoft Visual C++ и выберите из меню File строку New. На экра-
не появится диалоговая панель New, открытая на вкладке Projects (рис. 8-1).
Г~5Г1 c«d |
Рис. 8-1. Диалоговая панель New
334 Базы данных в Интернете. Практическое руководство
Servei Type •
§«ес1йаЫв ЕЕХЕ1
Service (EXE)
COM wo BoakStorelogrnMod.ds£
FVoject Directory:
Добавление объекта
В результате работы мастера проектов создается проект библиотеки DLL, в ко-
торой пока не определено ни одного объекта.
Выберите в меню Insert системы проектирования Microsoft Visual C++ стро-
ку New ATL Object. Откроется первая панели мастера объектов ATL ( рис. 8-4).
П? fiesparse
P Session
P" i^PPfcafo
''Ш Eile Edit View [rasa gfejecf |aM locfe Щпйои \je\(t jJfiJjS
i . IE . zJ
• -• '
•
7 ciHjid id]
Z.
*.
- I[p BookStoreLoginMod classes _*.
7 *T* CBaokStoreLoain '.--• Thia file vill be proce=,ss
•=• •>« IBookStaeLDgin -• pnducs -,he type library i
* DnEndPagefl
import "oaidl . idl " .
« OrStai(Pege|IUnknoi4n 1Unk) intp:it "ocidl . idl " .
^ CSookStoreLogh(| [
^ m_bOrSlartPageCailed abject.
uuid(36D176CF-BCFA-HD3-9
lS> rn_piAppScalion
dual.
^rn_piReqjest helpstnng( " IBookStctrelog
(Ц/ rn_piResporiie pointe3r_de£ault (unique) '
gl> m_piSarvei ]
& ni_piSession interface IBookStoreLogin i_j
liei>
P- **HEEBH3B! ".i-ii.daid Seivei- Sid - Co
-"SOnEndF io to Deliralion HRESUtT OnStertPage![m]
.!> OnS'aN AdaM^lbod... HRESUIT OnEndPagef);
- J Global; •Q£EH^| }
'
Oopsriji CheckResul"))
HRESULT CheckResuKfflSTR taName.BSTR
. [out. retval|BSTR ТАУЦ:
return S_OK;
IfCpOutVal == NULL)
return E.POINTER;
IpszName = OLE2A(*bsName);
IpszPassword = OLE2A(*bsPassword);
char szBuf[256j;
strcpy(szBuf, IpszName);
strcat(szBuf, ":");
strcat(szBuf, IpszPassword);
Глава 8. Создание серверных элементов управления ActiveX 339
CComBSTR bstrTemp;
bstrTemp = A20LE(szBuf);
if(!bstrTemp}
return E_OUTOFMEMORY;
«pOutVal = bstrTemp.DetachO;
Макрокоманда USES_CONVERSION используется для обозначения того факта, что
наш метод будет применять перс-кодировку строк BSTR в формат обычных строк
ASCII, закрытых двоичным нулем, причем для перекодировки будут применять-
ся макрокоманды OLE2A и A20LE. Первая из них предназначена для преобразова-
ния строк BSTR в строки ANSI, а вторая выполняет обратное действие.
В начале своей работы добавленный фрагмент кода проверяет указатель
pOutVal, передаваемый методу для записи значения свойства. Если он равен NULL,
метод завершает свою работу с соответствующей ошибкой.
Далее мы преобразуем входные параметры bsName и bsPassword в обычные
текстовые строки ANSI, записывая указатели на результат преобразования со-
ответственно в поля класса IpszName и IpszPassword. Эти поля типа LPSTR Вам
надо добавить самостоятельно в класс C B o o k S t o r e L o g i n обычным образом.
После преобразования наш метод копирует входные строки в буфер szBuf,
разделяя их двоеточием.
Для преобразования результата копирования в тип BSTR мы создаем указатель
b s t r T e m p типа CComBSTR и записываем в него результат преобразования, выпол-
ненного макрокомандой A20LE. Если оно выполнено с ошибкой, в указатель
bstrTemp будет записано нулевое значение. В этом случае метод возвращает код
ошибки, означающий отсутствие необходимого объема свободной памяти.
Чтобы вернуть значение свойства, мы вызываем метод Detach, определенный
в классе CComBSTR, На этом работа метода закончена.
Добавив описанные выше строки в определение метода ChcckResult, запус-
тите проект на трансляцию. Созданный в виде библиотеки DLL элемент управ-
ления будет зарегистрирован и доступен для вызова. Если этот файл Вы пред-
полагаете использовать на другом компьютере, его нужно зарегистрировать при
помощи программы REGSVR32, передав ей путь к файлу DLL. Запустив про-
грамму REGSVR32 без параметров, Вы увидите на экране краткую инструкцию
по ее использованию.
Добро пожаловать!
Имя prolov
Пароль F
Подключение пользователя
Бы ввели (идентификатор "пар о ль) - frolov:12345
I
Вначале метод O n S t a r t P a g e получает указатель на интерфейс I s c r i p t i n g -
Context:
CComPtr<IScriptingContext> spContext;
hr = pUnk->QueryInterface(IID_IScriptingContext,
(void **)&spContext);
Далее, пользуясь этим интерфейсом, метод извлекает указатели на объекты
Request, Response, Server, Session и Application:
hr = spContext->get_Request(&m_piRequest);
hr = spContext->get_Response(&m_piRssponse);
hr = spContext->get_Server(&m_piServer);
hr = spContext->get_Session(&m_piSession);
hr = spContext->get_Application(&m_piApplication);
Метод OnEndPage освобождает полученные указатели при завершении обра-
ботки серверного сценария, расположенного на странице:
m_piRequest. Release( );
m_piResponse. Release( ) ;
m_pi$erver. ReleaseQ;
m_piSession. ReleaseO;
m_piApplication . Release( ) ;
Кроме методов OnStartPage и OnEndPage в файле BookStorcLogin.cpp находится
определение созданного нами метода get_CheckResult:
STDMETHODIMP CBookStoreLogin: :get_CheckResult(
BSTR *bsName, BSTR *bsPassword, BSTR *pOutVal)
:
return S_OK;
if(szErrorURL != NULL)
strcpy(szErrorURL, "error.html");
if(szNoyificattonURL != NULL)
strcpy(szNoyificationURL, "notify.html");
Далее функция возвращает нулевой значение в качестве признака успешно-
го завершения своей работы.
Помимо функции f n S e n d P a y D a t a в исходном тексте нашей библиотеки DLL
определена стандартная функция DllMain:
BOOL APIENTRY DllMain(HANDLE hModule,
DWORDul_reason_for_call, LPVOID IpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAO_ATTACH:
case DLL_THREADJ)ETACH:
case DLL_PROCESS_DETACH;
break;
!
return TRUE;
char szSuccessURL[512];
char szErrorURL[512];
char szNoyificationURL[512];
tf(hCreditCard!nterfaceDLL != NULL)
{
fn = (DLLFN)GetProcAddress(
(HMODULE)hCreditCardlnterfaceDLL, "fnSendPayData");
if(fn != NULL)
I
int n = fn("123", "usd", 12345,
szSuccessURL, szErrorURL, szNoyificationURL);
FreeLibrary(hCreditCardlnterfaceDLL);
m_Amount=newVal;
return S_OK;
m_MerchantID=newVal;
return S_OK;
Глава 8. Создание серверных элементов управления ActiveX 347
fn = (DLLFN)GetProcAddress(
(HMODULE)hCreditCardlnterfaceDLL, "fnSendPayData");
:
Если библиотека загрузилась, а указатель получен без ошибок и не равен NULL,
метод преобразует значения из полей m_Amount и m _ C u r r e n c y в текстовые строки
ANSI:
pszAmount = QLE2A(m_Amount);
pszCurrency = OLE2A(m_Currency);
Соответствующие указатели pszAmount и pszCurrency Вы должны определить
в классе CreditCard:
char* pszCurrency;
char* pszAmount;
348 Базы данных в Интернете. Практическое руководство
Сумма1
Валюта (Рубли
Отменить
I ideal Nranel
Х>
<table border=1>
<trxtd>Success URL</td><td><X=sSuccessURLKX/td><tr>
<tr><td>Error URL</td><td><X=sErroiURL»></tdxtr>
<trxtd>Notification URL</td><tdxx=sNotificationURLXx/td><tr>
<tr><td>Amount</td><td><](=cc. Amount lx/tdxt r>
<trxtd>Currency</tdxtdxX=cc.CurrencyXx/tdxtr>
<tr><td>Merchant ID</tdxtd><X=cc.MerchentIDXx/td><tr>
</table>
<x
правки почты. Так как этот элемент реализует интерфейс автоматизации, его
можно вызывать из клиентских и серверных сценариев, а также из любых про-
грамм, способных работать с объектами СОМ.
Кому: jftolovSlglBsnetru
От кого. |trolDV©glosnettij
_дтгудвитьj Отивиить
WSADATA wsaData;
WSAStartupfO, &wsaData);
return Module. RegisterServer(TRUE);
Глава 8. Создание серверных элементов управления ActiveX 355
STDAPI DllUnregisterServer(void)
WSACleanupC);
return _Module.UnregisterServer(TRUE);
if(m_SMTPServer.LoadString(IDS_PQHT))
m_Port=atoi(m_SMTPServer);
else
(см. след, стр.)
356 Базы данных в Интернете. Практическое руководство
m_Port=25;
m_From. LoadString(IDS_FROM);
m_To.LoadString(IDS_TO);
m_SMTPServer.LoadStringCIDS_SMTPSERVER);
m_Subject.LoadString(IDS_SUBJECT);
m_Encoding.LoadString(IDS_ENCODING);
m_Format.LoadString(IDS_FORMAT);
}
В файле МТА.срр Вы также найдете определение свойств SMTPPort, Status и
StatusEx.
Файл scnd.cpp (листинг 8-13) содержит определение функций и методов,
применяемых для установки соединения с почтовым сервером и для отправки
сообщения.
Листинг 8-13 хранится в файле chOS/MTASend/send.cpp на прилагаемом к кни-
ге компакт-диске.
Рассмотрим метод Send, посылающий сообщение:
STDMETHODIMP M T A : : S e n d ( )
AFX_MANAGE_STATE(AfxGetStaticModuleState())
m_Status=1;
if(Connect(m_SMTPServer, n_Port))
{
try
{
TransferO;
}
catch(. . .)
I
m_Status=3;
}
else
{
m_StatLJS=2;
!
Disconnect();
return S_OK;
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr,s_addr = inet_addr(Host);
Далее мы получаем адрес ТР почтового сервера и сохраняем его в перемен-
ной he:
if(srv_addr.sin_addr.s_addr==INADDR_NONE)
•;
hostent *he=gethostbyname(Host);
if(he) memcpy((char FAR *)&srv_addr. sin_addr. s_addr,
he->h_addr, he->h_length);
else
{
TRACEfgethostbynameO failed code Xd\n", WSAGetLastErrorf)};
rc=FALSE; m StatusEx=1;
}
>
Полученный адрес копируется в поле s _ a d d r структуры s r v _ a d d r . s i n _ a d d r .
На следующем этапе метод Connect получает сокет для связи с почтовым сер-
вером:
srv_addr.sin_port=htons(Port);
fn_Socket=socket(PF_INET,SOCK_STREAM,0);
if (m_Socket==INVALID_SOCKET)
'
if(bind(m_Socket,(LPSOCKADDR)&cli_addr,sizeof(cli_addr))
== SOCKET_ERROR)
:
!
Далее выполняется соединение:
if(rc && connect(m_Socket, (LPSOCKADDR)&srv_addr,sizeof(srv_addr))
==SOCKET_ERHOR)
•:
!
358 Базы данных в Интернете. Практическое руководство
m_Stoped=-1;
ГЛАВА 9
Создавая приложения Java, никогда не следует забывать о том, что они пред-
назначены для работы на различных компьютерных платформах. При этом Вы
не можете полагаться на то, что Вам будет доступен какой-либо конкретный
шрифт, кнопки или другие компоненты будут иметь определенный размер или
форму, а видеоадаптер будет работать в режиме с каким-либо заданным или за-
ранее известным разрешением.
Для того чтобы обеспечить работу приложений Windows в режимах с различ-
ным разрешением видеоадаптера, размеры элементов управления «привязыва-
ются» к размерам системного шрифта. Однако указанный способ недостаточно
универсален для применения на различных платформах, так как в разных опе-
рационных системах, вероятно, эта привязка будет выполняться по-разному.
Кроме того, теоретически системный шрифт в какой-нибудь операционной си-
стеме может отсутствовать как таковой.
С другой стороны, динамическое формирование внешнего вида пользователь-
ского интерфейса во время работы программы позволит адаптировать его «на
ходу» к особенностям конкретной операционной системы. Для этого приложе-
ния Java используют достаточно гибкую и мощную систему управления разме-
щением компонентов и контейнеров с названием Layout Manager.
Система Layout Manager способна работать в нескольких основных режимах,
отличающихся различными стратегиями размещения компонентов, определения
их размеров и выравнивания.
Режим Flow/Layout
В самом простом режиме FlowLayout компоненты добавляются в окно контей-
нера с применением следующего алгоритма. Каждый новый добавленный ком-
понент располагается вслед за предыдущим в направлении слева направо и
сверху вниз, при этом выполняется центровка компонентов по горизонтали,
Одной из особенностей данного режима является возможное изменение взаим-
ного расположения добавленных компонентов при изменении размеров контей-
нера.
Установка режима FlowLayout выполняется при помощи метода set Layout,
как это показано ниже:
setlayout(new FlowLayoutQ);
Далее компоненты добавляются в окно контейнера методом add, например:
TextField tf;
Button btnGetName;
add(tf);
add(btnGetName);
Режим GridLayout
Когда установлен режим размещения GridLayout, все компоненты располагают-
ся в ячейках таблицы, имеющей заданное количество строк и столбцов. Разме-
ры компонентов изменяются таким образом, чтобы они полностью занимали
свои ячейки.
Глава 9. Применение аплетов Java 361
add(tf);
add(btnGetName);
Режим Border-Layout
Режим BorderLayout предполагает разделение окна контейнера на рамку и цен-
тральную часть. Методу add при этом указывается направление от центра окна,
в котором следует размещать компоненты.
Направление указывается следующим образом:
add("Center", btnl); // центр
add("East", btn2); // восток
add("West", btn3); // запад
add("North", btn4); // север
addf'South", btn5); // юг
Здесь мы добавили в окно контейнера компоненты b t n l , ..., btn5. При этом
компонент btnl располагается в центре окна контейнера, а остальные компонен-
ты — по бокам. Размеры компонентов изменяются таким образом, чтобы они
полностью заполняли контейнер.
Заметим, что Вы не обязаны каждый раз добавлять в контейнер именно пять
компонентов и задействовать при этом все возможные направления,
Режим CardLayout
Режим размещения CardLayout предназначен для поочередного размещения
нескольких компонентов в одном контейнере (например, класса Panel).
При добавлении компонента в контейнер необходимо передать его имя ме-
тоду add через первый параметр, например:
picFrame pf;
pf = new picFrameO;
add("picO", pf);
Остальные компоненты добавляются аналогичным образом.
В классе CardLayout предусмотрено несколько методов, предназначенных для
выбора отображаемого компонента. Эти методы перечислены в таблице 9-1.
Таблица 9-1. Методы для выбора компонента
Метод Компонент для отображения
first Первый
last Последний
next Следующий
previous Предыдущий
show Произвольный, заданный своим именем
362 Базы данных в Интернете. Практическое руководство
Режим GridBagLayout
Последний режим размещения системы LayoutManager — это режим GridBag-
Layout. Он считается наиболее трудным, однако по сравнению с другими режи-
мами он очень гибкий. В ряде случаев Вам просто не обойтись без него.
Так же как и рассмотренный нами ранее режим GridLayout, режим GridBag-
Layout предполагает размещение компонентов в ячейках некоторой таблицы
заданной размерности. Вот наиболее важные отличия между этими режимами:
4 в режиме GridLayout размещаемые компоненты изменяют свои размеры та-
ким образом, чтобы заполнить ячейки таблицы, в которых они располагают-
ся. Режим GridBagLayout позволяет контролировать этот процесс, причем
при необходимости Вы можете задать стратегию такого изменения или отка-
заться от него вовсе;
ф в режиме GridLayout каждый компонент занимает только одну ячейку. Что
же касается режима GridBagLayout, то здесь компоненты могут занимать
несколько смежных ячеек в строках или столбцах;
ф при изменении размеров контейнера во время работы приложения при ис-
пользовании режима GridLayout все компоненты неизбежно изменяют свои
размеры. Это далеко не всегда удобно. В режиме GridBagLayout Вы можете
управлять стратегией изменения размеров компонентов или отказаться от
такого изменения.
Режим размещения компонентов GridBagLayout удобен для создания диало-
говых панелей, содержащих такие компоненты, как текстовые поля редактиро-
вания, переключатели, кнопки и т. д.
Выбирая соответствующим образом параметры размещения отдельных ком-
понентов путем заполнения соответствующих полей класса GridBagConstraints,
можно создавать панели, напоминающие по своему внешнему виду и поведению
стандартные диалоговые панели Windows или других операционных систем с
графическим интерфейсом. При этом можно добиться, чтобы размеры компонен-
тов и их взаимное расположение не изменялись при корректировке размеров
окна контейнера. Это невозможно при работе в других режимах размещения,
таких, как FlowLayout или GridLayout.
Как пользоваться режимом размещения GridBagLayout?
Схема достаточно проста.
Прежде веего Вы должны создать объект класса G r i d B a g L a y o u t при помощи
конструктора и выбрать его, как это показано ниже:
GridBagLayout gbl = new GridBagLayoutO;
setLayout(gbl);
Далее Вам нужно создать объект класса G r i d B a g C o n s t r a i n t s , поля которого
будут определять параметры размещения отдельных компонентов:
GridBagConstraints с = new GridBagConstraintsO;
Глава 9. Применение аплетов Java 363
Поле anchor
Поле anchor задает выравнивание компонента внутри отведенного для него про-
странства. Он включается в работу, когда размеры компонента меньше размеров
выделенного для него места.
Для поля a n c h o r Вы можете указать значения, приведенные в таблице 9-3.
Таблица 9-3. Значения поля anchor
Значение Направление выравнивания
GridBagConstraints.CENTER По центру
GridBagConstraints.NORTH Вверх
GridBagConstraints,SOUTH Вниз
GridBagConstraints,EAST Вправо
GridBagConstraints,WEST Влево
GridBagConstraints,NORHEAST Вверх вправо
G ridBagConstraints,NORTHWEST Вверх влево
GridBagConstraints.SOUTHEAST Вниз вправо
GridBagConstraints.SOUTHWEST Вниз влево
Аплет с формой
В качестве примера демонстрации режима размещения GridBagLayout приведем
исходные тексты аплета GridBag с формой
3 ! -«Hussion FdiHonX" Book Irtfeinel Ш1 V.r.mi rVi.liM'lM.iid I '• '
EN ЕЖ Viem Faunas Joais
J J 4 4-*j<J^i- J3i"- У
ijep
i
ш
jAlJtesjiLl E'MRussioiEdHionM1 Book liternet DBVSourceVchOSVjiiflj^J •-
II
jlvan Fist name OK i
Jl-111' ZIPcode
ЙЬвги ^МуСоДО
name Ivan
le name; P
.asl name S idoiov
ZIPcodelinil
Couitrji Russia
.1
TextField tfMiddleName;
Label IbMiddleName;
TextField tfLastName;
Label IbLastName;
Глава 9. Применение аплетов Java 367
TextField tfZip;
Label IbZip;
TextField tfCountry;
Label IbCountry;
Button btnOK;
Button btnCancel;
Метод init
Этот метод получает управление при инициализации аплета. Он создает и раз-
мещает все компоненты в окне аплета, а затем регистрирует обработчики собы-
тий от кнопок.
Компоненты создаются обычным образом при помощи соответствующих
конструкторов:
tfFirstName = new TextField(20);
IbFirstName = new Label("First name");
c.gridx = GridBagConstraints.RELATIVE;
c.gridy = GridBagConstraints.RELATIVE;
c.insets = new Insets(10, 10, 0, 0);
Кнопка Cancel
Эта кнопка размещается так:
с.gridwidth = GridBagConstraints.REMAINDER;
с.ipadx = 10;
c.weightx = 1.0;
gbl.setConstraints(btnCancel, c);
add(btnCancel);
Здесь в поле g r i d w i d t h мы указали значение G r i d B a g C o n s t r a i n t s . REMAINDER,
поэтому кнопка Cancel будет последней во второй строке.
Обратите внимание на поле w e i g h t x . Его значение не равно нулю, значит,
последний столбец нашей таблицы займет все оставшееся место в направлении
вправо. Если бы значение этого поля равнялось нулю, все компоненты оказались
бы выровненными по центру в горизонтальном направлении. Вы можете сделать
это сами.
Поле Last name
Это поле добавляется в начало третьей строки:
с.ipadx = 0;
с.gridwidth = 1;
с.weightx = 0.0;
gbl.setConstraints(tfLastName, c);
add(tfLastName);
Здесь мы просто восстанавливаем параметры, аналогичные параметрам поля
Middle name, расположенного в начале второй строки.
Метка поля Last name
Эта метка занимает всю оставшуюся часть третьей строки, так как в поле g rid-
width мы задали значение G r i d B a g C o n s t r a i n t s . R E M A I N D E R :
с.gridwidth = GridBagConstraints.REMAINDER;
gbl.setConstraints(lbLastName, с);
add(lbLastName);
Поле ZIP
При размещении этого поля мы восстанавливаем значение параметра g r i d w i d t h ,
измененное на предыдущем этане:
с.gridwidth = 1;
gbl.setConstraints(tfZip, c);
add(tfZip);
Поле Country
Это поле занимает одну ячейку последней строки, поэтому в поле g r i d w i d t h мы
записали значение I:
с.gridwidth = 1;
gbl.setConstraints(tfCountry, с);
add(tfCountry);
Метод actionPerformed
В задачу этого метода входит обработка событий, вызываемых щелчком кноп-
ки, и отображение диалоговой панели.
При щелчке кнопки ОК метод a c t i o n P e r f o r m e d получает строки из полей
нашей формы и записывает их в текстовую переменную с именем s:
String s = "<Personal information:^';
s = "First name: " + tfFirstName.getlextC) +
"\nHiddle name: " + tfMiddleName.getTextO +
"\ni_ast name: " + tfLastName.getTextQ +
"\nZIP code: " + tfZip.getTextf) +
"\nCountry: " + tfCountry.getText();
Далее метод создает диалоговую панель класса A p p l e t M s g B o x (определенный
в нашем приложении), передавая строку s соответствующему конструктору:
AppletMsgBox amsgbox;
amsgbox = new AppletMsgBoxfs, "Information");
amsgbox.show();
Панель затем отображается методом show.
В том случае если Вы щелкнете кнопку Cancel, поля формы очистятся:
tfFirstName.setText("");
tfMiddleName.setText("");
tfLastName.setText("");
tfZip.setTextC" 1 );
tfCount ry.setText ("");
Глава 9. Применение аплетов Java 371
Класс AppletMsgBox
Этот класс мы создали для отображения диалоговой панели с сообщением и
кнопкой ОК. Он образован на базе класса Frame и реализует интерфейс Action-
Listener:
class AppletMsgBox extends Frame
implements ActionListener
{
!
В классе A p p l e t M s g B o x определены два поля:
Button btnOK;
TextArea ta;
Первое из них хранит ссылку на кнопку, а второе — ссылку на многостроч-
ный редактор текста, в окне которого мы отображаем сообщение.
Конструктор класса AppletMsgBox
Конструктору класса A p p l e t M s g B o x передаются два параметра — строка сообще-
ния и строка заголовка:
public AppletMsgBox(String msg, String title)
i
Первым делом конструктор класса AppletMsgBox вызывает конструктор базо-
вого класса Frame, передавая ему строку заголовка title, и устанавливает раз-
меры окна панели:
super(title);
setSize(400, 200);
Кнопка и редактор текста создаются обычным образом:
btnOK = new Button("OK");
ta = new TextAreafmsg, 5, 40);
ta.setEditable(false);
Так как поле ta будет использоваться только для отображения сообщений, мы
отменяем функцию редактирования, вызывая метод setEditable.
Далее мы устанавливаем режим размещения компонента GridBagLayout:
GridBagLayout gbl = new GridBagLayoutO;
GridBagConstraints с = new GridBagConstraintsO;
setLayout(gbl);
Параметры размещения текстового поля выбираются следующим образом:
с.anchor = GridBagConstraints.CENTER;
с.fill = GridBagConstraints.BOTH;
c.gridheight = 1;
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridx = GridBagConstraints.RELATIVE;
c.gridy = GridBagConstraints.RELATIVE;
c.insets = new Insets(10, 0, 0, 0);
gbl.setConstraints(ta, c);
add(ta);
372 Базы данных в Интернете. Практическое руководство
Метод actionPerformed
Этот метод скрывает окно диалоговой панели, когда пользователь щелкает кноп-
ку ОК:
public void actionPerformed(ActionEvent e)
!
if (e. getSourceO . equals(btnOK))
!
setVisible(false);
Класс InetAddress
Для работы с адресами IP в библиотеке классов Java предназначен класс Inet-
A d d r e s s . С его помощью приложение определяет адрес IP локального узла, а
также адреса удаленного узла, заданного своим доменным именем.
Вот примеры наиболее интересных методов этого класса:
Глава 9. Применение аплетов Java 373
Класс URL
Для работы с ресурсами, заданными адресами URL, в библиотеке классов Java
имеется очень у д о б н ы й и мощный класс с названием U R L .
С помощью определенных в нем конструкторов и методов нетрудно извлечь
и проверить отдельные компоненты адреса; протокол, адрес узла, номер порта,
имя файла. Вы также можете открыть поток, связанный с ресурсом и прочитать
сто для отображения, обработки или для копирования в другой поток.
Расскажем кратко о классе U R L
Конструкторы класса
В этом классе предусмотрено четыре конструктора.
Первый из них создает объект U R L для сетевого ресурса, адрес URL которого
передается в виде текстовой строки через единственный параметр spec:
public URLCString spec);
374 Базы данных в Интернете. Практическое руководство
Класс URLConnection
В этом классе нам интересен метод openConnection.
Он предназначен для создания канала между приложением и сетевым ресур-
сом, представленным объектом класса URL:
public URLConnection openConnectionQ;
В этом классе также определены методы g e t O u t p u t S t r e a m и g e t l n p u t S t r e a m .
средствами которых Вы сможете создать соответственно потоки вывода и вво-
да, привязанные к каналу.
_Л-
Your name OR
Expiation date
Done J=J My C
Name=lvsn%2QPetrQvbcc"230D34-E73I.expii-1 2/1 2/
Ц 1 >1
ок 1
Waiting: Applet Window
Инициализация аплета
При инициализации аплета метод in it вначале создает все необходимые компо-
ненты и устанавливает режим размещения GridBagLayout:
tfName = new TextField(20);
IbName = new Label("Your name");
setLayout(gbl);
Далее метод init устанавливает параметры размещения отдельных компонен-
тов и добавляет их в окно аплета. При этом используется техника, примененная
нами ранее в этой главе при создании аплета GridBag.
Отправка данных расширению сервера Web
Когда пользователь щелкает кнопку ОК, управление передается методу action-
P e r f o r m e d . Рассмотрим выполняемые им действия.
Прежде всего, этот метод извлекает данные из компонентов, размещенных в
окне аплета. Данные оформляются в виде текстовой строки параметров запус-
ка расширения ISAPI с именем iscard.dll, как это показано ниже:
URL и;
try
{
и = new URL("http://saturn/cgi-bin/iscard.dll?" +
"Name=" + tfName.getText() +
"&cc=" + tfNumber.getTextO +
"&expir=" + tfExpiration.getTextQ + "&");
>
catch(Exception ioe)
{
String serr=ioe.toString();
amsgbox = new AppletMsgBox(serr, "Information");
amsgbox.showO;
IpECB-XJwHttpStatusCode = 200;
return HSE_STATUS_SUCCESS;
Создавая реальный проект, не забудьте выполнить сканирование строки
« l p E C B - > l p s z Q u e r y S t r i n g » с целью извлечения из нее параметров и при необхо-
димости — расшифровку. Далее Вы сможете использовать параметры в соответ-
ствии с логикой обработки номеров кредитных карточек, предусмотренной бан-
ком или процессинговой компанией.
Передача параметров странице ASP
Если по каким-либо причинам Вы не сумеете создать на сервере расширение
ISAPI, описанный способ передачи информации о кредитных карточках можно
реализовать средствами серверных сценариев, расположенных в документах
ASP. При этом аплет Java получит номер кредитной карточки посетителя и за-
шифрует его, а затем передаст странице ASP с использованием механизма за-
грузки нового документа в окно браузера. В этом разделе мы приведем пример
аплета CreditCard2, выполняющего указанные действия.
Полный исходный текст аплета приведен в листинге 9-5.
Листинг 9-5 Вы найдете в файле ch09/CreditCard2/CreditCard2.java на прилага-
емом к книге компакт-диске.
Так же как и только что описанный аплет CreditCard, аплет CreditCard2 по-
лучает от посетителя информации) о кредитной карточке при помощи компонен-
тов, добавленных в режиме размещения GridBagLayout.
Когда посетитель щелкает в окне аплета кнопку ОК, метод actionPerformed
загружает страницу ASP с именем ccard.asp, указывая ее полный адрес URL в
конструкторе класса URL:
u = new URL("http://saturn/ccard.asp?" +
"Name=" + tfName.getText() +
Глава 9. Применение аплетов Java 381
"&сс=" + tfNumber.getTextO +
"&expir=" + tfExpiration.getTextC) + "&");
Для того чтобы серверный сценарий, расположенный на странице ccard.asp,
получил информацию о кредитной карточке, при создании объекта класса U R L
мы добавляем к адресу страницы параметры, отделив их символом «?». При этом
параметры отделяются друг от друга символом «&»,
Чтобы загрузить страницу ccard.asp в окно браузера, мы создаем объект клас-
са AppletContext, вызывая для этого метод getAppletContext:
AppletContext appletContext;
appletContext = getAppletContextf);
Далее, пользуясь полученным контекстом, аплет загружает в окно браузера
документ с адресом TJRL, подготовленным в переменной и:
if (и != null)
<
appletContext.showDocument(u, "_self");
}
Для загрузки мы вызываем метод showDocument, передавая ему в качестве
второго параметра имя окна для загрузки документа. Пользуясь этим парамет-
ром, аплет может загрузить документ в любой фрейм, в существующее или вновь
созданное окно браузера. Параметр «_self >> означает, что документ будет загру-
жен в то же самое окно, где находится аплет.
Исходный текст страницы ccard.asp приведен в листинге 9-6.
Листинг 9-6 Вы найдете в файле ch09/CreditCard2/ccard.asp на прилагаемом к
книге компакт-диске.
В нем мы получаем параметры, переданные аплстом, при помощи объекта
R e q u e s t , а затем отображаем их з н а ч е н и я в динамически создаваемом докумен-
те HTML:
<p>Name = <X=Request("Name")(1)X>
<br>Credit card number = <K=Request("cc")(l)*>
<br>Expiration date = <X=Request("expir")(1)X>
В реальном проекте параметры можно, например, передать серверному эле-
менту управления ActiveX для расшифровки и дальнейшей обработки. Таким
образом, страница ASP станет удобным средством извлечения параметров из
данных, отправленных посетителем Вашего сервера при помощи анлста Java.
ПРИЛОЖЕНИЕ 1
Переменные
В сценариях JavaScript Вы можете использовать переменные, адресуясь к ним
по имени. Переменные бывают как глобальные, так и локальные. Первые до-
ступны из любого места сценария. Область действия локальных переменных ог-
раничивается функцией, внутри которой эти переменные объявлены.
Так же как и в языке программирования Basic, при составлении сценариев
JavaScript допустимо использовать переменные без их предварительного объяв-
ления. Исключение из этого пранила — локальные переменные, определенные
в функциях.
Объявление переменных
Все переменные в JavaScript объявляются с помощью ключевого слова var, как
это показано ниже:
var szHelloMsg;
При объявлении тип переменной не указывается. Он присваивается перемен-
ной только тогда, когда в нее записывается какое-либо значение.
При выборе имен переменных следует придерживаться следующих правил:
ф имя переменной должно начинаться с буквы или с символов «_», «$>> и мо-
жет состоять только из букв, цифр, а также символов «_», «$»;
^ имя переменной не должно совпадать с зарезервированными ключевыми
словами JavaScript.
Список зарезервированных ключевых слов JavaScript приведен в таблице П-1.
Таблица П-1. Зарезервированные ключевые слова JavaScript
break default false - .', true
case delete finally null try
catch do for return typeof
class else function super var
const enum I switch void
continue export import this while
debugger extends •i throw with
Приложение 1. Элементы языка JavaScript 383
"12345"
'Привет!'
384 Базы данных в Интернет!!. Практическое руководство
Операторы
Операторы языка сценариев JavaScript напоминают общеизвестные операторы
языка С.
Унарные операторы
Они применяются для изменения знака, выполнения операции дополнения,
инкремента и декремента (таблица П-2).
Таблица П-2. Унарные операторы
Унарный оператор Назначение
Изменение знака па противоположный
: Дополнение. Используется для реверсирования значения логи-
ческих переменных
++ Увеличение значения переменной. Также применяется как пре-
фикс переменной или как ее суффикс
Уменьшение значения переменной. Также применяется как пре-
фикс переменной или как ее суффикс
Бинарные операторы
Бинарные операторы соединяют два операнда. В языке сценариев JavaScript
предусмотрены бинарные операторы для вычитания, сложения, у м н о ж е н и я ,
деления и вычисления остатка деления (таблица П-3).
Таблица П-3. Бинарные операторы
Бинарный оператор Назначение
Вычитание
+• Сложение
* Умножение
/ Деление
X Вычисление остатка от деления
Операторы сдвига
Для выполнения операций сдвига в языке JavaScript предусмотрено три опера-
тора (таблица П-5).
Таблица П-5. Операторы сдвига
Оператор сдвига Назначение
» Сдвиг вправо
« Сдвиг плево
>» Сдвиг вправо с заполнением освобождаемых разрядов нулями
Перед использованием операторов сдвига значение переменной преобразует-
ся в 32-разрядное целое число.
Операторы отношения
Операторы отношения используются для сравнения значений переменных (таб-
лица П-6). Они возвращают логические з н а ч е н и я t r u e или false в зависимости
от результата сравнения и применяются главным образом в у с л о в н ы х опера-
торах.
Таблица П-6. Операторы отношения
Оператор отношения Условие, при котором оператор возвращает значение t r u e
> Левый операнд больше правого
>= Левый операнд больше или равен правому
Левый операнд меньше правого
Левый операнд меньше или равен правому
== Левый операнд равен правому
!= Левый операнд не равен правому
В условных операторах также часто применяются логические операторы (таб-
лица П-7).
Таблица П-7. Логические операторы
Логический оператор Описание
| | Оператор «ИЛИ». Возвращает значение t r u e , когда один
из операндов равен true
&& Оператор «И». Возвращает значение t r u e , когда оба
операнда равны t r u e
Оператор присваивания
Оператор присваивания задает значения переменным. В языке сценариев Java-
Script, так же как и в языке программирования С, допускается комбинирование
этого оператора с другими для изменения содержимого переменных.
В таблице П-8 мы перечислили вес возможные комбинации оператора при-
сваивания и других операторов.
Приложение 1. Элементы языка JavaScript 387
Условные операторы
Любой язык программирования бесполезен, если в нем не предусмотрены тс или
иные средства ветвления при выполнении программы. В языке JavaScript име-
ется условный оператор else-if, который позволяет выполнять разные про-
граммные строки в зависимости от условия.
Общий вид оператора else-if показан ниже:
ИЧуслоеие)
строка 1
[else
строка 2]
Часть оператора, выделенная квадратными скобками, является необязатель-
ной. При выполнении этого оператора оценивается условие, заданное в круглых
скобках после ключевого слова if. Если в результате оценки условия получи-
лось логическое значение true, выполняется строка 1. Если же получилось зна-
чение false, то выполняется строка 2 (в том случае, когда она присутствует).
Оператор if-else может быть вложенным. Учтите, что еели в строке 1 или 2
необходимо расположить несколько операторов, их следует выделить фигурны-
ми скобками.
Существует также специальный тип условного оператора, который называ-
ется оператором «?:». В общем виде он записывается так:
выражение ? строка 1 : строка 2
При вычислении оператора <<?:» вначале оценивается логическое выражение,
расположенное в левой части. Если оно равно true, выполняется строка 1, а если
false — строка 2.
Операторы цикла
В языке JavaScript несколько операторов предназначены для организации циклов.
Базы данных в Интернете. Практическое руководство
Оператор for
Общий вид оператора for показан ниже:
^([инициализация; ] [условие;] [итерация])
}
Б области инициализации обычно выполняется присваивание начальных
значений переменным цикла. Здесь допустимо объявление новых переменных
при помощи ключевого слова var.
Вторая область задает условие выхода из цикла. Это условие оценивается
каждый раз при прохождении цикла. Если в результате оценки получается ло-
гическое значение true, выполняются строки тела цикла.
Область итерации применяется для изменения значений переменных цикла,
например для уменьшения счетчика цикла.
Оператор for-in
Оператор f o r - i n предназначен для просмотра всех свойств объекта и записыва-
ется в следующем виде:
^[•(переменная in объект)
{
Оператор while
Для организации циклов с проверкой условия их завершения перед выполнени-
ем итерации используется оператор while:
ипПе(условие)
10)
break;
:
Оператор continue
Выполнение оператора c o n t i n u e внутри цикла f o r или w h i l e приводит к тому,
что итерация прерывается, а затем возобновляется заново. Этот оператор не пре-
рывает цикл.
Ниже мы приводим пример использования оператора c o n t i n u e :
van i = 0;
while(i < 100)
Прочие операторы
Среди прочих операторов языка сценариев JavaScript мы рассмотрим оператор
доступа к полю, индексирование массива, скобки и запятую (таблица П-9).
Таблица П-9. Прочие операторы
Оператор Описание
Доступ к полю объекта
[] Индексирование массива
() Скобки
Запятая
Первый из этих операторов применяется для вызова методов, определенных
в объектах, а также для доступа к полям объектов, или, как их еще называют, к
свойствам объектов.
Ниже, например, мы вызвали метод write, определенный в объекте d o c u m e n t :
document. write("Hello, world! ");
Квадратные скобки используются для индексации массивов аналогично тому,
как это делается в других языках программирования.
Что касается круглых скобок, то они применяются либо для изменения по-
рядка вычисления выражений, либо для передачи параметров функциям.
Оператор «запятая* предназначен для разделения выражений, которые надо
оценивать последовательно. Этот прием называется многократным вычислени-
ем. Например, в фрагменте исходного текста, показанном ниже, па каждой ите-
рации цикла выполняется увеличение содержимого переменных i и nCycleCount;
390 Базы данных в Интернете. Практическое руководство
var i;
var nCycleCount = 0;
ford = 0; i < 25; i++, nCycleCount++)
Многократное вычисление
Функции
Вы можете оформить фрагменты исходного текста в виде функции, вызывая эту
функцию по мере необходимости из различных мест сценария JavaScript.
Для клиентских сценариев функции обычно определяются в разделе заголов-
ка документа HTML, отмеченного тегами <HEAD> и </HEAD>. Как мы уже говори-
ли, функцию надо определить перед вызовом. Размещение всех определений
функций в разделе заголовка документа HTML гарантирует доступность этих
функций при обработке документа. В серверных сценариях ASP функции необ-
ходимо определить до их использования.
Общий вид определения функции показан ниже:
function имя([параметр 1] [.параметр 2] [ . . . . п а р а м е т р N])
Приложение 1. Элементы языка JavaScript 391
[return значение]
Встроенные объекты
В таблице IT-11 мы перечислили встроенные объекты, свойства и методы кото-
рых доступны в сценариях JavaScript без предварительного определения этих
объектов.
Таблица П-11. Встроенные объекты JavaScript
Объект Описание
Array Массив
Boolean Логические данные
Date Календарная дата
Function Функция
Global Глобальные методы
Math Математические константы и функции
Number Числа
Object Объект
String Строки
Встроенные объекты очень удобны для выполнения различных операций со
строками, календарными датами, массивами, числами и т. д. Они освобождают
программиста от выполнения рутинных операций вроде преобразования строк
или вычисления математических функций.
Как работать со встроенными объектами?
Это достаточно просто. Программа создает объекты, а затем обращается к их
свойствам и методам.
В качестве примера, имеющего практическое значение, рассмотрим фрагмент
сценария, расположенного в документе HTML, в котором отображается текущая
дата и время:
var dt;
van szDate="";
dt = new DateC);
szDate = "Date; " + dt,getDate() + "."
+ dt.getMonthO + "." + dt,getYear();
document.write(szDate);
document.write("<BR>");
document.write("Time: " + dt.getHoursO
+ ":" + dt.getMinutesO + ":" + dt.getSecondsO);
!43ак. 3571
392 Базы данных в Интернете. Практическое руководство
Здесь сценарий JavaScript создает объект Data, применяя для этого ключевое
слово new. знакомое всем поклонникам языка C++, и конструктор Date без па-
раметров:
van dt;
dt = new Date();
Создаваемый таким образом объект Data инициализируется текущей локаль-
ной датой, установленной на компьютере пользователя (а не на сервере Web, с
которого был загружен соответствующий документ HTML). Если же располо-
жить данный фрагмент кода в серверном сценарии ASP, то объект Data, напро-
тив, инициализируется датой, установленной на сервере,
В следующей строке формируется текстовая строка даты:
szDate = "Date: "• + dt.getDateO + "."
+
+ dt.getHonthO + "•" dt.getYear{>;
Значение календарного числа, номера месяца и года здесь получается при
помощи методов getDate, getMonth и getYear соответственно. Эти методы вызы-
ваются для объекта dt, содержащего текущую дату.
Текстовая строка даты выводится в документ HTML с помощью метода write,
определенного в объекте document:
document.write(szDate);
Заметим, что объект Date содержит также информацию о текущем времени.
Она извлекается для отображения с помощью методов getHours, g e t M i n u t e s и
getSeconds (соответственно часы, минуты и секунды):
docifment.writeC'Time: " + dt.getHoursO
+ ":" + dt.getMinutes() + ":" + dt.getSecondsC)};
Массивы
Язык сценариев JavaScript допускает работу с массивами встроенных объектов,
объектов браузера и любых других объектов.
Вы можете создать массив как объект встроенного класса Array, а затем об-
ращаться к его элементам средствами обычной операции индексации:
var myArray;
myArray = new Array(256);
;пуАггау[0] = 255;
туАггау[1] = 254;
туАггау[255] = 0;
Свойства
Перечислим свойства класса Math. Все они являются математическими констан-
тами, поэтому сценарий JavaScript не может изменять их значение.
Приложение 1. Элементы языка JavaScript 393
Е
Это свойство представляет собой константу е. Приблизительное ее значение
равно 2,72.
Вот пример использования свойства Е:
var nE;
nE = Math.E;
Здесь мы записываем в переменную пЕ значение константы е,
PI
Свойство PI — это число р, то есть константа с приблизительным значением,
равным 3,14.
Пример использования свойства PI:
var nl_;
var nR;
nL = 2 * Math.PI * nfl;
Здесь свойство PI применяется для вычисления длины окружности по ее
радиусу. Формула такова:
1 = 2pR,
где R — радиус окружности.
LN2
Свойство LN2 — константа со значением натурального логарифма числа 2, то есть
1п2.
Пример использования:
var nValue;
nValue = Math.LN2;
LN10
Свойство IN10 — константа со значением натурального логарифма числа 10, то
есть 1пЮ.
Пример использования:
var nValue;
nValue = Math.LNIO;
LOG2E
Это свойство является константой со значением, равным логарифму числа 2 по
основанию е. то есть 1ой
ое2.
Пример использования:
var nValue;
nValue = Math.LOG2E;
LOG10E
Свойство LOG10E — это логарифм числа е по основанию 10, то есть loglOe.
Пример использования:
var nValue;
nValue - Math.LOGlOE;
394 Базы данных в Интернете, Практическое руководство
SQRT2
Свойство SQRT2 — это значение квадратного корня из 2.
Пример использования:
var nValue;
nValue = Math.SQRT2;
SQRT1_2
Свойство SQRT1_2 — это значение квадратного корня из ОД
Пример использования:
var nValue;
nValue = Math.SQFn"L2;
Методы
Перечислим методы класса Math.
abs
Вычисление абсолютного значения.
Пример использования:
var nValueAbs;
nValueAbs = Math.abs(nValue);
Здесь в переменную n V a l u e A b s записывается абсолютное значение перемен-
ной nValue.
acos
Вычисление арккосинуса.
Пример использования:
var nValue;
nValue = Math.acos(nAngle);
asin
Вычисление арксинуса.
Пример использования:
var nValue;
nValue = Math.asln(nAngle);
atan
Вычисление арктангенса.
Пример использования:
var nValue;
nValue = Math.atan(nAngle);
ceil
Вычисление наименьшего целого значения, большего или равного аргументу
функции.
Пример использования:
Приложение 1. Элементы языка JavaScript 395
var nValue;
nValue = Math.ceil(nArg);
COS
Вычисление косинуса.
Пример использования:
var nValue;
nValue = Math.cos(nAngle);
exp
Экспоненциальная функция, значение которой равно числу е, возведенному в
степень аргумента функции.
Пример использования:
var nValueExp;
nValueExp = Math.exp(nValue);
floor
Вычисление наибольшего целого значения, меньшего или равного аргументу
функции.
Пример использования:
var nValue;
nValue = Math.floor(nArg);
log
Вычисление натурального логарифма аргумента.функции.
Пример использования:
var nValue;
nValue = Math.log(nArg);
max
Определение наибольшего из двух значений.
Пример использования:
var nValuel;
var nValue2;
var nValueMax;
nValueMax = Math.max(nValue1, nValuel);
J
min
Определение наименьшего из двух значений.
Пример использования:
var nValuel;
var nValue2;
var nValueMin;
nValueMin = Math.min(nValuel, nValuel);
396 Базы данных в Интернете. Практическое руководство
pow
Возведение числа в заданную степень.
Пример использования:
var nValue;
nValue = Math.pow(2, 3);
Здесь число 2 возводится в степень 3, а результат, равный 8, записывается в
переменную n V a l u e .
random
Метод random возвращает случайное число в интервале от 0 до 1.
Пример использования:
var nflandomValue;
nRandomValue = Math.random();
round
Метод r o u n d предназначен для выполнения округления значения аргумента до
ближайшего целого. Если десятичная часть числа равна 0,5 или больше этого
значения, то выполняется округление в большую сторону, если меньше — в мень-
шую.
Пример использования:
var nValue;
nValue = Hath.round(I.B);
После выполнения округления значение n V a l u e равно 2.
sin
Вычисление синуса.
Пример использования:
var nValue;
nValue = Math.sin(nAngle);
sqrt
Вычисление квадратного корня от аргумента.
Пример использования:
var nValueSqrt;
nValueSqrt = Math.sqrt(nArg);
tan
Вычисление тангенса.
Пример использования:
var nValue;
nValue = Math.tan(nAngle);
getDate
Определение даты, хранящейся в объекте класса Date.
Метод возвращает значение календарной даты в диапазоне от 1 до 31.
398 Базы данных в Интернете. Практическое руководство
Пример использования:
var dtNewDate;
var nDate;
dtNewDate = new Oate();
nDate = dtNewDate.getDate();
getDay
Определение номера дня недели, хранящегося в объекте класса Date-
Метод возвращает 0 для воскресения, 1 — для понедельника и т. д.
Пример использования;
nDay = dtDate.getDayO;
getHours
Определение количества часов, прошедших после полуночи.
Пример использования:
nHours = dtDate.getHoursO;
getMinutes
Определение количества минут, прошедших с начала часа.
Пример использования:
nMinutes = dtOate.getMinutesO;
getMonth
Определение количества месяцев, прошедших с января.
Пример использования:
nMonth = dtDate.getMonth();
getSeconds
Определение количества секунд, прошедших с начала минуты.
Пример использования:
nSeconds = dtDate.getSecondsO;
getTime
Определение времени для заданного объекта класса Date.
Метод getTime возвращает количество миллисекунд, прошедших с 1 января
1970 года.
Пример использования:
nMilliseconds = dtDate.getTimeO;
getTimeZoneOffset
Определение смещения локального времени относительно времени по Гринви-
чу (в миллисекундах).
Пример использования:
nOffsetMilliseconds = dtDate. getTitneZoneOffset();
Приложение 1. Элементы языка JavaScript 399
getYear
Метод getYear возвращает год, хранящийся в объекте класса Date.
Пример использования:
nYear = dtDate.getYearf);
parse
Метод p a r s e возвращает количество миллисекунд, прошедших с 00 часов
00 минут 1 января 1970 года по время, указанное в параметре функции. Для вы-
зова этого метода Вам не нужно создавать объект класса Date, достаточно про-
сто сослаться на имя этого класса:
nMS = Date.parse(szDataString);
Через параметр szDataString Вы можете указать время, например так:
"12 Get 1998 12:00:00"
"12 Oct 1998 12:00:00 GMT"
"12 Oct 1998 12:00:00 GMT+0330"
Первая из этих строк задает локальную дату и время, вторая — дату и время
по Гринвичу, и, наконец, последняя — время и дату по Гринвичу со смещением
на 3 часа и 30 минут.
Метод parse обычно применяют вместе с конструктором объекта Date или с
методом setTime, который мы рассмотрим ниже.
setDate
Метод setDate используется для установки календарной даты в объекте класса
Date.
Пример использования:
dtNewDate.setDate(nDateNumbe г);
Параметр nDateNumber может принимать значения от 1 до 31.
setHours
Метод setHours используется для установки количества часов, прошедших после
полуночи, в объекте класса Date.
Пример использования:
dtNewDate.setHours(nHours);
Параметр nHours может принимать любые положительные или отрицатель-
ные значения. При необходимости происходит соответствующее изменение ка-
лендарной даты, записанной в объекте класса Date.
setMinutes
Метод setMinutes используется для определения количества минут, прошедших
с начала часа, в объекте класса Date.
Пример использования:
dtNewDate.setNinutes(nHinutes);
400 Базы данных в Интернете. Практическое руководство
setMonth
Метод setMonth используется для установки номера месяца, прошедшего с на-
чала года, в объекте класса Date.
Пример использования:
dtNewDate.setMonth(nMonth);
Параметр nMonth может принимать любые положительные или отрицатель-
ные значения. При необходимости происходит соответствующее изменение ка-
лендарной даты, записанной в объекте класса Date.
setSeconds
Метод setSeconds применяется для определения количества секунд, прошедших
с начала минуты, в объекте класса Date.
Пример использования:
dtNewDate.setSeconds(nSeconds);
Параметр nSeconds может принимать любые положительные или отрицатель-
ные значения. При необходимости происходит соответствующее изменение ка-
лендарной даты, записанной в объекте класса Date.
setTime
С помощью метода setTime можно установить дату в объекте класса Date, соот-
ветствующую заданному количеству миллисекунд, прошедших с 1 января
1970 года.
Пример использования:
dtNewDate.setTime(nMilliseconds);
setYear
Метод setYear применяется для определения номера года в объекте класса Date.
Пример использования:
dt NewDate.setYea r(nsetYea г);
toGMTString
Метод toGMTString предназначен для преобразования даты в строку, записанную
в стандартном формате времени по Гринвичу (GMT).
toLocaleStrlng
Аналогично предыдущему, однако вместо времени GMT используется локаль-
ное время.
итс
Метод UTC преобразует дату, заданную параметрами метода, в количество мил-
лисекунд, прошедшее с 1 января 1970 года.
Приложение 1. Элементы языка JavaScript 401
При использовании метода UTC, так же как и метода parse, Вам не нужно со-
здавать объект класса Date:
nMillisecond = Date.UTC{year, month, date, hours, min, sec, ms);
Встроенные функции
В этом разделе перечислены несколько полезных встроенных функций, которые
Вы можете использовать в своих сценариях JavaScript.
eval
Функция eval предназначена для преобразования текстовой строки в численное
значение. Через единственный параметр она получает текстовую строку и вы-
числяет ее как выражение языка JavaScript. Функция возвращает результат
вычисления:
var nValue - Eval(szStr);
parselnt
Эта функция предназначена для преобразования текстовой строки в целочислен-
ное значение. Строка передается функции через параметр:
var nValue = parselnt(szStг);
parseFloat
Функция parseFloat пытается преобразовать текстовую строку в число с пла-
вающей точкой. Текстовая строка передается этой функции через первый пара-
метр, а основание счисления — через второй:
var nFloat = parseFloat(szStr, n R a d i x ) ;
escape
Средствами функции escape сценарий JavaScript может закодировать текстовую
строку с применением URL-кодировки. В этой кодировке специальные симво-
лы — пробел или символ табуляции — преобразуются к следующему виду: Кхх,
где хх — шестнадцатеричный код символа.
Вот пример использования этой функции:
var szURL = escape(szStr);
unescape
Функция unescape выполняет действие, прямо противоположное действию фун-
кции unescape — перекодирует строку из URL-кодировки в обычную текстовую
строку:
var szStr = unescape(szURL);
П Р И Л О Ж Е Н И Е 2
Объект Server
Этот объект обеспечивает сценариям доступ к методам и свойствам сервера. С
их помощью выполняются такие операции, как создание объектов, перекодиро-
вание строк и т. д,
Свойства
Данный объект имеет только одно свойство ScriptTimeout. Оно определяет время
работы сценария перед возникновением состояния тайм-аута.
Пример использования:
<Х Server.ScriptTimeout = 150 Ъ>
Здесь устанавливается интервал, равный 150 секундам (по умолчанию он
равен 90 секундам).
Методы
Рассмотрим методы объекта Server.
CreateObject
Этот метод применяется серверными сценариями для создания экземпляра эле-
мента управления ActiveX.
Пример использования:
var connect;
connect = Server.CreateObject("ADODB.Connection");
Здесь мы создаем объект A D O D B . C o n n e c t i o n , передавая методу CreateObject
идентификатор объекта.
Execute
Метод предназначен для вызова файла ASP из серверного сценария. Вызванный
файл обрабатывается так, как будто он является частью вызывающего файла
ASP.
Пример использования:
Server.Execute("asp2.asp");
Приложение 2. Встроенные объекты ASP 403
GetLastError
Этот метод доступен до момента, когда сценарий отправил пользователю ответ.
Он возвращает объект A S P E r r o r , описывающий состояние возникшей ошибки.
HTMLEncode
Метод HTMLEncode преобразует текстовую строку ANSI в строку с кодировкой
HTML, заменяя символы пунктуации специальными символьными объектами
типа «<».
URLEncode
Аналогичен предыдущему, но выполняется кодировка URL. В частности, про-
белы заменяются символами «+», а символы пунктуации преобразуются к виду
Ххх, где хх — шестнадцатиричный код символа.
MapPath
Выполняет отображение виртуального или логического пути на физический
путь сервера. Позволяет получить физический путь для заданного виртуально-
го пути.
Например, в результате выполнения следующей строки в переменную path
записывается физический путь, по которому находится текущая страница ASP:
path = server.MapPath(Request.ServerVariables("PATH_INFO"});
Transfer
Метод T r a n s f e r передает информацию о текущем состоянии в другой файл ASP
для дальнейшей обработки.
Пример использования:
Server.Transfer("nextasp.asp");
В результате обработка будет продолжена в файле nextasp.asp.
Объект Request
Объект R e q u e s t предоставляет доступ к информации, переданной браузером
через заголовок HTTP: использованный метод передачи данных (POST или GET),
значения Cookie и т. д.
Для объекта Request определены наборы, свойства и методы.
Наборы
Наборы могут содержать один или несколько объектов.
Cookies
В этом наборе хранятся значения Cookie, переданные браузером.
Ниже показано, как можно извлечь из набора значение Cookie с именем
NameCookie:
nc = Request.CookiesC'NameCookie") ;
404 Базы данных в Интернете. Практическое руководство
Form
Этот набор содержит значения из полей формы.
Ниже мы показали, как сценарий определяет количество элементов в набо-
ре с именем Colors:
i = Request.FormC'Colors").Count;
Первый элемент набора извлекается следующим образом:
clr = Request.Forn("FavoriteFlavor")(1) ;
Мы широко использовали набор Form в наших примерах серверных сценари-
ев, работающих с формами.
QueryString
Набор Q u e r y S t r i n g содержит данные, передаваемый как часть адреса URL стра-
ницы ASP.
ClientCertificate
Этот набор хранит такие значения клиентского сертификата, как серийный но-
мер, признак действительности сертификата, дата истечения срока действия
сертификата и т. д.
ServerVariables
Набор S e r v e r V a r i a b l e s хранит значения из предопределенных переменных сре-
ды. Некоторые из них позволяют определить параметры системы посетителя
сервера и представляют большой интерес для разработчиков.
В таблице П-13 перечислены некоторые, наиболее интересные, на наш взгляд,
переменные среды, доступные через набор S e r v e r V a r i a b l e s .
Таблица П-13. Переменные среды
Параметр Описание
ALL_HTTP Все заголовки HTTP, отправленные браузером посетителя
ALL_RAW Аналогичен предыдущему, но заголовки представлены в
необработанном виде (без префикса НТТР_ и без преобразо-
вания символов в заглавные буквы)
CONTENT^LENGTH Размер данных, отправленных браузером
CONTENTJTYPE Тип данных, полученных от браузера
НТТР_АССЕРТ Заголовок Accept
HTTP_ACCEPT_LANGUAGE Строка, описывающая национальный язык, который следуег
использовать для отображения содержимого
HTTP_USER_AGENT Строка описания браузера посетителя
НТТР_СООК1Е Строка значений Cookie
HTTP_REFERER Адрес URL для перенаправления.
HTTPS Содержит строку «ON*-, если передача данных выполняется
по защищенному каналу SSL, или «OFF», если применяется
незащищенный канал
LOGON_USER Учетная запись Windows NT, с которой подключился посе-
титель сервера
Приложение 2. Встроенные объекты ASP 405
Свойства
Для данного объекта определено только одно свойство TotalBytes. Оно содер-
жит количество байт данных, полученных в результате выполнения запроса.
Методы
Для объекта Request определен метод BinaryRead, позволяющий извлечь данные,
отправленные серверу как часть запроса POST.
Объект Response
Объект R e s p o n s e используется для управления информацией, отправляемой
клиентским сценарием в браузер посетителя. Он позволяет передавать данные
непосредственно, выполнять переадресацию браузера на другой адрес URL, a
также устанавливать значения Cookie.
Наборы
В объекте Response предусмотрен набор Cookies, позволяющий устанавливать
значения Cookie. Об использовании этого набора мы рассказали в третьей гла-
ве нашей книги.
Свойства
Рассмотрим свойства объекта Response.
Buffer
Признак буферизации вывода страницы. Если вывод буферизован, в этом свой-
стве хранится значение true, в противном случае — false.
406 Базы данных в Интернете. Практическое руководство
Методы
Ниже перечислены методы объекта Response.
AddHeader
Добавление к создаваемой странице нового произвольного заголовка.
AppendToLog
Добавление строки в журнал сервера Web.
BinaryWrite
Запись на страницу двоичных данных для использования клиентскими объек-
тами. Выполняется без преобразований символов.
Приложение 2. Встроенные объекты ASP 407
Clear
Удаление данных из буфера вывода создаваемой страницы HTML (без стирания
заголовков HTTP).
End
Прекращение обработки сценария.
Flush
Передача содержимого буфера клиенту и последующая очистка буфера.
Redirect
Переадресация браузера посетителя по адресу URL, указанного в параметре
данного метода.
Write
Запись текстовой строки в формируемый документ HTML. Для сокращения
вместо этого метода используется символ «=».
Объект Application
Как Вы знаете из третьей главы нашей книги, объект A p p l i c a t i o n позволяет со-
здавать переменные, доступные одновременно всем пользователям, работающим
с Вашим приложением.
Для этого объекта определены наборы, методы, а также события.
Наборы
В объекте A p p l i c a t i o n определены два набора — Contents и StaticObjects,
Contents
Этот набор содержит все элементы, добавленные приложением при помощи
сценария.
Методы Contents. Remove и Contents. RemoveAll позволяют удалить выбранные
или все элементы соответственно.
StaticObjects
Набор StaticObjects содержит все статические объекты, добавленные при по-
мощи тега <OBJECT>.
Методы
С помощью методов Lock и Unlock сценарий может соответственно запретить или
разрешить доступ к набору переменных объекта A p p l i c a t i o n .
События
События, возникающие при работе посетителей с приложением ASP, обрабаты-
ваются при помощи сценария, расположенного в файле global.asa. Мы подроб-
но описали эту процедуру в третьей главе нашей книги.
Перед созданием нового сеанса возникает событие A p p l i c a t i o n _ O n S t a r t , a
перед завершением работы приложения — событие A p p l i c a t i o n _ O n E n d .
408 Базы данных в Интернете. Практическое руководство
Объект Session
Объект Session применяется для хранения информации, имеющей отношение
к сеансам отдельных посетителей сервера, а не ко всему приложению в целом.
В рамках этого объекта определены наборы, свойства, методы и события.
Наборы
В объекте Session определены два набора — Contents и StaticObjects.
Contents
Набор Contents содержит все элементы, созданные для сеанса при помощи сце-
нария.
StaticObjects
Этот набор, в отличие от предыдущего, содержит статические элементы, создан-
ные при помощи тега <OBJECT>.
Свойства
Четыре свойства объекта Session позволяют определить четыре параметра сеанса.
CodePage
Кодовая страница, применяемая для отображения динамически создаваемой
страницы. Для символов кириллицы в Windows применяется кодовая страница
1251.
LCID
Идентификатор локального контекста (идентификатор национального языка),
SessionID
Идентификатор текущего сеанса.
Timeout
Период тайм-аута, по истечении которого сеанс автоматически прекращает ра-
боту и закрывается.
Методы
Метод Abandon, определенный для объекта Session, выполняет аварийное завер-
шение сеанса. При этом уничтожаются все объекты, созданные в рамках сеанса,
и освобождаются все ресурсы, заказанные сеансом для своей работы.
События
В файле global.asa Вы можете выполнить обработку событий Session_OnStart и
Session_OnEnd.
Первое из них возникает в начале работы сеанса, а второе — перед ее завер-
шением.
Предметный указатель
н LivcScript 7
LN10 393
HclpContext 130,214
HeipFile 130,214 LN2 393
History 22 LoadLibrary 345
history 22 location 22
HRESULT 2 1 1 Location 22
HTML 1 log 395
HTTP 138 LOC10E 393
HTTP_ACCEPT 404 LOG2E 393
HTTP_ACCEPT_LANGUAGE 404 LOGONJJSER 4(M
I1TTP_COOKIL 404 M
IITTP_REFERER 404
Math 391,392
HTTP_USER_AGENT 404
Math.floor 58
HUpExtensionProc 311, 312, 329 Mai h.random 39
HTTPS 404 max 395
I MFC 208, 266
TAccessoT 233, 243 Microsoft InterDev 118
IColumnsInfo 233,243 Microsoft Management Console 87
IC ommand Prepare 2 33 Microsoft OLE DB Provider 120
ICommandProperties 233 Microsoft; Visual Basic 207
ICommandText 233,242 Microsoft Visual C++ 207
IDBCreatcSession 232,241 min 395
IDBInitialize 232,240 money 247
IDBProperties 232,239 MSDASQL 120
IDispatch 126 MSDN Library 19
lErorlnfo 234 MTASend 350
lErrorlnfo 208 N
lErrorLookup 234 Name 133
I Error Records 234 NativeError 130,214
Image 22 navigator 22
image 246 Netscape Navigator 10
IMS 12 null 383
InctAddress 372 number 130, 214
I N I T G U I D 253
Number 130,214,391
Initialize 232; 240 numeric 246
inncrText 35
instanceof 132 0
ini 246 Oak 7
IRowset 233, 242, 243 Object 391
IRowsetlnfo 233,243 ODBC 11,120.265
ISAPI 4.208,289,310 ODBC Data Sources 143
ISQLErrorlnfo 234 OLE Automation 12
[Support Error Info 234 OLE DB 12, 120,231
[Unknown 242 onMoiiscOut 63
onMouseOver 63
Open 123
Java 7,207,359 open 48
JavaScript 7
JPEG 60
J Script 7,118 Parameter 124
Parameters 124
L parse 399
LARGE_INTEGLR 247 parseFloat 384,401
Layer 22 parselnt 384,401
Layout Manager 360 Perl 5
Link 22 Plugin 22
links 22 POST 293
и Базы данных в Интернете. Практическое руководство
toGMTString 73,400 К
loLoraleString 400 ключевые слова JavaScript 382
Transact-SQL 121.233 кодировка URL 38
Type 133 количество активных сеансов 105
и курсор 128
кэширование страниц 39. 100, 176, 194
unescape 401
Unicode 125
Uninitialize 232
м
массивы JavaScript 392
URL 1, 373
URLConnection 375 н
UTC 400 набор 122
- строк 233
V
Value 133 О
varbinary 246 объекты браузера Netscape Navigator 22
varchar 140, 246 оператор сравнения LIKE 170, 198
VARIANT 208, 221
VARIANT_FALSE 225 п
VR Script 7, 207 параметры окна браузера 49
VSAM 12,231 параметры тега <IMG> 61
переменные сеанса 97
w привязка данных 250
Win32 208, 222 приложение ASP 94
window 22 проверка
window.location 38 - почтового адреса 28
WritcClient 319 - формы 25, 92
процессинговый центр 16
анимационное изображение 63
расширение 1SAPI 310
анкер 53
регистрация посетителей !
i !
аплеты Java 7
сегментированное графическое
оезопасность административного
изображение 61
приложения 148
создание источника данных 142
стандартный поток ввода 293
виртуальный ката/юг оУ Т
- приложения 146
т и н курсора 128
Г Ф
глобальный уникальный иденти-
фильтр 69
фикатор 126 - ISAP1 310
графическая ссылка 61
форма 24, 291
дданные Cookie 80
фрейм 51, 150
X
дссятично-шестнадцатеричиое
хранимая процедура 141
преобразование 56
диапазон календарных дат 28
доступ анонимных пользователей
э
! :. элементы управления ActiveX 9
элементы форм 23
запрет на использование Cookie 85
язык серверного сценария
И язык сценариев 7
импорт библиотеки типов 134, 208
интеллектуальный указатель 208
Библиографический список
Редактор Ю. П. Леонова
Компьютерная верстка В. Б. Хилъченко
Технический редактор Н. Г. Тимченко
Дизайнер обложки Е. В. Козлова
TypeMarketFon/Z,ibrar)> гащвм
Подготовлено к печати
Издательско-торговым ломом «Русская Редакция*
новый
интернет-магазин
http://ITbook.ru
tUi , •
|Ыиевныйкуес
БОГАТЫЙ
ВЫБОР
НИЗКИЕ
ЦЕНЫ
Новые журналы mf \ '
ТОЛЬКО
по подписке журналь
Каталог
Роспечати
истов
2001 г.
«Газеты.
Журналы»
в любом
почтовом
отделении
связи
Журнал
SQL Server
для профессионалов
индекс
79946
индекс
79947
(без дискеты) (с дискетой)
\ Журнал
Visual Basic
для профессионалов
индекс
79951
индекс
79952
(без дискеты) (с дискетой)
Журнал
Active Web
для профессионалов
индекс
79956
индекс
79957
(без дискеты) (с дискетой)
H P V Р Г 1П Я Р Р И П1 II И
I 1 \/ V 11 Л Л Г L 1\ Л L 1\ П Л
тел.; (095) 142-0571; тел./факс: (095) 145-4519
e-mail: info@rusedit.ru; http://www.rusedil.ru
Официальные учебные
пособия Microsoft
гарантия
Вашей квалификации!
Windows 2000
Server
Книги серии «Учебный курс*
для подготовки
к экзаменам MCSE-2000
Windows 2000
Professional
BI П С С П 1 пли
тел.: (095) 142-0571; тел./факс:'(095) 145-4519
e-mail, info@rusedit.ru; http://www.rusedit.ru
газета, которую все ждали
1p://spy.ryberp
одписнои индек
phone.: ( 0 9 5 ) 9 7 6 - 9 2 2 4 , f a x : 9 7 6 - 7 9 9 7 , e-mail: c r @ s l . r u , w w w . c o m p u r u s . r u
КАТАЛОГ
ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
П Р Е Д Н А З Н А Ч Е Н Д Л Я
© Руководителей отделов информатизации
© Системных администраторе»
0 Разработчиков программного обеспечения
© Разработчиков решений для Internet
©Системных интеграторов
О Профессионалов рынка информационных
технологий
СПОСОБ ПОКУПКИ
ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
БЕС! (095)232-0023
www.softline.ru
БЛАНКПОДПИСКИ
ФАМИЛИЯ ИМЯ
ОРГАНИЗАЦИЯ должность
СТРАНА ИНДЕКС ГОРОД
УЛИЦА/ДОМ
ТЕЛЕФОН ФАКС
E-MAIL WWW
HAiitodesk
Диалсгняука
Math Sat 1
Novell ORACLE
VER!TAS VlSiO
Windows 2000
Professional
Ресурсы
Windows 200C
Курс Serve*
мдлодого
йца
Торговый доч «Москва» ул. Тверская. 8. та.1 (095) 229-6483 • в Киеве {Украина):
000 Издательство .Ирина", теп. (044) 2119-0423
«йпекс и К"» Магазин «Книги» г. Зелеипград.
^Техническая книга на Петровке», тел.. (П44) 464-685S
• в Минске (Бвяпргсеня):
Фирменный магазин «Компьютерная и деловая книга» ОТО ..Попурри- тел.. {0171 223-5726
Москва, Ленинский проспект, строение 38, тел.: (095) 773-7269