You are on page 1of 446

Александр Вячеславович Фролов,

Григорий Вячеславович Фролов

в Интернете
практическое руководство по созданию
Web-приложений с базами данных

Издание 2-ое, исправленное

. Р У С С К И РЕШШ
УДК 004.7
ББК 32.973.202
ФЭ1

Фролов А. В., Фролов Г. В.

Ф91 Базы данных в Интернете: практическое руководство по созданию


Web-приложений с базами данных. — Изд. 2-ое, испр. — М.: Издатель-
ско-торговый дом «Русская Редакция», 2000. — 448 с.: ил.

ISBN 5-7502-0174-0

Эта книга —практическое руководство по созданию приложений Web с


базами данных для Интернета, а также для корпоративной интрасети. Здесь
рассмотрены современные технологии, созданные Microsoft для работы с база-
ми данных. Основное внимание уделяется применению ASP, CGI и ISAPI,
ADO, элементов управления ActiveX, аплетов Java, сценариев JavaScript, а так-
же DHTML. Описаны основы использования и создания серверных элементов
управления ActiveX (с применением библиотеки классов ATL), а также апле-
тов Java.
Материал книги проиллюстрирован множеством примеров.
К книге прилагается компакт-диск с примерами программ и другим ПО.
Книга предназначена тем, кто создает собственные серверы Web с базами
данных в Интернете, разработчикам корпоративных баз данных в интрасетях,
студентам высших учебных заведений. Она также полезна при самостоятель-
ном изучении новых технологий, предназначенных для работы с базами данных.

УДК 004.7
ББК 32.973.202

Использованные в примерах л упражнениях названия компаний и продуктов, персонажи


и события являются вымышленными. Любые совпадения с реальными компаниями, про-
дуктами, людьми и событиями являются случайными.
ActiveX. JScript, Microsoft. Microsoft Press, MSDN, MS-DOS, PowerPoint, Visual Basic,
Visual C++. Visual biterDev, Visual SourceSafe, Visual Studio, Win32. Windows и Win-
dows NT являются либо товарными знаками, либо охраняемыми товарными знаками кор-
порации Microsoft в США и/или других странах. Все другие товарные знаки являются
собственностью.соответствующих фирм.

© Фролов А. В., Фролов Г. В., 2000


© Издательско-торговый дом
ISBN 5-7502-0174-0 «Русская Редакция», 2000
Оглавление

Предисловие ко второму изданию XV


Введение XIX
Обзор технологий Интернета XX
Требования к читателям XXI
Структура книги XXIII
Благодарности XXVI

Глава 1 Сервер Web как ядро приложений для Интернета 1


Основы работы сервера Web 1
Пассивные и активные серверы Web 2
Программы CGI 2
Приложения ISAPI 4
Расширения ISAPI 4
Фильтры ISAPI 4
Активные страницы ASP 4
Приложения Web 5
Активность на стороне клиента 6
Клиентские сценарии JavaScript 7
Клиентские сценарии VB Script 7
Aiuierajava 7
Элементы управления ActiveX 9
Проблемы с реализацией клиентской активности 9
Интеграция серверов Web и SQLServer 10
Программный интерфейс DB Library 11
Программный интерфейс ODBC 11
Объектный интерфейс Remote Data Object 12
Объектный интерфейс OLE DB 12
Объектный интерфейс ActiveX Data Objects 13
Проект Интернет-магазина 13
Установка программ и подготовка к работе 17
Операционная система сервера 17
Браузеры 18
SQLServer 18
Сервер Web 18
Microsoft Developer Studio 18
Microsoft FrontPage 19
Глава 2 Сценарии в страницах HTML и DHTML 21
Объектная модель браузера Microsoft Internet Explorer 21
Применение сценариев для создания интерфейса пользователя 24
Начальная регистрация пользователя 25
Ввод календарных дат 28
Загрузка новой страницы в окно браузера 37
VI Базы данных в Интернете. Практическое руководство

Перекодирование содержимого полей форм 38


Отключение кэширования страниц 39
Модальные и немодальные диалоговые панели 39
Модальные диалоговые панели 40
Вывод информационных сообщений 40
Сообщение с выбором одной из двух возможностей 42
Диалоговая панель на базе документа HTML 44
Немодальные диалоговые панели 48
Работа с фреймами 51
Файл описания фреймов 51
Параметры тега <FRAMESET> 52
Параметры оператора <FRAME> 52
Взаимодействие между фреймами 53
Десятично-шестнадцатеричный преобразователь 53
Одновременная замена нескольких документов HTML в окнах
разных фреймов 58
Использование растровых изображений 60
Растровое изображение как объект 61
Динамическая замена растровых изображений 62
Изменение внешнего вида графических ссылок 63
Создание анимационных изображений 64
Ожидание загрузки всех изображений 66
Наложение фильтра на графическое изображение 67
Применение Cookie в клиентских сценариях 69
Выполнение основных операций с Cookie 70
Создание Cookie 71
Создание Cookie расширением сервера Web 71
Создание Cookie в клиентском сценарии 72
Получение значения Cookie 74
Изменение значения параметра Cookie 75
Удаление Cookie 75
Ограничения на использование Cookie 76
Примеры использования Cookie 76
Фиксация повторных посещений страницы 76
Записная книжка Cookie Notepad 78
Настройка параметров документа HTML 81
Настройка браузера для работы с Cookie 84
Отладка клиентских сценариев 85

Глава 3 Применение технологии ASP 86


Основы ASP 87
Простейший пример 87
Пример с циклом 89
Обработка формы 90
Комбинирование клиентского и серверного сценариев 92
Приложения ASP и сеансы 94
Файл global.asa 95
Переменные приложений ASP 96
Практическое применение ASP 97
Создание мастера средствами ASP 97
Подсчет количества активных сеансов .. 105
Оглавление

Использование Cookie в сценариях ASP 107


Регистрация пользователей 111
Передача параметров через скрытые поля форм 115
Отладка сценариев в страницах ASP 118

Глава 4 Связь приложений с базами данных через ADO 120


Основы ADO 120
Программная модель ADO 120
Установка соединения 121
Подготовка команды и параметров 121
Выполнение команды 121
Обработка результатов выполнения команды 121
Обработка ошибок 121
Объекты ADO ."". 122
Объект Connection 122
Объект Command 123
Объект Parameters 124
Объект Recordset 127
Объект Errors 130
Объект Properties 132
Константы ADO 133
Проект Интернет-магазина 134
Создание базы данных 134
Подготовка таблиц 135
Таблица managers 136
Таблица clients 137
Таблица books 139
Таблица orders 140
Подготовка хранимых процедур 141
Создание источника данных 142
Подготовка виртуальных каталогов сервера Web 146
Виртуальный каталог приложения покупателя BookShopClient 146
Виртуальный каталог административного приложения BookShop 146
Приложение покупателя 148
Файл global.asa 152
Страницы входа и начальной регистрации 152
Вход зарегистрированных посетителей 152
Обработка ошибок 155
Хранимая процедура ClientLogin 156
Определение фреймов главной страницы 157
Страница меню команд 158
Страница просмотра списка книг 159
Добавление книги в корзину 161
Страница просмотра содержимого корзины 161
Удаление книги из корзины 165
Административное приложение 166
Файл global.asa 173
Страницы входа 173
Главная страница 175
Страница меню команд 176
Страница с сообщением о подключении 176
Базы данных в Интернете. Практическое руководство

Страницы управления персоналом 177


Просмотр списка сотрудников 177
Создание новой записи 179
Удаление учетной записи сотрудника 181
Редактирование записи сотрудника 182
Редактирование списка книг 184
Просмотр списка книг 184
Добавление новой книги 186
Удаление книги 188
Редактирование описания книги 188
Работа с записями покупателей 192
Форма поиска покупателей.: 192
Просмотр списка зарегистрированных покупателей 194
Удаление записи покупателя 199
Просмотр содержимого корзины покупателя 203
Редактирование регистрационных данных покупателя 204
Работа с ADO в приложениях C++- 207
Импортирование библиотеки типов ADO 208
Обращение к интерфейсам и методам ADO 210
Инициализация СОМ 210
Установка соединения с источником данных 210
Выполнение команды 211
Работа с набором записей 212
Вызов хранимой процедуры '. 213
Обработка ошибок 214
Пример программы 216
Функция login 218
Функция get Managers ; 220
Вызов ADO через функции Win32 222
Обращение к интерфейсам и методам ADO 222
Инициализация СО Ми переменных BSTR 222
Установка соединения с источником данных 223
Выполнение команды 224
Работа с набором записей 224
Пример программы 226
Глава 5 С«язь приложений с базами данных через OLE DB 231
Программная модель OLE DB 232
Инициализация среды выполнения 232
Инициализация источника данных 232
Открытие сеанса 232
Подготовка команды и параметров 233
Выполнение команды 233
Обработка результатов выполнения команды 233
Обработка ошибок 233
Объекты OLE DB 234
Объект SQLOLEDB 234
Создание объекта 234
Подготовка параметров инициализации 235
Установка свойств 238
Инициализация объекта ... ... 240
Оглавление IX

Объект Session 241


Объект Command 241
Создание объекта 241
Определение команды 242
Выполнение команды 242
Интерфейс IRowset и набор записей 243
Получение описания набора записей 243
Подготовка информации для привязки данных 247
Выполнение привязки данных 250
Обработка набора записей 251
Программа OLEDB 253
Глобальные определения 253
Функция main 254
Функция init 255
Функция start Command 256
Функция get_records .-. 257
Использование библиотеки шаблонов ATL 258
Класс CDataSource 258
Класс CSession 259
Класс CCommand 260
Программа ATLOLEDB 262
Глобальные определения 262
Функция main 263

Глава 6 Связь приложений с базами данных через ODBC 265


Программный интерфейс ODBC 265
Структура приложения ODBC 266
Инициализация 266
Инициализация среды выполнения 267
Инициализация среды для установки соединения 268
Установка соединения 268
Подготовка и запуск команды 269
Получение идентификатора команды 269
Запуск команды 269
Обработка результата выполнения команды 270
Привязка полей к локальным переменным 270
Цикл обработки записей 273
Обработка ошибок 273
Извлечение диагностических записей 273
Записи состояния 275
Программа ODBCAPP : 276
Глобальные определения и константы 277
Функция main 277
Функция GetErrorMsgConn 280
Функция GetErrorMsg 282
Запуск хранимых процедур 283
Привязка параметров 283
Запуск процедуры 285
Извлечение значений выходных параметров процедуры 285
Программа ODBCPARAM 286
Базы данных в Интернете. Практическое руководство

Глава 7 Расширения CGI и ISAPI сервера Web 289


Программы CGI и базы данных 290
Немного о фор>мах HTML 290
Передача данных программе CGI 292
Метод GET 292
Метод POST 293
Выбор между GET и POST :
Передача ответа из программы CGI 293
Переменные среды для программы CGI 294
Примеры программ CGI 297
Программа CGIHELLO 297
Программа CONTROLS 298
Программа AREF 303
Доступ к базе данных из CGI 304
Создание приложений ISAPI 310
Принципы работы и структура расширения ISAPI 311
Вызов расширения ISAPI сервером Web 311
Функция GetExtensionVersion 312
Функция HttpExtensionProc 312
Получение данных расширением ISAPI 315
Функция GetServerVariable 316
Функция ReadClient 318
Отправка данных расширением ISAPI 319
Функция WriteCilent 319
Функция ServerSupportFunction 319
Приложение ISHELLO 321
Вызов функций ODBC из ISAPI 324
Обращение к базе данных в отдельном потоке 329

Глава 8 Создание серверных элементов управления ActiveX 332


Первый проект элемента ActiveX 333
Создание проекта 333
Добавление объекта 335
Определение нового свойства 337
Редактирование исходного текста свойства 338
Подготовка страницы ASP 339
Определения методов элемента 341
Автоматическая обработка кредитных карточек 342
Библиотека для имитации интерфейса 343
Тестовая программа для вызова имитатора интерфейса 344
Элемент управления CreditCard 345
Вызов элемента управления CreditCard 348
Отправка почтового сообщения из сценария ASP 350
Основы работы SMTP 351
Элемент управления MTASend ; 352

Глава 9 Применение аплетов Java 359


Система Layout Manager 359
Режим Flow Layout 360
Режим GridLayout .. .. 360
Оглавление XI

Режим BorderLayout 361


Режим CardLayout 361
Режим GridBagLayout 362
Поля gridx и gridy 363
Поля gridwidth и gridheight 363
Поле fill 364
Поле anchor 364
Поля weightx и weighty 364
Поля ipadx и ipady 365
Поле insets 365
Аплет с формой 365
Главный класс аплета GridBag 366
Метод init 367
Поле First name 367
Метка для поля First name 368
Кнопка OK 368
Поле Middle name и метка этого поля 368
Кнопка Cancel 369
Поле Last name 369
Метка поля Last name 369
Поле ZIP 369
Метка поля ZIP 369
Поле Country 370
Метка поля Country 370
Метод actionPerformed -. 370
Класс AppletMsgBox 371
Конструктор класса AppletMsgBox 371
Метод actionPerformed 372
Классы Java для работы в сети 372
Класс InetAddress 372
Класс URL 373
Конструкторы класса 373
Методы класса URL 374
Класс URLConnection 375
Взаимодействие приложений Java и расширений сервера Web 375
Аплет для передачи номера кредитной карточки 376
Инициализация аплета 377
Отправка данных расширению сервера Web 378
Размещение аплета в документе HTML 379
Исходный текст расширения ISAPI 379
Передача параметров странице ASP 380

Приложение 1 Элементы языка JavaScript 382


Переменные 382
Объявление переменных 382
Присвоение значения переменным 383
Типы данных 383
Числа 383
Текстовые строки 383
Логические данные 384
Данные неопределенного типа 384
Базы данных в Интернете, Практическое руководство

Преобразование типов данных 384


Операторы 385
Унарные операторы 385
Бинарные операторы 385
Операторы для выполнения логических операций 385
Операторы сдвига 386
Операторы отношения 386
Оператор присваивания 386
Условные операторы 387
Операторы цикла 387
Оператор for 388
Оператор for-in 388
Оператор while 388
Оператор break 388
Оператор continue 389
Прочие операторы 389
Старшинство операторов JavaScript 390
Функции 390
Встроенные объекты 391
Массивы 392
Встроенный класс Math 392
Свойства 392
Е 393
PI 393
LN2 393
LN10 393
LOG2E 393
LOG10E 393
SQRT2 394
SQRT1_2 394
Методы 394
abs 394
acos 394
asin 394
atan 394
ceil 394
cos 395
exp 395
floor 395
log 395
max 395
min 395
pow 396
random 396
round 396
sin 396
sqrt '. 396
tan 396
Встроенный класс Date 396
Конструкторы класса Date 397
getDate ... 397
Оглавление

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

Приложение 2 Встроенные объекты ASP 402


Объект Server 402
Свойства 402
Методы 402
CreateObject 402
Execute 402
GetLastError 403
HTMLEncode 403
URLEncode , 403
MapPath 403
Transfer 403
Объект Request 403
Наборы 403
Cookies 403
Form 404
QueryString 404
Client Certificate 404
ServerVariables 404
Свойства 405
Методы 405
Объект Response 405
Наборы 405
Свойства 405
Buffer 405
CacheControl ... 406
XIV Базы данных в Интернете. Практическое руководство

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

Предметный указатель 409

Библиографический список 414

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
Популярные издания

Web-узел издательства компьютерной литературы «Русская Редакция» мы


построили на платформах Microsoft Windows NT 4.0, Microsoft Internet Infor-
mation Server 4.0 и Microsoft SQL 7.0 с применением ASP, JavaScript, серверных
элементов управления ActiveX и расширений ISAPI. То есть использовали тех-
нологии, о которых рассказывали в этой книге.
XVI Базы данных в Интернете. Практическое руководство

Заметим, что вся информация о книгах, отзывы, новости и другие сведения


хранятся в базе данных. Организован запросный режим по различным атрибу-
там книг, таким, как название, фамилия автора, номер ISBN. Посетителям пре-
доставлена возможность оценивать книги по 5-бальной системе и высказывать
свои мнения о них.
Для наполнения информационного содержания узла Web и выполнения те-
кущих работ создано специальное административное приложение. Оно позво-
ляет заносить в базу данных все атрибуты книг, сведения об издательствах и
сериях книг, просматривать отзывы (публикуя их или удаляя) и т. д.
Для размещения на узле Web изображений обложек книг и их содержимого
в виде PDF-файлов один из авторов этой книги разработал расширение ISAPI,
предназначенное для загрузки файлов через браузер при помощи технологии,
описанной в RFC1867 (Form-based file Upload in HTML). В результате все ос-
новные технологические процессы, связанные с изменением информационного
наполнения узла, оператор сможет выполнять, не привлекая квалифицирован-
ных Web-мастеров или программистов.
Для выполнения почтовой рассылки мы разработали серверный элемент
управления ActiveX, обеспечивающий передачу почты по протоколу SMTP из
сценариев JavaScript, и некоторые вспомогататьные серверные элементы управ-
ления ActiveX.
Для создания Интернет-магазина IT Book мы применили те же технологии,
что и для обновления Web-узла.
31 hltp://m»«.rutedit ru/RRihup/tlefault «ap?T=IHFXPH - Microsoft Internet Explore
• . £]R3Ae

I J

книги no с--з»ре»*иив| ч ннфармьцноннвм т ^ х к о я с г м вн


book«ru

| Учебный курс

Б.1!ы данных в Каипьютерныб сети*. Н О В О С 1 и!


Интернете. Учеоный курс
/Microsoft Cofp
созламил Web-прнгкжвиий с
и даньы> Настоящий учебный курс
Александр Фролов. — перевод третьего
Григории Фропов издания бестселлера ПИК
Эта книга представляет Microsoft Press 03.09.83 13 47-37
собой практическое «Networking Essentials>.
дополненного га «он.мтей
руководство по создании
приложений Web с базами материалами о новинка*
данные для Интернета, а в сетевых технологиях и
также для корпоративной особенностям Microsoft
интрэсети В ней Windows NT Server 4 О
рассмотрены
современные технологии,
созданные Microscrf- для
работы с базами дэннык.
подробнее... Q1 Ю.0818 32-25

Служоа клталогпв
распределенных Active Diiectory в
приложений НА Microsoft Windows 200(1.
Microsoft Visual Basic Ученным курс
6.0. Учеоныи курс Microsoft Coip
Microsoft Cap. Книга содержит всю
Предисловие ко второму изданию XVII

Интернет-магазин снабжен рубрикатором для поиска книг по темам, систе-


мой рассылки сообщений ленты новостей по подписке, а также системой сбора
и обработки заказов, использующей концепцию корзины покупателя. Стоимость
заказа рассчитывается автоматически и зависит от общего веса книг (в граммах),
а также от региона, в который выполняется доставка.
Административное приложение, управляющее работой Интернет-магазина,
поддерживает все необходимые средства для изменения информационного на-
полнения базы данных и узла Web Интернет-магазина, а также для работы с
клиентами и заказами (от проверки заказа до распечатки счета).
Создание приложений для Интернета кажется нам весьма интересной темой,
посему мы начинаем работу над новой книгой с ориентировочным названием
«Создание Web-приложений», в которой расскажем о построении Web-прило-
жений на платформе операционной системы Microsoft Windows — от подготов-
ки простейших страниц HTML и применения сценариев ASP до технологичес-
ких процессов, реализованных в Интернет-магазинах, и узлов Web, интегриро-
ванных с базами данных.
Эта книга, как мы надеемся, будет полезна и тем, у кого нет ни малейшего
опыта в построении Web-приложений, и тем, кто уже попробовал себя в роли
разработчика Web-приложений. Мы рассмотрим технологии, которые хорошо
зарекомендовали себя при создании сложных активных узлов Web, а также рас-
скажем о некоторых новшествах. Ориентировочно наша книга появится в про-
даже в конце 2000 или в начале 2001 года.
И последнее: мы уведомляем всех заинтересованных лиц, что у нас сменились
адреса. Теперь ищите нас по http://www.frolov.pp.ru, alexandre@frolov.pp.ru и
grigory@frolov.pp.ru.
Введение

Мы не слишком преувеличим, если скажем, что бурное развитие глобальной сети


Интернет оказывает огромное влияние на многие сферы деятельности челове-
ка. И уж, конечно, Интернет вызвал поистине революционные изменения в ин-
дустрии программного обеспечения. Появилась новая категория приложений,
специально разработанных для Интернета и учитывающих особенности серве-
ров Web. Последние играют в «жизни» приложений основополагающую роль,
именно поэтому программы для Интернета часто называют приложениями Web.
За примером далеко ходить не надо — это многочисленные Интернет-мага-
зины, поисковые и справочные серверы, системы для Интернет-телефонии и
обмена сообщениями в реальном времени, системы передачи живого видео че-
рез Интернет и т. д.
Для приложений Web характерно то, что пользователи общаются с ними при
помощи обыкновенных браузеров, таких, как Microsoft Internet Explorer. При
этом доступ к приложениям Web осуществляется как к обычным статическим
страницам HTML по их адресу URL.
Однако по своему поведению приложения Web совсем не похожи на стати-
ческие страницы HTML. При посредничестве сервера Web они способны обра-
щаться к различным активным объектам, сервисам и системам, таким, например,
как базы данных. Так, в ответ на запрос, введенный пользователем в окне брау-
зера, сервер Web способен сформировать отчет и отобразить его в том же окне,
Для получения данных при этом сервер Web генерирует запрос к базе данных.
Многоуровневый и компонентный подход к созданию приложений Web за-
метно сокращает сроки разработки и облегчает отладку. Многие достаточно
сложные системы можно проектировать без применения таких сложных в ис-
пользовании языков программирования, как C++.
Важно отметить, что приложения Web способны работать не только в Интер-
нете, но и в корпоративных интрасетях. Применение браузера на компьютере
конечного пользователя в качестве основного приложения для доступа к базам
данных и другим корпоративным системам значительно уменьшает стоимость
сопровождения крупных локальных сетей. При этом не только упрощается про-
цедура установки программного обеспечения на рабочие станции сети, но и об-
легчается сопровождение корпоративных баз данных и других систем, работа-
ющих централизованно на специально выделенных серверах.
Для того чтобы показать преимущества применения приложений Web в ин-
трасетях, приведем простой пример.
Предположим, две-три сотни сотрудников некоторой фирмы работают с од-
ной базой данных MS SQL Server. При классической клиент-серверной архитек-
туре на каждую рабочую станцию помимо операционной системы необходимо
XX Базы данных в Интернете. Практическое руководство

установить клиент SQL Server и настроить его. Если клиентские приложения


созданы при помощи Borland Delphi или аналогичного средства, каждой рабо-
чей станции потребуется дополнительный клиентский модуль, необходимый для
работы с базой данных.
Всякий раз при внесении изменений в прикладную программу или при мо-
дификации версии сервера SQL Server администратору сети скорее всего потре-
буется обновлять параметры всех компьютеров пользователей. Таких компью-
теров — сотни, поэтому корректировка займет не одну неделю.
Если же для обращения к базе данных применяется браузер, на компьютеры
рабочих станций достаточно установить только операционную систему Windows.
Вся настройка будет заключаться в том, чтобы указать, какая страница сервера
Web должна загружаться в окно браузера по умолчанию. Очевидно, эта проце-
дура намного проще установки и настройки клиента сервера базы данных.
Так как все прикладное программное обеспечение хранится и работает на
сервере, внесенные в него изменения сразу становятся доступными на всех ра-
бочих станциях. Администратору больше не придется обходить две сотни поль-
зователей только для того, чтобы изменить имя сервера базы данных или уста-
новить новый клиент. Удобно это и в случае выхода из строя рабочей станции —
администратор может быстро заменить ее другой, из резерва, и настроить ее
автоматически, индивидуальная настройка замененного компьютера не потре-
буется.
Справедливости ради следует заметить, что клиент-серверную архитектуру
можно применять и в Интернете. Однако особенности этой сети делают исполь-
зование данной архитектуры крайне неэффективной,
Во-первых, между сервером и клиентом иногда располагаются прокси-серве-
ры и брандмауэры, затрудняющие или даже делающие невозможным доступ к
серверу базы данных.
Во-вторых, клиент-серверная архитектура предполагает установку на компь-
ютере пользователя специального клиентского программного обеспечения. Ко-
нечно, его можно загрузить через Интернет и затем установить, однако это дли-
тельная и утомительная для пользователя процедура.
В-третьих, пользователи Интернета работают с самым разным программным
обеспечением — Unix, OS/2, Windows, MAC OS или каким-то еще. Если созда-
тель базы данных желает обеспечить доступ к ней максимальному количеству
пользователей, ему придется подготовить клиентские программы для всех мыс-
лимых компьютерных платформ, а это нереально.
Кроме того, браузеры есть практически везде. Порт 80, применяемый для
обращения к серверам Web, обычно никогда не блокируется прокси-серверами
и брандмауэрами, поэтому здесь проблем тоже не предвидится. Что же касает-
ся совместимости браузеров, то в этой книге мы расскажем, как сделать Ваше
приложение Web совместимым с наиболее популярными браузерами.

Обзор технологий Интернета


При создании приложений Web применяется множество технологий, с которы-
ми мы постепенно будем Вас знакомить. А сейчас — кратко о том, что Вам нуж-
но знать, чтобы успешно создавать приложения для Интернета.
Введение XXI

ф Язык разметки гипертекстов HTML.


Этот язык широко используется для создания страниц сервера Web. Несмот-
ря на изобилие визуальных средств проектирования документов HTML, глу-
бокое знание языка HTML, на наш взгляд, необходимо. В частности, оно
нужно для создания приложений Web, хорошо совместимых с различными
браузерами и по-настоящему эффективных.
В нашей книге Вы тем не менее не найдете подробного описания языка
HTML, хотя по ходу изложения мы будем делать необходимые пояснения.
Языку HTML посвящено много книг, и мы надеемся, что Вы без труда их
отыщете.
ф Динамический язык гипертекстовой разметки DHTML.
Он позволяет создавать динамические интерактивные страницы. Несмотря
на то, что объектные модели наиболее популярных браузеров сильно разли-
чаются, интенсивное применение DHTML оправдано в первую очередь в тех
случаях, когда на компьютере пользователя установлен браузер Microsoft
Internet Explorer. Это возможно в интрасетях или при создании администра-
тивных приложений Web, когда разработчик может установить на компью-
тере пользователя заранее выбранный браузер.
В этой книге мы не стали во всех подробностях описывать DHTML, а заост-
рили внимание на его практическом применении при создании интерактив-
ных страниц.
ф Языки сценариев JavaScript и VB Script.
Сценарии, написанные на языках JavaScript и VB Script, применяются как на
стороне клиента, так и на стороне сервера Web. Клиентские сценарии встро-
ены в документы HTML (или DHTML), загружаемые пользователем в окно
браузера. Эти интерпретируемые программы, исполняемые браузером, спо-
собны оживить статические документы HTML; они связывают различные
объекты, расположенные внутри таких документов. Серверные сценарии
исполняются на сервере и служат для динамического формирования доку-
ментов HTML, отправляемых пользователю.
К сожалению, клиентские сценарии работают по-разному в различных брау-
зерах, Поэтому для обеспечения совместимости приходится принимать спе-
циальные меры. Серверные сценарии, напротив, не вызывают никаких про-
блем совместимости, так как исполняются непосредственно на компьютере
сервера Web.
В нашей книге мы пользуемся и клиентскими, и серверными сценариями
JavaScript. Сценариям полностью посвящена вторая глава нашей книги. Кро-
ме того, в приложении к книге Вы найдете краткие справочные материалы,
касающиеся языка сценариев JavaScript, — они пригодятся Вам для практи-
ческой работы.
ф Активные страницы Active Server Pages (ASP).
Технология активных серверных страниц ASP является ключевой для созда-
ния приложений Web на базе сервера Microsoft Internet Information Server,
поэтому в нашей книге мы посвятили ASP отдельную главу.
XXII Базы данных в Интернете. Практическое руководство

Приложения Web на базе Microsoft Internet Information Server основаны на


наборе текстовых файлов с расширением имени asp. Б первом приближении
это документы HTML, в которые встроены серверные сценарии JScript (вер-
сия JavaScript, созданная Microsoft) или VB Script. Именно эти сценарии осу-
ществляют обращение к базе данных или к другим активным серверным
объектам.
4 Технология ActiveX Data Objects (ADO).
Она имеет самое непосредственное отношение к базам данных. Посредством
объектов ADO серверные сценарии ASP обращаются к базе данных, Объект-
ная модель ADO фактически предоставляет в распоряжение разработчика
простой набор объектов и методов для доступа к базам данных. Объекты
ADO вызываются не только из сценариев, но и из обычных приложений
Windows, расширений CGI и ISAPI сервера Web, а также из других объектов
ActiveX.
В нашей книге Вы найдете не только краткое описание интерфейсов ADO,
но и практические примеры использования этой технологии.
+ Интерфейсы DB Library, ODBC и OLE DB.
Приложения, написанные на традиционных языках программирования, мо-
гут обращаться к базам данных через естественный прикладной интерфейс
базы данных (DB Library) или через прикладной интерфейс ODBC. В этом
случае обращение выполняется посредством вызова соответствующих фун-
кций программного интерфейса. Что же касается интерфейса OLE DB, то он
основан на модели компонентных объектов СОМ.
Перечисленные выше интерфейсы недоступны из серверных сценариев, по-
этому Вам удастся их использовать только в обычных приложениях или в
расширениях CGI и ISAPI сервера Web. Последний случай мы и рассмотрим
в нашей книге.
ф Расширения CGI и ISAPI сервера Web.
Расширения CGI и ISAPI сервера Web широко применяются вместе с ASP
при создании активных серверов Web. Они представляют собой специальным
образом составленные программы, выполняющиеся на компьютере сервера
Web. Расширения сервера Web способны принимать данные от пользовате-
ля, динамически создавать новые документы HTML и отправлять их обратно.
Фактически Вы можете создавать приложения Web с базами данных только
с использованием обычных статических страниц HTML и расширений CGI
и ISAPI сервера Web. Однако такое решение чаще всего намного сложнее ре-
ализовать, чем решение, основанное на ASP и ADO.
ф Элементы управления ActiveX.
Как и сценарии JavaScript и VB Script, элементы управления ActiveX приме-
няют как на стороне сервера, так и на стороне клиента. Фактически они пред-
ставляют собой библиотеки динамической компоновки DLL, работающие
либо на компьютере пользователя, либо на сервере.
Если элемент управления ActiveX работает на стороне клиента, то он может
быть встроен в документ HTML и иметь или не иметь собственное окно. Об-
ладая неограниченным доступом к ресурсам локального компьютера, элемент
Введение XXIII

управления ActiveX способен получить и передать на сервер Web практичес-


ки любую информацию. Эта возможность не всегда используется при пере-
даче данных через Интернет из соображений их безопасности, однако в инт-
расетях она иногда оказывается очень полезной.
Элемент управления ActiveX допустимо также устанавливать на сервере Web,
дабы расширить его возможности. Например, Вы можете создать такой ком-
понент для снятия денег с кредитной карточки клиента, для автоматизиро-
ванной регистрации купленного программного обеспечения или для выпол-
нения других служебных функций.
Относительно недавно корпорация Microsoft создала библиотеку шаблонов
ActiveX Template Library (ATL). Ее применение упростило создание элемен-
тов управления ActiveX настолько, что оно перестало быть уделом узкого
круга профи, досконально разбирающихся в тонкостях реализации модели
компонентных объектов СОМ. Библиотека стандартных шаблонов STL так-
же оказывает заметную помощь в разработке приложений вообще и элемен-
тов ActiveX в частности. В нашей книге рассказано, как создавать такие
объекты, не затрачивая месяцы на изучение документации.
f Аплеты Java.
Язык Java, разработанный Sun Microsystems, должен обеспечивать работо-
способность приложений на различных платформах без перекомпиляции. И
хотя до абсолютной совместимости еще далеко, приложения Java вполне ра-
ботоспособны на большинстве наиболее популярных платформ.
В проектах для сети Интернет чаще всего применяются аплеты Java. Они
представляют собой одну из разновидностей приложений Java. Аплеты Java
встраиваются в документы HTML и работают под управлением браузера.
В отличие от органов управления ActiveX, возможности доступа аплетов к
локальным ресурсам компьютера пользователя практически сведены к нулю,
поэтому их применение не угрожает безопасности данных. Однако есть и об-
ратная сторона — такие ограничения во многом снижают пользу от аплетов.
Как правило, аплеты применяются для создания динамичного интерактивно-
го пользовательского интерфейса на стороне клиента. Помимо этого, они спо-
собны взаимодействовать с расширениями CGI и ISAPI сервера Web, с кото-
рого они загружены. Именно эти вариангы мы и рассмотрим в нашей книге.

Требования к читателям
Мы предполагаем, что Вы уже изучили язык HTML и имеете опыт создания хотя
бы простейшего сервера Web. Это может быть домашняя страничка на сервере
поставщика услуг Интернета, сервер в интрасети или даже сервер Web на Ва-
шем локальном компьютере. Для создания сервера Web Вам необходимо хоро-
шо знать Microsoft Internet Information Server версии 4.0 (мы рассказываем об
установке и настройке именно этого сервера) или более новой.
Неплохо также, если Вы освоите какие-нибудь средсгва визуального проек-
тирования документов HTML, такие, как Microsoft FrontPage. Список литера-
туры, которая понадобится Вам для этого, приведен в конце нашей книги.
Так как книга посвящена созданию приложений с базами данных, Вам нуж-
но уметь работать с Microsoft SQL Server и знать язык SQL. Мы не рассмотри-
XXIV Базы данных в Интернете. Практическое оуководство

ваем здесь вопросы установки и администрирования SQL Server, а также при-


емы программирования на Transact SQL (хотя в книгу вставлены примеры про-
грамм на языке Transact SQL). Для изучения этих вопросов Вам нужно обра-
титься к дополнительной литературе.
Проектирование расширений CGI и ISAPI для сервера Web, а также элемен-
тов управления ActiveX обычно требует знаний языка программирования C++
и интерфейса Win32. Для работы с нашей книгой Вы должны владеть основа-
ми стандартной библиотеки классов STL. Заметим, что многие достаточно слож-
ные приложения Web можно составлять и без них, обходясь готовыми элемен-
тами управления ActiveX, средствами DHTML, JavaScript, VB Script и ASP.
Мы не стали подробно описывать язык программирования Java, работе с
которым посвящена одна из глав книги. Мы рекомендуем Вам изучить этот язык,
пользуясь дополнительной литературой (см. «Библиографический список»).
Настоящая книга имеет практическую направленность. Предполагается, что
Вы, читая ее, попытаетесь реализовать описанные в ней примеры приложений.
Для этого Вам потребуется компьютер или локальная сеть с серверами Microsoft
Internet Information Server 4.0 и Microsoft SQL Server 7.0. Эти приложения не-
обходимо установить в среде Windows NT Server. Отдельные вопросы, не свя-
занные с применением ASP, Вы сможете решить и в среде Microsoft Windows 98,
однако это удается не всегда.
Разработку и отладку страниц ASP лучше всего выполнять в среде Microsoft
Visual InterDev 6.0. Для создания расширений CGI и ISAPI сервера Web, а так-
же элементов управления ActiveX Вам потребуется Microsoft Visual C++ 6.0.
Аплеты Java можно разрабатывать средствами Microsoft Visual J++ 6.0 или Sun
Java Workshop 1.0.
Ну и, конечно, Вашу работу значительно облегчит библиотека Microsoft
MSDN Library, поставляемая на компакт-дисках. Она содержит исчерпывающую
информацию (на английском языке) по всем вопросам использования техноло-
гий Microsoft для Интернета. Заметим, что библиотека Microsoft MSDN Library
доступна не только в виде набора компакт-дисков, но и через Интернет
(http://msdn.microsoft.com).

Структура книги
В первой главе описаны отличия приложений Web от обычных программ. Рас-
смотрен принцип работы сервера Web, объяснена разница между активными и
пассивными серверами Web. Кроме того, из этой главы Вы узнаете о серверной
активности, достигаемой средствами технологии ASP, расширений CGI и ISAPI,
а также об активности на стороне клиента, связанной с применением сценари-
ев JavaScript, VB Script, элементов управления ActiveX и аплетов Java. Здесь
перечислены преимущества и недостатки использования серверной активности
и активности на стороне клиента в различных ситуациях. Приведен краткий
обзор методов интеграции сервера Web и сервера СУБД, связанных с исполь-
зованием расширений CGI, ISAPI, драйверов ODBC, объектов OLE DB, и ADO.
Анализируются недостатки и преимущества этих методов при создании прило-
жений Web.
Практическая часть первой главы содержит рекомендации по установке про-
граммных средств, необходимых для дальнейшей работы с книгой, — MS IIS 4.0,
Введение XXV

MS SQL Server, MS Visual InterDev, MS Visual C++, MS FrontPage, пакетов об-


новлений операционной системы и инструментальных средств и т. д,
Во второй главе рассмотрены серверные и клиентские сценарии — их назна-
чение и особенности, а также приемы конструирования интерфейса пользова-
теля — важный этап создания приложений для Интернета, включающий приме-
нение сценариев. В нашей книге нет описания языка JavaScript (литературы на
эту тему издано много), хотя в приложении имеется краткая справочная инфор-
мация. Вместо этого мы уделили внимание объектной модели браузеров MS IE
и Netscape, понимание которой необходимо для написания клиентских сценари-
ев. Во второй главе рассмотрены практические приемы применения клиентских
сценариев (а также средств DHTML) для создания пользовательских интерфей-
сов к базам данных, таких, как формы и отчеты. Затронуты проблемы совмес-
тимости сценариев и средств DHTML с различными версиями браузеров (и
указаны методы решения этих проблем). Описана процедура отладки клиент-
ских сценариев. Многие примеры сценариев из второй главы Вы сможете ис-
пользовать для реализации новых проектов.
Третья глава посвящена технологии ASP. В ней рассказано, как построить
активный сервер Web с применением расширений CGI, ISAPI и серверных сце-
нариев. Описаны возможности и преимущества серверных сценариев JavaScript
и VB Script в страницах ASP по сравнению с расширениями CGI, ISAPI и с
клиентскими сценариями. Вводится понятие приложения ASP и рассказано о
применении процедур, перечислений, переменных приложения и переменных
сеанса, объектов ASP. Вы узнаете о том, как сделать выбор между JavaScript и
VB Script при создании страниц ASP. На конкретных примерах мы проиллюст-
рируем методики создания форм и отчетов с применением ASP и клиентских
сценариев.
В четвертой главе рассказано об использовании объектов ADO для связи
приложений Web с базами данных, Рассмотрена программная и объектная мо-
дель ADO, методика обработки событий. Кратко описан программный интер-
фейс ADO. Приведены исходные тексты приложений Web, созданных с приме-
нением ADO, ASP и MS SQL Server. На их примере показана технология обра-
ботки ошибок. Мы также расскажем здесь, как интегрировать обычные прило-
жения Windows и расширения сервера Web с базами данных при помощи ADO.
Глава содержит многочисленные примеры страниц ASP виртуального книжно-
го Интернет-магазина, подготовленные на основе реальных проектов.
Пятая и шестая главы посвящены вопросам применения интерфейсов OLE
DB и ODBC для связи приложений с базами данных. Мы рассмотрим отличия
этих интерфейсов от интерфейса ADO, кратко опишем программную и объект-
ную модель OLE DB, а также программный интерфейс OLE DB и ODBC. Кро-
ме того, здесь приведены примеры приложений, работающих с базами данных
посредством указанных интерфейсов.
В седьмой главе описана структура расширений CGI и ISAPI сервера Web.
Здесь рассказано об отличиях CGI и ISAPI, а также о критериях выбора нуж-
ной технологии. На конкретных примерах показано, как создавать расширения
CGI и ISAPI для сервера Microsoft IIS, работающих с базами данных.
Восьмая глава посвящена элементам управления ActiveX. Здесь рассказано
о возможности применении элементов управления ActiveX на стороне клиента,
XXVI Базы данных в Интернете. Практическое руководство

а также на стороне сервера для расширения объектной модели ASP. Описана


методика быстрого создания элементов управления ActiveX с применением биб-
лиотеки ATL. Приведены примеры использования элементов управления Acti-
veX для связи сервера Web с сервером банка, выполняющим платежи в режиме
on-line, а также для автоматической передачи почтовых сообщений с примене-
нием протокола SMTP.
Девятая глава посвящена использованию аплетов Java на стороне клиента для
связи с расширениями сервера CGI и ISAPI. В ней приведены примеры апле-
тов Java, передающих информацию о кредитных карточках расширению серве-
ра Web и серверному сценарию, расположенному на странице ASP.
Книга укомплектована компакт-диском, содержащим полные листинги всех
приложений, описанных в книге.

Благодарности
Мы благодарим генерального директора издательства компьютерной литерату-
ры «Русская Редакция» (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 представляют собой неко-
торое связующее звено между объектами различных типов. Их проектируют с
применением специального языка разметки гипертекстов Hyper Text Markup
Language, или сокращенно — HTML.
Для доступа к информации, расположенной на серверах Web, пользователи
применяют специальные клиентские программы — браузеры. В настоящее вре-
мя существуют десятки различных браузеров, но у пользователей Интернета
наибольшей популярностью пользуются два — Microsoft Internet Explorer и
Netscape Navigator.
Каждая страница сервера Web имеет свой так называемый универсальный
адрес ресурса Universal Resource Locator (URL). Для того чтобы получить доступ
к той или иной странице, пользователь должен указать ее адрес URL програм-
ме браузера. Как правило, любой сервер Web имеет одну главную страницу, со-
держащую ссылки на все другие страницы этого сервера. Поэтому просмотр со-
держимого сервера Web обычно начинается с его главной страницы.
Когда пользователь указывает браузеру адрес страницы сервера \Veb, она
начинает загружаться с сервера. При этом браузер устанавливает соединение с
сервером Web, применяя протокол передачи данных Hyper Text Transfer Praroro/
Базы данных в Интернете. Практическое ауководство

(HTTP), получает нужную страницу и разрывает соединение. Принятая страни-


ца отображается в окне браузера. Этот процесс показан на рис. 1-1.
/ 1
Браузер пользователя Сервер Web выбирает
отправляет на сервер документ HTML
Web адрес U RL
с заданным адресом URL
документа HTML
\ /

Компьютер
пользователя Сервер Web
с браузером

Браузер показывает Сервер Web отправляет


в своем окне принятый браузеру выбранный
документ HTML документ HTML

Рис. 1 -1. Взаимодействие браузера и сервера Web

Пассивные и активные серверы Web


Различают пассивные и активные серверы Web. Если страницы сервера содер-
жат только статическую текстовую и мультимедийную информацию, а также
гипертекстовые ссылки на другие страницы, то сервер называется пассивным.
Когда же страницы сервера ведут себя аналогично окнам обычных интерактив-
ных приложений, вступая в диалог с пользователем, мы имеем дело с активным
сервером.
Очевидно, статический сервер Web не может служить основой для создания
интерактивных приложений в сети Интернет с базами данных, так как он не
.предусматривает никаких средств ввода и обработки запросов. Поэтому в нашей
книге мы будем рассматривать только активные серверы Web.
Как же создаются и как работают активные серверы Web?
Мы рассмотрим два варианта. Первый них предполагает применение специ-
альных программных расширений сервера Web, таких, как CGI и ISAPI. Второй
связан с использованием серверных сценариев и технологии активных страниц
Active Server Pages (ASP).
Программы CGI
Для того чтобы сервер Web мог вести диалог с пользователем, был разработан
механизм программных расширений сервера, основанный на применении так
называемого стандартного шлюзового интерфейса Common Gateway Interface
(CGI). Программы CGI пользуются этим интерфейсом для получения (через
протокол HTTP) информации от пользователя, для ее обработки и отправки
Глава 1 Сервер Web как ядро приложений для Интернета

обратно в виде нового документа HTML, ссылки на существующий документ


или на другой объект.
При этом для ввода информации со стороны пользователя в документ HTML
встраиваются формы, содержащие такие органы управления, как текстовые поля,
списки, переключатели, кнопки и т. д. Обычно одна из кнопок предназначена для
завершения ввода. Когда пользователь заполнит всю форму, он нажимает эту
кнопку, и данные из полей формы передаются программе CGI. Обработав дан-
ные, программа CGI динамически формирует новый документ HTML с резуль-
татами обработки и отправляет его обратно пользователю. При необходимости
программа CGI может обращаться к СУБД.
Этот процесс проиллюстрирован на рис. 1-2. О программах ISAPI Вы узнае-
те из следующего раздела.
/ ^\
1
Браузер пользователя г Программа CGI или ISAPI
отправляет на сервер выбирает существующий
Web адрес URL документ HTML
программы CG! или ISAPI или создает новый
\ J

Компьютер
пользователя Серзер Web
с браузером

Сервер Web отправляет


Браузер показывает
браузеру выбранный или
в своем окне принятый
созданный динамически
документ HTML
документ HTML

Рис. 1-2. Формирование динамических документов HTML с помощью CGI и ISAPI


Таким образом, программы CGI позволяют серверу Web вести активный ди-
алог с пользователем.
Что представляет собой программа CGI?
Это обычное консольное приложение, работающее в среде операционной
системы сервера Web и осуществляющее обмен данными через стандартные
потоки ввода и вывода. Такое приложение загружается в оперативную память
сервера и запускается только по явному запросу пользователя, когда к нему
выполняется обращение из документа HTML. Окончив обработку запроса поль-
зователя, программа CGI завершает свою работу и выгружается из памяти сер-
вера.
Программа CGI работает на сервере как отдельный процесс. В рамках этого
процесса она способна, в частности, обращаться к базам данных для выборки или
обновления информации.
Бы можете писать программы CGI на любом языке программирования, до-
пускающем создание исполняемых модулей. Часто их составляются с примепе-
Базы данных в Интернете. Практическое руководство

нием интерпретируемого языка Perl. Интерпретаторы Perl доступны на различ-


ных платформах. Однако программы CGI, приведенные в нашей книг*;, написа-
ны на языке программирования C++, так как книга ориентирована на примене-
ние платформы Microsoft Windows.
Приложения ISAPI
Если сервер Web создан на базе Microsoft Internet Information Server, вместо
программ CGI Вы можете использовать приложения ISAPI, реализованные в
виде библиотек динамической загрузки DLL.
Приложения ISAPI условно делятся на расширения ISAPI и фильтры ISAPI.
Расширения ISAPI
Расширения ISAPI выполняют тс же функции, что и только что рассмотренные
программы CGI. Однако есть и очень важные отличия.
Самос главное из них заключается в том, что расширение ISAPI загружает-
ся в адресное пространство сервера Web, но не работает как отдельный процесс.
Причем такая загрузка выполняется один раз, когда к расширению обращается
первый пользователь. В дальнейшем расширение остается в оперативной памя-
ти и может быть задействовано без повторной загрузки.
Заметим, что сервер Microsoft Internet Information Server версии 4.0 позво-
ляет загружать программы ISAPI в отдельное адресное пространство. Эта воз-
можность, замедляющая работу сервера, обычно используется для отладки но-
вых программ. Аварийное завершение программы ISAPI, загруженной в отдель-
ное адресное пространство, как правило, не приводит к полной остановке сер-
вера Web.
В результате расширения 1SAPI работают быстрее но сравнению с програм-
мами CGI, особенно когда сервер Web посещают много пользователей. Что же
касается программ CGI, то для каждого пользователя приходится запускать от-
дельный процесс, а это отнимает немало времени.
С другой стороны, приложения ISAPI приходится отлаживать намного тща-
тельнее, чем программы CGI. Так как приложение ISAPI работает в адресном
пространстве сервера Web, ошибка в приложении ISAPI способна вызвать ава-
рийное завершение работы сервера Web. Ошибки в программе CGI обычно ме-
нее значимы, так как в худшем случае авария произойдет только в том процес-
се, в котором работает эта программа, а не в процессе сервера Web.
Фильтры ISAPI
Фильтры ISAPI, так же как и расширения ISAPI, реализованы в виде библио-
тек динамической загрузки DLL, однако они предназначены для другого. Филь-
тры ISAPI способны контролировать весь поток данных между браузером и сер-
вером Web на уровне протокола HTTP. Благодаря этому их можно применять
для решения таких задач, как динамическая перекодировка и шифрование дан-
ных, создание дополнительных процедур аутентификации пользователей, сбор
статистической информации об использовании ресурсов сервера и т. д.
Активные страницы ASP
Технология ASP предполагает интенсивное использование серверных сценари-
ев и объектов СОМ для создания активных серверов Web. При ее применении
Глава 1. Сервер Web как ядро приложений для Интернета

на сервере Microsoft Internet Information Server располагаются текстовые фай-


лы с расширением asp, содержащие операторы языка HTML, и сценарии, состав-
лепные на языках JScript или VB Script.
Средствами технологии ASP можно легко создавать интерактивные страни-
цы Web, не используя расширения CGI или ISAPI, что позволяет в ряде случа-
ев полиостью избежать или максимально сократить программирование на C+ +
или Perl. Активные страницы ASP выполняют обработку данных, кведенных
пользователями при помощи форм, обращаясь при необходимости к базам дан-
ных или другим активным объектам.
Как работают серверные сценарии в страницах ASP?
Когда пользователь обращается к странице ASP, сервер Web интерпретиру-
ет расположенный в ней сценарий. При этом анализируются параметры, пере-
данные этой странице. Далее страница модифицируется (или создается заново),
а затем отправляется обратно пользователю.
Заметим, что пользователь не может каким-либо образом получить содержи-
мое страницы ASP, так как сервер Web отправляет ему не саму страницу, а ре-
зультат ее интерпретации. Таким образом, логика работы страницы скрыта от
пользователей.
Серверный сценарий, встроенный в страницу ASP, способен обращаться к
базам данных через вызов методов интерфейса ActiveX Data Objects (ADO) -
простую и понятную процедуру. Если у Вас возникнет необходимость реализо-
вать собственную бизнес-логику, Вы можете создать новые объекты СОМ или
использовать объекты СОМ сторонних разработчиков.
Наша книга главным образом посвящена активным страницам ASP, однако
мы уделим некоторое внимание и созданию активных серверов при помощи
расширений CGI и ISAPI.

Приложения Web
В нашей книге мы постоянно будем-Оперировать такими понятиями, как «при-
ложения Web», «приложения для Интернета и интрасети». Теперь, когда Вы
узнали об активных серверах Web, мы раскроем эти понятия. Однако вначале
напомним, как работают обычные программы.
Первые программы для мэйнфреймов работали в пакетном режиме. Им были
доступны все ресурсы компьютера. Немного позже появились интерактивные
системы, поддерживающие одновременное обращение к ресурсу компьютера
многих пользователей при помощи неинтеллектуальных терминалов. Создате-
ли таких систем учли, что программам придется работать в многозадачном и
многопользовательском режиме. Если система содержала базу данных, то все
интерактивные пользователи обращались к ней через терминалы с помощью
специального программного обеспечения, работающего на мэйнфрейме.
В рамках приложений Web на стороне клиента работает браузер, который по
своим интеллектуальным возможностям далеко опережает алфавитно-цифровые
или графические терминалы. Как видно из следующих разделов, браузер спосо-
бен предварительно обрабатывать данные, отправляемые на сервер, а также об-
рабатывать и представлять результаты, полученные от сервера, в удобном для
пользователя виде.
В роли основного сервера для приложений Web выступает, как можно легко
догадаться, сервер Web — разумеется, активный.
6 Базы данных в Интернете. Практическое эуководство

Однако сервер Web выполняет только часть работы. Он отвечает за получе-


ние данных от пользователя и подготовку страниц, отправляемых обратно. Что
же касается запросов к базе данных или обращений к другим активным объек-
там, реализующим бизнес-логику, то для решения этих задач сервер Web обра-
щается к серверам базы данных или другим серверам приложений. Обращения
выполняются либо средствами ASP, либо при помощи расширений CGT или
ISAPI.
Мы будем называть приложениями Web набор страниц ASP, HTML, DHTML,
объектов СОМ, клиентских и серверных элементов управления ActiveX, клиен-
тских и серверных сценариев, а также аплетов Java, расположенных на одном
или нескольких серверах Web и предназначенных для работы в рамках одного
приложения. При этом активные компоненты приложений Web могут обращать-
ся к серверам баз данных или другим прикладным серверам для выполнения тех
ли иных запросов.
Подмножество приложений Web, созданных с применением технологии ак-
тивных серверных страниц ASP, мы будем называть приложениями ASP. Более
точное определение мы приведем в третьей главе нашей книги,
Несмотря на кажущееся сходство приложений Web и программ, ориентиро-
в а н н ы х па мэйнфреймы и терминалы, они относятся к разным типам, каждый
из которых имеет свои особенности. И если во втором случае системой управ-
ляет администратор, а линии связи между терминалами пользователей и мэйн-
фреймом обладают высокой надежностью и достаточной пропускной способно-
стью, то в случае приложений Web дело обстоит иначе.
Разработчик приложения Web должен быть готов к тому, что линия связи
может в любой момент оборваться, а пользователь после ее восстановления по-
желает возобновить работу с того места, на котором случился обрыв. Пропуск-
ная способность и надежность каналов Интернета пока оставляет желать луч-
шего, поэтому необходимо минимизировать обмен данными между клиентом и
сервером. На стороне клиента может оказаться любая операционная система и
любой браузер, поэтому забота о совместимости — насущная необходимость. В
Интернете много желающих получить несанкционированный доступ к популяр-
ным ресурсам и воспользоваться номерами чужих кредитных карточек, а значит,
вопросы обеспечения безопасности становятся первоочередными.

Активность на стороне клиента


В большинстве случаев целесообразно разделять работу между клиент-ом и сер-
вером, чтобы добиться оптимальной производительности в условиях пизкоско-
ростных каналов Интернета и лимитированных ресурсов серверов Web. Так,
предварительную обработку введенных данных, отправляемых серверу, имеет
смысл выполнять на стороне клиента. Это позволит исключить, например, по-
вторные передачи неправильно заполненных форм. Графическое представление
результатов запроса также стоит выполнять на стороне клиента, что существенно
сократит объем данных, передаваемых по сети. А нот выборку из базы данных
должен выполнять сервер.
Для реализации клиентской активности Вы можете применять сценарии
JavaScript, VB Script, аплеты Java и элементы управления ActiveX.
Глава 1. Сервер Web как ядро приложений для Интернета

Клиентские сценарии JavaScript


Язык сценариев JavaScript разработан фирмой Netscape Communication Cor-
poration и первоначально назывался LiveScript. Заметим, что JavaScript не имеет
никакого отношения к языку Java, созданному Sun Microsystems на базе языка
Oak.
Конструкции языка JavaScript встраиваются в страницы HTML и исполня-
ются (интерпретируются) под управлением браузера при загрузке страниц, а
также при совершении пользователем определенных действий над объектами,
расположенными в этих страницах,
В частности, сценарии JavaScript способны обрабатывать данные, введенные
пользователями в полях форм, а также события, возникающие в процессе мани-
пуляций пользователя с мытью, копировать в окно браузера другие страницы
HTML или изменять содержимое уже загруженных страниц.
Сценарии JavaScript широко применяются для создания различных визуаль-
н ы х эффектов, таких, например, как изменение внешнего вида элементов управ-
ления, над которыми установлен курсор м ы ш и , а н и м а ц и я графических изобра-
жений, создание звуковых эффектов и т. д.
Механизм локальной памяти Cookie позволяет сценариям JavaScript сохра-
нять на компьютере локальную информацию, введенную пользователем. Напри-
мер, в Cookie может храниться список товаров из Интернет-магазина, отобран-
ных для покупки.
Так как язык JavaScript разработан для применения непрофессиональными
программистами, создание клиентских сценариев обычно не составляет особо-
го труда. Однако для обеспечения совместимости с различными браузерами все
же приходится учитывать многочисленные детали. Например, браузер Microsoft
Internet Explorer реализует собственную версию JavaScript, называемую JScript.
Кроме того, для выполнения многих функций клиентские сценарии обращают-
ся к интерфейсу объектной модели браузера. Эта модель отличается для разных
браузеров.

Клиентские сценарии VB Script


Помимо JScript, браузер Microsoft Internet Explorer (и только он) способен ра-
ботать с языком клиентских сценариев VB Script. Последний представляет со-
бой подмножество Microsoft V i s u a l Basic и функционально равноценен языку
JavaScript.
Так как многие пользователи сети Интернет не работают с браузером Micro-
soft Internet Explorer, применение VB Script для создания страниц серверов Web,
расположенных в Интернете, едва ли можно считать оправданным.
Ситуация меняется, если технологии Интернета применяются в корпоратив-
ной интрасети. Когда администратор может установить на компьютеры всех
пользователей Microsoft Internet Explorer, а в штате компании есть программи-
сты, имеющие большой опыт работы с Microsoft Visual Basic, то применение
VB Script вместо JavaScript заметно сокращает сроки и стоимость разработки.

Аплеты Java
Подмножество приложений Java, называемых аплетами Java, используют наря-
ду с клиентскими сценариями для организации активности на стороне клиен-
Базы данных в Интернете. Практическое руководство

та. Вы можете применять аплеты Java для получения визуальных и звуковых


эффектов, для организации ввода и предварительной обработки данных перед
отправкой их на сервер, а также для представления полученных от сервера дан-
ных в графическом, табличном или каком-либо другом виде.
Аплеты Java встраивают в страницы сервера Web, дабы они взаимодейство-
вали с клиентскими сценариями, составленными на JavaScript или VB Script. В
частности, сценарии способны вызывать методы аплета, передавать данные или
принимать их от аплетов.
Создание аплетов во многом упрощается благодаря наличию обширных биб-
лиотек классов Java. В этих библиотеках есть мощные и удобные средства для
организации пользовательского интерфейса, работы с графическими изображе-
ниями, передачи данных по сети и др. Это позволяет больше внимания уделить
логике работы аплета, а не реализации типовых задач программирования вроде
организации динамических массивов или загрузки графических изображений.
Аплеты выполняются под управлением браузера и не имеют никакого досту-
па к локальным ресурсам компьютера. Однако они способны взаимодействовать
с расширениями сервера CGI и ISAPI, а также читать файлы с того сервера Web,
с которого они были загружены. Это несколько увеличивает выигрыш от при-
менения аплетов Java при создании приложений для Интернета.
На рис. 1-3 показано, как аплеты Java используются на сервере Web издатель-
ства «Русская Редакция».

Элвнроижаи mmnmpei. Снмяютескмс

Hi; еловая (ишиткгоь. s Бтнайщм брикя баегопккй объем тсргавыг транзакций

кенбгодиныи злгтентом усшшшго вивихя бизнеса как опя неЗопышп §хрн, так а
гая крупных корпораций. Эта канта прещгаэданеш рукоюдитепяк ПШЕОШИЙ я
изрдаривш, технический днрнторал, CIKUKUIHCIBK гю агтоиапоадан вгонвса к ЕССМ

Вк даятиоагйсь с освввнык'. асткгаии бепекия апвктронаой комтершш,


описи», еигоию 1ш ее истопьзовашв аоя Вшвй ф(фмы. чяимте, как
пэтребктепьские (крешотазе гаргочки, цифрой* деньги .) я бюнес-прмюйеюя
{разяенвшв якатав. икташикие счждаь и футе типы крупньи трашюннй).
узлвте различные Еврианты Фзниеиишя ивктронной игоиерпкн и ее

Рис. 1 -3. Использование аплетов Java


Глава 1. Сервер Web как ядро приложений для Интернета 9

В левой нижней части страницы находится аплет, в окне которого отобража-


ются пиктограммы книг, подготовленных издательством. Изображение периоди-
чески обновляется, Если щелкнуть пиктограмму, то в правой части страницы
появится подробное описание книги. Таким образом, пользователь не будет тра-
тить усилия на просмотр списка книг. Ему достаточно щелкнуть пиктограмму
понравившейся книги, чтобы получить о ней полную информацию.
Заметим, что, несмотря на идею независимости приложений Java от конкрет-
ной компьютерной платформы, на практике возможны случаи несовместимос-
ти. Это происходит из-за того, что на различных платформах интерпретатор Java
реализуется no-разному. Поэтому пока, к сожалению, о полной переносимости
приложений Java вообще и аплетов Java в частности говорить не приходится.
Элементы управления ActiveX
Элементы управления ActiveX можно использовать как на стороне сервера, так
и на стороне клиента. В последнем случае их применяют для решения тех же
задач, что и аплеты Java. Дополнительно они предоставляют неограниченный
доступ к ресурсам локального компьютера.
Надо сказать, что возможность доступа элементов управления ActiveX к ло-
кальным ресурсам компьютера имеет не только преимущества, но и недостатки.
Конечно, удобно, когда Ваш сервер Web сможет обрабатывать любую инфор^
мацию, «добытую» из компьютера пользователя. Однако мы полагаем, что най-
дется очень мало людей, готовых предоставить неограниченный доступ к своим
дискам.
Для того чтобы убедить пользователей в том, что предлагаемый для загруз-
ки элемент управления ActiveX безопасен, предлагается технология цифровых
сертификатов. Следует, однако, учесть, что сама по себе эта технология не ог-
раждает от проблем, связанных с наличием дыр в защите браузера. Многие про-
сто предпочтут не рисковать и не станут загружать потенциально опасное про-
граммное обеспечение. Кроме того, чтобы получить цифровой сертификат и
подписать свой элемент управления ActiveX, разработчик должен внести еди-
новременный денежный взнос, а затем производить ежегодную плату.
Заметим, что если Вы разрабатываете элементы управления ActiveX для сер-
вера Web, то их можно не подписывать. Эти модули не загружаются клиентами,
а выполняются непосредственно па сервере.

Проблемы с реализацией клиентской активности


Выбирая между серверной и клиентской активностью, Вам надо принять во
внимание многие факторы.
Прежде всего, при создании информационной системы в Интернете необхо-
димо учесть ее разнородность. Если Вы, например, создаете в Интернете свой
электронный магазин, то Вам, скорее всего, хочется, чтобы в него заглянуло как
можно больше посетителей. Значит, стоит проявить особую заботу о совмести-
мости своих решений с максимальным количеством компьютерных платформ.
По возможности не обременяйте посетителей загрузкой разного рода клиент-
ских программ, особенно больших. Если посетитель столкнется с необходимос-
тью загрузить, например, приложение размером 2 Мб, весьма вероятно, что он
решит поискать тот же товар в другом магазине.
10 Базы данных в Интернете. Практическое зуководство

Следует также продумать выбор средств безопасности для защиты номеров


кредитных карточек и другой подобной информации, передаваемой клиентом на
Ваш сервер или на сервер банка, выполняющего операции со счетами пользова-
телей.
Ну и, конечно, не забудьте о дизайне страницы и удобстве работы пользова-
теля. Позаботьтесь, чтобы покупатели быстро находили нужный им товар (или
некую информацию), а страницы не выглядели отталкивающе и аляповато.
С учетом сказанного выше Вам следует с осторожностью выбирать средства
клиентской активности, Например, сценарии JavaScript составляйте таким об-
разом, чтобы они исполнялись без ошибок всеми популярными браузерами.
Применять эти сценарии следует л и ш ь в самых необходимых случаях. Кроме
того, обязательно учитывайте, что особенно осторожные пользователи могут
запретить работу сценариев JavaScript, соответствующим образом настроив свой
браузер,
Что же касается клиентских сценариев VB Script, го мы не рекомендуем их
применять. Создав с их помощью в сети Интернет электронный магазин, Вам
заранее придется отказаться от посетителей, использующих, например, браузер
Netscape Navigator. А таких весьма много.
Аилеты Java тоже способны вызвать проблемы, связанные с совместимостью,
а также с тем, что пользователи отключают их загрузку при настройке браузе-
ра. И уж, конечно, еще больше сложностей будет с самодельными органами уп-
равления ActiveX. Вы, естественно, можете средствами соответствующего моду-
ля заставить их работать под управлением браузера Netscape Navigator, но Вам
придется еще убедить пользователей в безопасности их загрузки.
Поэтому мы рекомендуем создавать приложения для Интернета с использо-
ванием не клиентской, а серверной активности. Так Вам будет легче обеспечить
совместимость с различными браузерами и компьютерными платформами. Что
же касается корпоративных интрасетей, то здесь у Вас больше возможностей
реализовать клиентскую активность, так как вопросы совместимости решаются
централизованно.

Интеграция серверов Web и SQL Server


В нашей книге мы рассмотрим использование приложений Web с сервером
Microsoft SQL Server, однако многое из сказанного можно применить и к дру-
гим серверам баз данных, вместе с которыми поставляются драйверы ODBC для
операционной системы Microsoft Windows.
Что же касается Microsoft SQL Server, то для него разработан целый набор
интерфейсов и средств доступа. Мы расскажем, как использовать некоторые из
них для интеграции SQL Server и сервера Web, выполненного на базе Microsoft
Internet Information Server.
. Если Ваш сервер Web содержит информацию о каком-либо товаре, то его
интеграция с базой, данных позволит посетителям увидеть всю информацию о
товаре. При этом страницы сервера Web будут автоматически отражать текущее
состояние дел, учитывая, например, наличие товара на складе и возможность его
заказа по предварительной заявке.
Глава 1. Сервер Web как ядро приложений для Интернета 11

На сервере Web издательства «Русская Редакция» Вы сможете просмот-


реть список книг, хранящийся в базе данных, и детальное описание этих книг
(рис. 1-4).
Шр /Мм», luifld* lu/nihnii/™» siwh hnnk ааЛ a.pi'SFBILK^SI XI'-(1bNUMID=1 - Mreio.oK Internet t.plocci pumdril \.y

book»ru

тру [Уиебныйкурс *J ngji* j

УЧЕБНЫЙ КУРС -d^^^^^gjgjgg^gggggBS


Каздзя книга серии (Учебный куэс» ( *Trairang») Microsoft Press - это и справочник, и
официальный учебный <урс для подготовки к сдаче эизаиена по программе M.cro=oft
Certifed Prafessnnal (МСР) Серия составлена с учетом требований корпорации
Microsoft к уровни грофессно-1ал=ных знании специалистов го конкретной теме

Найдено книг: 17 {в щ-

Разработка распределенных приложений на


Microsoft Visual Basic 6.0. Учебный курс
Itictosaft Ccvp, 400 стр., с ил

ызаимапвиаеаш

Рис. 1-4. Список книг, полученный из базы данных

Программный интерфейс DB Library


Программный интерфейс DB Library представляет собой естественный интер-
фейс SQL Server, реализованный как набор функций. В документации Microsoft
не рекомендует применять этот интерфейс, так как вместо него гораздо удобнее
пользоваться другими, о которых мы расскажем далее. Фактически в новой вер-
сии SQL Server этот интерфейс оставлен только для совместимости с разрабо-
танными ранее приложениями.

Программный интерфейс ODBC


Программный интерфейс ODBC создан специально для доступа к реляционным
базам данных. Это универсальный интерфейс, доступный не только в SQL
Server, но и при работе с другими (в том числе нереляционными) базами дан-
ных.
Предполагается, что разработчик базы данных предоставляет в распоряжение
прикладных программистов драйвер ODBC, и все обращения к базе данных
выполняются с использованием интерфейса ODBC, предоставляемого этим
драйвером.
12 Базы данных в Интернете. Практическое руководство

Преимущества организации доступа к базам данных через унифицированный


интерфейс ODBC очевидны — программы, обращающиеся к базам данных, ста-
новятся менее зависимыми от этих баз данных. Однако это только в теории. На
практике отличия в реализации /драйверов ODBC не всегда позволяют добить-
ся полной независимости.
Заметим, что программный интерфейс ODBC выполнен как набор функций.
Поэтому он непосредственно доступен только из программ, составленных на
традиционных языках программирования, таких, как C++. Следовательно, этот
интерфейс можно использовать при взаимодействии'сервера Web с базами дан-
ных только через расширения CGI и ISAPI. Технология ASP не позволяет об-
ращаться непосредственно к интерфейсу ODBC, так как серверные сценарии
способны вызывать интерфейсы объектов СОМ только посредством механизма
автоматизации (известным ранее как OLE Automation).

Объектный интерфейс Remote Data Object


Современные технологии Microsoft в области Интернета основаны не на про-
граммных интерфейсах — Win32 или ODBC, а на объектных интерфейсах, до-
ступных в модели компонентных объектов СОМ.
Упрощенно такую модель можно представить в виде набора интерфейсов,
реализующих методы и свойства. Проведя аналогию между моделью компонен-
тных объектов СОМ и обычными классами языка программирования C++, мож-
но сказать, что методы соответствуют функциям — членам класса, а свойства —
переменным, определенным в классе. На самом деле свойства в модели компо-
нентных объектов СОМ тоже реализуются через функции, однако суть дела от
этого не меняется. Свойства используются преимущественно для хранения дан-
ных, а методы — для выполнения операций над данными или другими объекта-
ми. Подробнее обо всем этом мы расскажем в главе, посвященной разработке и
использованию элементов управления ActiveX.
Теперь вернемся к интерфейсам SQL Server.
Специально для упрощения доступа к SQL Server из приложений Microsoft
Visual Basic и Visual Basic for Applications разработан объектный интерфейс
Remote Data Object (RDO). Он реализует все основные возможности интерфей-
са ODBC, но не применяется при разработке приложений Web. Мы его упомя-
нули для полноты изложения.

Объектный интерфейс OLE DB


Другой объектный интерфейс, разработанный для предоставления доступа к
базам данных, называется OLE DB. Фактически OLE DB представляет собой
открытый стандарт, предназначенный для организации универсального досту-
па к базам данных. Причем имеются в виду не только реляционные БД, но и
нереляционные, такие, как серверы почты, базы данных на мэйнфреймах с ме-
тодами доступа IMS, VSAM и т. д.
Компоненты OLE DB состоит из трех элементов: провайдера (provider), по-
требителя (consumer) и служебного элемента, выполняющего обработку и пе-
редачу данных.
В роли потребителя могут выступать приложения, составленные на языке
программирования C++, или объекты ADO, о которых мы расскажем позже.
Глава 1. Сервер Web как ядро приложений для Интернета 13

Такие приложения получают доступ к базам данных посредством объектного


интерфейса OLE DB.
Задача провайдера OLE DB — реализовать интерфейс OLE DB. Б составе
OLE DB поставляются провайдеры для интерфейсов ODBC, для текстовых
файлов и некоторые другие. Пользуясь провайдером ODBC, потребители интер-
фейса OLE DB могут получить доступ к базам данных через драйвер ODBC.
Объектный интерфейс OLE DB не реализует механизм автоматизации. По-
этому Вы сумеете задействовать OLE DB только в расширениях CGI и ISAPI
сервера Web, но не в страницах ASP (строго говоря, к объектам OLE DB можно
обращаться через интерфейс ADO, о котором мы расскажем ниже). Тем не ме-
нее интерфейс OLE DB иногда удобнее интерфейса ODBC, особенно в тех слу-
чаях, когда сервер Web должен обращаться к нереляционным базам данных. Он
рекомендуется как средство для создания системных утилит, работающих с ба-
зами данных, а также для создания инструментов для разработки приложений.

Объектный интерфейс ActiveX Data Objects


Объектный интерфейс ActiveX Data Objects (ADO) построен на основе интер-
фейса OLE DB. Модель ADO представляет собой набор объектов и значитель-
но упрощает разработку приложений с базами данных, так как позволяет исполь-
зовать высокоуровневые средства разработки и серверные сценарии.
Это возможно, в частности, потому, что объекты ADO реализуют средства
автоматизации. В результате интерфейс ADO доступен из приложений, состав-
ленных с применением целого спектра инструментальных средств, таких, как
ASP, C++, Visual Basic, Visual Basic for Applications, Java и т. д.
Для нас сейчас важнее всего то, что интерфейс ADO доступен из серверных
сценариев, размещенных в страницах ASP на сервере Microsoft Internet Infor-
mation Server. Это позволяет организовать очень простой и наглядный интер-
фейс приложения Web с базой данных, исключающий необходимость програм-
мирования на таких сложных языках, как C++.
Разумеется, объекты ADO доступны и из программных расширений сервера
Web типа программ CGI и приложений ISAPI, однако нас будет интересовать в
первую очередь именно связь страниц ASP с базами данных через объекты ADO.

Проект Интернет-магазина
Параллельно увеличению влияния Интернета на все сферы человеческой дея-
тельности появилась и быстро развивается электронная коммерция. В Интер-
нете открываются все новые и новые электронные магазины, благодаря которым
Вы можете покупать товары в разных странах мира, не вставая из-за своего ра-
бочего стола.
Наша книга, как мы надеемся, поможет Вам в вашей практической деятель-
ности, поэтому мы рассмотрим вопросы создания отдельных элементов такого
магазина. Предположим, он торгует книгами (а на что еще, спросите Вы, хватит
фантазии авторов книг?), но Вы, конечно, вправе разместить в нем любой дру-
гой товар.
В качестве примера мы приведем список книг, расположенный в новом Ин-
тернет-магазине IT Book (рис. 1-5).
Базы данных в Интернеге. Практическое руководство

a afJ J 1 1М я !
* " -"-: "'i"-''•'"''"•" "•"• "" "' ^'Г'"-'т"'''"'^ *fl»fioluv.pp.(irtb2-S:CAIF3aEFlffiB Miami* Internet Liplo. ИВЕ)

ЕЙ Ш 5-»'.- F-,jve!tei IIK* He" tM-t aiSR**™ ®[KaMS

| Покупка книг по прайс-листу

УЧЕБНЫЙ КУРС

Компьютерны* cent.. У ч е н н а я i-ifp'-


№crosoft Corp . CD^flOilH, 552 cmp, с an, 2000 г
Pt?ij><tti#Tii[i |1Л.:прбдрл»чныч щткчя с«мй нд

ttcrcsoff Сй!р . CD-fiOfl-t, 5Г6 сгцо.. с un, 2000


At Сертификация, Уч«»иый еурс
Micmscfl Cotp, 512 смр с ил, 2000 г
Рлзуиотия и |! нп ижк и ив иа MltitisoSi ^i
Ё.8. Ученный h^])-.
jf( Gyp., CD-ROM №0 cmp. с an 200C

Ученный typt
Microsoft Civp.. CD-RO*f, 512 стр.. с ал. ХЮО >
MS Sterner Ыо.тлПи» S&IV*F 4.0 « Mi Pi<i«s

Рис. 1-5. Выбор книг в Интернет-магазине


Здесь после регистрации в качестве покупателя Вы можете просмотреть спи-
сок книг и другого товара. Щелкнув название книги, Вы увидите ее подробное
описание. С помощью ссылки купить книга может быть добавлена в Вашу пер-
сональную корзину покупателя.
На рис. 1-6 мы показали содержимое корзины после добавления в нее не-
скольких книг.
Здесь Вы можете отредактировать заказ, изменив количество заказанных
экземпляров книг, или удалить книгу из корзины, щелкнув крестик в столбце
Удал, напротив названия книги.
При создании собственного электронного магазина Вам надо решить множе-
ство вопросов. Вот некоторые из них:
• необходимо организовать базу данных, содержащую сведения о товаре, по-
ставщиках, клиентах, сотрудниках, доставляющих товар покупателям, бухгал-
терскую информацию, сведения о платежах и т. д.;
ф часть этой базы данных, содержащая сведения о товаре, должна быть доступ-
на всем покупателям в Интернете, причем покупателям надо предоставить
возможность не только ознакомится со всеми достоинствами товара, но и
«положить» купленное в специальную электронную корзину;
• следует тщательно продумать вопрос оплаты товара покупателями — кре-
дитными карточками, через банк или наличными деньгами по получении
товара ;
Глава 1. Сервер Web как ядро приложений для Интернета 15

Книги, отобранные Вами для покупки

Цена, Коч- Сумял. Отказ от


N Книга
руб. EH |iyo. покупки
1 Компьютерные сетин-. Учебный куре 23G I? 22Ш'% г- и

2 Разработка рас поеден е иных приложвьий на Nicrosoft Visjal Basic 150


6.0 Учебный курс
3 Разработка приложений на Microsoft Visual Basic a 0 Учебный курс 150

Л /«(гло-русски и толковый словаре по в BI ч целительной '


Интернету и программировании;
£ Основы AS/400

Изменив значения в поле Кол-во или i тку фпатаов в поле ClTK.il ш покупки, щелкните к-опку
Пересчитать

Всего Вы отобрали 7В экэ 5 наимзн на сумму 1 '350 руб. (Ееэ учета доставим)

Йптовын покупателям: для пс-л/чения окончательной суумы заказа примените скидку, согласованн
поставщиком

ЙИЮВШЕШ Доставка
Перед; оформлением заказа выберите наименование области, края или республику, куда необ«одимс
поставить приобретенный Вами товар В эаеисимо:ти от BaiLerc выбора а также от общего веса
приобретении» «чиг будет рассчитана стоимость доставки.

Рис. 1-6. Просмотр содержимого корзины покупателя


ф процесс обработки кредитных карточек должен выполняться в безопасном
режиме, исключающем возможность похищения важной информации треть-
ими лицами;
4 необходимо продумать вопросы администрирования магазина через Интер-
нет при помощи специально разработанного для этой цели приложения Web;
ф административное приложение должно позволять редактировать базу данных
и составлять подробные отчеты о клиентах, товарах, поставщиках, продажах,
а также собирать необходимую статистическую информацию.
Много проблем вызовет обработка номеров кредитных карточек.
Как правило, если Вы создаете Интернет-магазин с оплатой товара посред-
ством кредитных карточек, Вам нужно заключить договор с компанией, выпол-
няющей обработку (нроцессинг) таких карточек через Интернет. К сожалению,
на момент написания этой книги мы не слышали ни об одной подобном процес-
синговом центре в России. Однако Вы можете заключить договор на обработку
кредитных карточек и с зарубежными компаниями.
После этого Вы полностью уясните себе технические детали процесса обра-
ботки. Например, это может выглядеть так.
Вы получаете в процессинговом центре библиотеку динамической компонов-
ки DLL или элемент управления ActiveX и размещаете его на сервере Web Ва-
шего магазина. Кроме того, Вам выделяется страница на сервере Web процессин-
гового центра, выполняющая обработку номеров кредитных карточек с исполь-
зованием защищенного соединения.
16 Базы данных в Интернете. Практическое руководство

Когда пользователь собирается оплатить товар, Ваш сервер Web направляет


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

:
Передает номер своей
Выполняет операцию
кредитной карточки
снятия денег со счета
в процессинговый
покупателя
центр

СП ^ __

Посетитель '"—
Интернет-магазина роцессинговый
центр

Передает результат
заверщения транзакции
и номер транзакции
на сервер
Интернет-магазина

Сообщает покупателю
о результате
выполнения платеса

Рис. 1-7. Обработка кредитных карточек при покупке товара в Интернет-магазине


Эта схема, с одной стороны, избавляет Вас от необходимости создавать защи-
щенное соединение между посетителем Интернет-магазина и Вашим сервером
Web (перекладывая все эти проблемы на процессинговый центр), а с другой -
защищает интересы посетителей магазина, так как номера кредитных карточек
не становятся известны администратора сервера Web вашего магазина.
Глава 1. Сервер Web как ядро приложений для Интернета 17

Пользуясь номером транзакции, Ваши сотрудники всегда смогут проследить


прохождение платежа, взаимодействуя с процессинговым центром. Это иногда
требуется для разрешения всякого рода конфликтных ситуаций.
Описанная выше процедура обработки кредитных карточек проиллюстриро-
вана на рис. 1-7.
Еще одна проблема, с которой Вы столкнетесь, такова: номера кредитных
карточек иногда воруют. Время от времени процессинговый центр будет Вам
присылать списки украденных номеров кредиток, с помощью которых были
сделаны покупки в Вашем магазине. Это — чистый убыток. Поэтому, открывая
электронный магазин в Интернете, будьте готовы, что какую-то часть товара
попросту украдут. Практика показывает, что доля покупок, сделанных по укра-
денным номерам кредиток, составляет несколько процентов.
Помимо перечисленных выше вопросов, Вам придется продумать способ по-
лучения и доставки товара, однако эти (и многие другие) вопросы коммерции
не связаны с тематикой настоящей книги.
Как видите, создание полноценного электронного магазина в Интернете пред-
ставляет собой достаточно сложную задачу. Наша книга не ставит перед собой
цель решить ее, однако мы рассмотрим вопросы, связанные с организацией
пользовательского интерфейса, интерфейса с базой данных, интерфейса с пла-
тежной системой банка, обрабатывающего кредитные карточки, и другие техни-
ческие вопросы, без которых не обойтись при создании своего магазина.
В качестве технической базы электронного магазина мы будем использовать
SQL Server 7.0 и Microsoft Internet Information Server 4.0, работающие в среде
Microsoft Windows NT Server 4.0. На рабочем месте администратора и сотруд-
ников магазина, как мы считаем, установлен браузер Microsoft Internet Exp-
lorer 5.0. Что же касается рабочих мест покупателя, то мы постараемся обеспе-
чить максимально возможную совместимость с различными компьютерными
платформами.

Установка программ и подготовка к работе


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

Операционная система сервера


Прежде всего, на более или менее мощный компьютер с 128 Мб оперативной
памяти и 10 Гб дисковой памяти установите операционную систему Microsoft
Windows NT Server 4.0.
Далее если Вы собираетесь пользоваться в своих разработках кириллицей,
откройте папку Control Panel и с помощью аплета Regional Settings выберите
Russian. Пометьте переключатель Set as system default locale. Только после
18 Базы данных в Интернете. Практическое руководство

выполнения этой процедуры установите пакет обновления Service Pack для


Windows NT версии 6.0 или более новой версии.
Важно отметить, что, если в процессе работы Вам пришлось менять конфи-
гурацию операционной системы. Вам придется выполнить повторную установ-
ку пакета обновления Service Pack для Windows NT.
Браузеры
Далее обновите браузер Microsoft Internet Explorer до версии 5.0. Вы можете
рассматривать эту операцию как второй этап установки пакета обновлений.
Фактически без этого Вам не удастся полноценно работать со средствами раз-
работки приложений, созданных Microsoft и справочной библиотекой MSDN
Library.
Для тестирования совместимости страниц HTML и клиентских сценариев
стоит установить еще несколько браузеров и прежде всего — браузер Netscape
Navigator (он доступен по адресу http://www.netscape.com). Устанавливая дру-
гие браузеры, оставьте приоритет за Microsoft Internet Explorer — по умолчанию
именно он должен открывать файлы с расширением имени htm и html.
SQL Server
Хотя по умолчанию мы считаем, что Вы пользуетесь Microsoft SQL Server вер-
сии 7.0, большинство приведенных в нашей книге примеров (но не все) работа-
ют и с версией 6.5.
Вы можете устанавливать SQL Server как обычно, пользуясь руководством
по установке или одной из книг, упомянутых в списке литературы. Главное -
не забудьте выбрать правильный тип сортировки и установить самые свежие
пакеты обновления Service Pack для SQL Server. На момент написания этой
книги для SQL Server 6.5 был доступен пакет обновления Service Pack 5a, а для
SQL Server 7.0 — пакет обновления Service Pack 1.

Сервер 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 Developer Studio


Почти все примеры в этой книге подготовлены с использованием Microsoft
Visual Studio 6.0. Устанавливая Microsoft Visual Studio, не забудьте выбрать та-
Глава 1. Сервер Web как ядро приложений для Интернета 19

кие его компоненты, как 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

Сценарии в страницах HTML и DHTML

Прежде чем Вы начнете использовать страницы ASP и непосредственно рабо-


тать с базой данных, Вам необходимо освоить основные приемы создания доку-
ментов HTML и DHTML с клиентскими сценариями на языках JavaScript и
VB Script. Мы предполагаем, что Вы уже владеете этими языками программи-
рования, поэтому не стали подробно излагать их в книге. Однако краткую спра-
вочную информацию по JavaScript и VB Script мы поместили в приложение к
книге. При необходимости Вы можете к ней обратиться.

Объектная модель браузера Microsoft Internet Explorer


Для выполнения каких-либо действий над страницами HTML, загруженными в
окно браузера, клиентский сценарий должен обращаться к интерфейсам объек-
тов сценария, доступных в браузере.
Схематически объектная модель браузера Microsoft Internet Explorer пред-
ставлена на рис. 2-1. Вы можете обращаться к этому рисунку при составлении
клиентских сценариев для указанного браузера.

all

history links

navigator anchors

frames | forms I—H elements

window document scripts

location frames

event images

screen selection

stylesheets

body

Рис. 2-1. Объектная модель браузера Microsoft Internet Explorer


. Базы данных в Интернете. Практическое руководство

Объект window соответствует окну браузера Microsoft Internet Explorer. Он


содержит такие объекты, как d o c u m e n t , navigator, frames, history, location, screen
и event. В свою очередь, объект document может содержать объекты links, anchor,
f o r m s и другие, а объект f o r m s — объект elements.
Когда пользователь загружает в окно браузера страницу HTML, перечислен-
ные выше объекты создаются и становятся доступными для сценариев. Их со-
став и количество зависит от содержимого загруженной страницы.
В таблице 2-1 Вы найдете описание некоторых объектов браузера Microsoft
Internet Explorer.
Таблица 2-1. Объекты браузера Microsoft Internet Explorer
Объект Описание
window Представляет собой сам браузер и считается корневым в иерархии объектов
history Содержит методы для имитации кнопок браузера, предназначенных для
просмотра предыдущей и последующей страницы
navigator Позволяет извлечь информацию о браузере — название браузера и помер
его версии. Полезен в тех случаях, когда сценарий должен учесть особен-
ности браузера
frames Позволяет ссылаться на окна набора фреймов. Мы будем его использо-
вать в тех случаях, когда страница HTML подготовлена с применением
фреймов
document Представляет собой страницу HTML, загруженную в окно браузера. В
зависимости от содержимого страницы этот объект может включать в
себя другие объекты, размещенные па странице, — ссылки, локальные
ссылки, формы и т. д.
location Содержит адрес URL страницы, загруженной в окно браузера. Изменяя
свойства этого объекта, можно загрузить в окно браузера новую страницу
event Содержит информацию о возникающих событиях
links Представляет собой массив всех ссылок, расположенных в загруженной
странице HTML
anchors Содержит массив всех локальных анкеров внутри страницы HTML
forms Представляет формы, размещенные внутри страницы HTML. Мы будем
использовать этот объект для доступа к нолям и другим элементам уп-
равления, расположенным в формах
elements Содержит массив всех элементов управления, расположенных в текущей
странице HTML
scripts Представляет сценарии, расположенные в загруженной странице HTML
В зависимости от содержимого страницы HTML, загруженной в окно брау-
зера, сценариям становятся доступными и другие объекты, например i m a g e s и
applets.
Заметим, что браузер Netscape Navigator имеет другой набор объектов и дру-
гие связи между ними.
Окно браузера Netscape Navigator соответствует объекту w i n d o w , содержаще-
му такие объекты как Frame, document, Location и History. Объект Document, пред-
ставляющий собой документ HTML, загруженный в окно браузера, содержит в
свою очередь объекты Form, Layer. L i n k , Image, A r e a , A n c h o r , A p p l e t и P l u g i n .
Глава 2. Сценарии в страницах HTML и DHTML 23

Если документ HTML, загруженный в браузер Netscape Navigator, содержит


формы, то соответствующие объекты Form могут включать к себя объекты, со-
зданные для элементов форм, таких, как переключатели, поля ввода текстовой
информации, списки и т. д. Это объекты Text,Textarea, Password, Hidden, S u b m i t ,
Reset, Radio, Checkbox, Button, Select и FileUpload. Объект Select, представля-
ющий собой список, содержит объекты Option, создаваемые для строк списка.
Более подробную информацию об объектах браузера Netscape Navigator Вы
найдете в Интернете по адресу http://developer.netscape.com/doc/manuals, а так-
же в литературе, список которой приведен в конце нашей кнш и.
В качестве простого примера обращения сценариев JavaScript к объекту
N a v i g a t o r мы приведем л и с т и н г небольшого документа HTML, расположенно-
го в файле Browserlnfo.html (листинг 2-1).
Листинг 2-1 Вы найдете в файле chCH/Browserlnfo.html на прилагаемом к кни-
ге компакт-диске.
Обращаясь к свойству браузера navigator.appName мы можем определить
название браузера:
document. writeln("<TRxTD>Ha3BaHne браузера: </TDXTD>"
+ navigator,appName.bold() + "</TO></TR>");
Аналогичным образом мы определим и другие параметры браузера и систе-
мы, такие, как версия, тип процессора, язык и т. д.:
document.writeln("<TRxTD>KoAOBoe имя браузера:</TD><TD>"
+ navigator. appCodeName.boldO + "</TDx/TR>"};
document. writeln("<TRxTD>Bepcnn браузера:</TD><TD>"
+ navigator.appVersion.bold<) + "</TD></TR>");
document. writelrv{ "<ТВ><ТО>Агвнт HTTP: </TD><TD>"
+ navigator. userAgent. bold() + "</TDX/TR>");
document.writeln("<TR><TD>Tnn процессора:</TD><TD>"
+ navigator.cpuClass.boldO + "</TDX/TR>");
document. writeln("<TRxTD>fl3biK системы:</TD><TD>"
+ navigator.systemLanguage. bold() + "</TDx/TR>");
document. writeln("<ТРхТО>Язык пользователя: </TD><TD>"
+ navigator. userLanguage. bold() + "</TDX/TR>"};
document. writeln("<TRxTD>BTOpaa цифра версии браузера:</TD><TD>"
+ navigator. appMinorVersion.bold() + "</TDx/TR>");
При загрузке этого документа в окно браузера Вы увидите сведения о вер-
сии браузера. На рис. 2-2 и 2-3 мы показали, что получится при загрузке доку-
мента Browserlnfo.html в браузер Microsoft Internet Explorer 5.0 и в браузер
Metscape 4.51.
Обратите внимание, что браузер Netscape не показал нескольких свойств, с
которыми легко справился Microsoft Internet Explorer. Это свойства n a v i g a -
tor. cpuClass, navigator,systemLanguage, n a v i g a t o r . u s e r L a n g u a g e и naviga-
t o r . a p p M i n o r V e r s i o n , появившиеся в последних версиях браузера Microsoft
Internet Explorer. Даже на таком простейшем примере видно, что сценарии
JavaScript могут работать по-разному в разных браузерах.
Далее мы расскажем о применении различных объектов описанной выше
модели браузера.
..: Базы данных в Интернете. Практическое руководство

ЭИнформация о вянем браузере Microsoft Internet E»i>l»iei

Urns Й]Вэ!( tilth!

.
Информация о вашем браузере
Название браузера: 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 п йпьз ОБ ателя.
Втор м цифра Еер п
браузера

Рис. 2-2. Информация о браузере Microsoft Internet Explorer

Sj Информация о вашем браузере - Neticape

Reload Home Searck M

Информация о вашем
браузере
Название браузера Netscape
Кодовое имя браузера. Mozifla
Версия браузера. 4.51 [<?п[ (\VinNT; I)
Агент HTTP Ы(кШа.;4.51 [та] (WinNT: I)

Рис. 2-3. Информация о браузере Netscape

Применение сценариев для создания интерфейса


пользователя
При создании Интернет-магазина мы используем диалоговый интерфейс, осно-
ванный на применении форм и фреймов. Если Вы знакомы с языком HTML, то
должны знать, что формы создаются с помощью тегов <FORM>. Внутри формы
могут располагаться текстовые поля ввода, списки, переключатели, кнопки и
другие объекты.
Как выполняется обработка д а н н ы х , введенных пользователем в форме?
Тут возможны два варианта. Первый таков: .чанные, введенные пользователем
при помощи формы, сразу переправляются на сервер для проверки и обработ-
ки. Во втором случае эти данные предварительно проверяются клиентским сце-
нарием.
Глава 2. Сценарии в страницах HTML и DHTML 25

У каждого из этих двух способов есть свои преимущества и недостатки.


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

Начальная регистрация пользователя


Как правило, прежде чем пользователь попадет к виртуальным прилавкам Ва-
шего магазина, ему необходимо выполнить регистрациго. В процессе регистра-
ции он сообщит Вам свое полное имя, почтовый и электронный адреса и, воз-
можно, другую информацию. Все эти сведения Вам необходимо сохранить в базе
данных.
В этом разделе мы расскажем о том, как выполнить предварительную провер-
ку регистрационных д а н н ы х , в в е д е н н ы х пользователем, перед их отправкой на
сервер. Сам процесс отправки мы рассмотрим позже, в разделе, посвященном
страницам ASP.
В качестве примера мы подготовили небольшую форму, предназначенную для
регистрации посетителей магазина (рис. 2-4).
В полях этой формы посетитель должен указать сведения о себе, необходи-
мые для оформления покупки. Заметьте, что здесь пользователю не предлагается
ввести номер своей кредитной карточки. Такая информация должна передавать-
ся напрямую на сервер компании, занимающейся обслуживанием кредитных
карточек, минуя сервер Интернет-магазина.
Некоторые поля в этой форме отмечены как обязательные для заполнения.
Перед тем как отправлять введенные данные на сервер для записи в базу дан-
ных покупателей, было бы неплохо проверить правильность заполнения полей
формы. Для этой цели мы используем сценарий JavaScript, который:
ф проверяет, заполнил ли посетитель обязательные поля, отмеченные в форме
символом «*»;
ф сравнивает пароль с подтверждением пароля — они должны совпадать;
f проверяет e-mail, который должен содержать по крайней мере один символ
«@» и одну точку.
Базы данных в Интернеге, Практическое эуководство

Регистрация посетителей магазине - Mirmsotl Inler.. F I '

^J Ei'JRussionEditiom!' Book Interne! DB\Sourcekhd »

Регистрация посетителей магазина


Псиц отмечзнЕые cm здшм', нужно запошить обязательна

Введите нмя,отче.;тво л фамилию: '


[Иван Петрович Сидоров

Укажите страну, в копрой ли проживите:

[Россия 3
Вши жо.таын почтовый алргг:

|ООЭ000. Москва ул. Первня.дом 1, кв.1

E-Mtil:

|ivanov@MaNServBr.MoilDomain

Ваш телефон:

Нпторхте под nip о ли:

Рис. 2-4. Форма регистрации посетителей магазина


В качестве тренировки Вы можете добавить строку для проверки номера те-
лефона — в нем допустимы пробелы, символы «-» и цифры. Чтобы отличить
цифры от других символов, используйте функцию isDigit.
Исходный текст документа HTML с формой и сценарием JavaScript показан
в листинге 2-2. Этот документ был создан при помощи Microsoft FrontPage.
Клиентские сценарии редактировались в среде Microsoft InterDev 6.0.
Листинг 2-2 Вы найдете в файле ch01/RegisterForm.html на прилагаемом
к книге компакт-диске.
В документе имеется форма с полями ввода регистрационной информации:
<form name="RegForn" action="http://www.YourServer.YourDomain/YourCGI.cgi"
metod="post">
<table border="0" cellPadding="0" cellSpacing="0" width="385" height="340">
<trxtd valign="top" align="left" colspan="2">
<Ь>Введите имя, отчество и фамилию: </b> <font color="«FFOOOO">*</fontx/tdx/tr>

<select name="country" size="1">


<option selected value="Poccnfl">PoccHR</option>
<option уа1ие="Беларусь">6еларусь</ор11оп>
<option value="Чexocлoвaкия">ЧexDCлoвaкия</option>
<option уа1ие='Термания">Германия</ор^оп>
<option value="Греция">rpeunfl</option>
<option уа!ие="Польша">Польша</ор11оп>
<option уа1ие="США">США</ор11оп>
<option value="yKpaHHa">yKpaHHa</option>
Глава 2. Сценарии в страницах HTML и DHTML 27

<option уа1ие="Швейцария">Швейцария</ор11оп>
</selectx/td></tr>

<input type="button" onClick="checkForm();"


value="Регистрация" name="B1"x/td>
<td valign="top" align="left">
<input type="reset" уа!ие="0тмена" name="82"x/tdx/tr>
</table>
</form>

Обращаем Ваше внимание на следующий фрагмент этого документа:


<input type="button" onClick="checkForm();"
value="Регистрация" name="B1"x/td>
Когда посетитель щелкнет мышью кнопку Регистрация, будет вызвана фун-
кция checkForm, выполняющая проверку полей формы:
function checkFormO
{
if(document.RegForm.name.value == "" ||
document.RegForm.addr.value == "" ||
document.RegForm.email.value == "" ||
document.RegForm.password.value == "")

alert("Bbi заполнили не все обязательные поля формы");


return;

if(document.RegForm.password.value != document.RegForm.passwordcnk.value)

alert("OuiH6Ka при подтверждении пароля");


return;
:
iff!mailAddressIsValid(document.RegForm.email.value))
{
alert("Oum6Ka в адресе E-Mail");
return;

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);
Базы данных в Интернете. Практическое руководство

Если при вводе допущены ошибки, функция отобразит на экране пользова-


теля сообщение об ошибке, а если все правильно — отправит данные на сервер
Web для обработки программой CGI.
Доступ к полям формы выполняется при помощи объекта document, например:
document.RegForm.name.value
Здесь мы ссылаемся на имя формы ( R e g F o r m ) , на имя поля (name), а также на
свойство value, содержащее значение этого поля.
Для проверки правильности ввода почтового адреса мы применили функцию
mail Address Is Valid:
function mailAddressIsValid(mailAddress)
{
var atPresent=false;
var dotPresent=false;
for(var i=0; i < mailAddress,length; i++)

var c=mailAddress.substring(i, i+1);


if(c == "@")
atPresent=true;
if(c == '•.•')
dotPresent=true;
}
if(atPresent == true && dotPresent == true)
return true;
else
return false;
!
Если все данные введены правильно, мы отображаем их на экране функци-
ей alert и затем отправляем на сервер Web. Для отправки данных формы при-
меняется метод submit, как это показано ниже:
document. RegForm. submitO;
Такое действие эквивалентно щелчку кнопки типа Submit, применяемой в
обычных формах для отправки данных.
Ввод календарных дат
Еще одна задача, часто возникающая при разработке приложений Web, это ввод
календарных дат и диапазонов календарных дат. Например, Вам может потре-
боваться список клиентов, зарегистрировавшихся в определенный период вре-
мени, или список платежных транзакций, выполненных за определенный пери-
од времени.
При кажущейся простоте этой задачи ее реализация тем не менее может быть
затруднена рядом «подводных камней*-.
Во-первых, если разрешить пользователю вводить даты в свободном форма-
те, Вашему приложению придется иметь дело не только с ошибочно введенны-
ми датами, но и со множеством правильных форматов дат. В зависимости от
страны проживания пользователи будут указывать дату по-своему, и это силь-
но затрудняет Вашу задачу.
Глава 2. Сценарии в страницах HTML и DHTML

Во-вторых, необходимо, чтобы при проверке дат учитывалось количество


дней в указанном месяце, а также високосные годы.
И, наконец, при записи даты в базу данных или при использовании дат в за-
просах SQL Вам придется преобразовать ее в фиксированный формат, с кото-
рым сможет работать программа SQL.
С учетом сказанного выше, на наш взгляд, лучше всего применять для ввода
дат такие органы управления, как списки. Предварительно в них следует указать
возможные значения дат.
Нам понравилось, как решена проблема ввода диапазона дат Максимом Си-
невым из компании Spektrum Web Development (http://www.spektrum.org.ru). На
рис. 2-5 мы показали форму с календарями, предназначенную для просмотра
списка покупателей по дате регистрации.
В этой форме мы применили (в немного измененном виде) разработанное им
решение для ввода диапазона дат, основанное па применение средств DHTML
и сценариев JavaScript. Заметим, что в силу использования объектной модели
браузера Microsoft Internet Explorer этот метод не пригоден для работы с дру-
гими браузерами. Однако данное обстоятельство не имеет решающего значения
при создании приложений Web для интрасетей и административных приложе-
н и й Web н Интернете.
Как пользоваться формой, показанной на рис. 2-5?
Все операции выполняются мышью. Щелкая ссылки вида <« и >». Вы
можете изменять год в начальной и конечной дате. В данном случае выполня-
ется поиск пользователей, зарегистрировавшихся в период с 1998 до 2000 года.
3 EAIHussion EdilionM! Book Internet О В \Sejurce\cMmCu si on : h hlml Miciocoir Internet Expl
View Fjvoiites Xcols
J& ' " LHs gjeestdtheWsb gjChamelGuide

Просмотр списка покупателей

Ныя, втчестю или фамилия покупателя: ]ИвансЕ

Адфес E-Mail: fVailOVCj

I-* искать по дате регистрации:

Начальная дата Конечная дата

Поиск
....
I My Cqinputei

Рис. 2-5. Форма для просмотра списка покупателей по дате регистрации


Щелкая название месяца и календарной даты в левом и правом календаре, Вы
установите период времени с точностью до одного дня. В нашем случае началь-
Базы данных в Интернеге, Практическое эуководство

нал дата — 3 марта 1998 года, а конечная — 29 февраля 2000 года. Для нагляд-
ности названия месяцев и календарные даты выделены в окне формы желтым
цветом.
Для того чтобы запустить поиск, достаточно щелкнуть мышью ссылку Поиск.
Если снять флажок у переключателя искать по дате регистрации:, календа-
ри в форме исчезнут, причем это произойдет мгновенно, без повторной загруз-
ки формы с сервера Web (рис. 2-6).
ВЕЛШиКЮл EdilionV! Book Inlejnel DB\Souice\.ch01 \CuslomeiSearch.h... ВВЕЗ I

'Book Inter net DB\Souree\ch01SCuslomepSeafcKhtmj

Просмотр гпнскя покупателей

Ibm отчести нлнфамкиняиякулаталн: [Иванов

Алфее E-Mail: vanov@tvlfiJI3erver.M9JIDoma

: \ Kraaib по дате регистрации'

Поиск

JTJ My Compute

Рис. 2-6. Теперь дата регистрации не учитывается


После его повторного включения календарь также быстро появится на экра-
не вновь. Операции рисования календаря, его сокрытия и повторного отображс-
м и я выполняются с применением сценариев JavaScript и объектной модели
DHTML.
Форма, предназначенная для определения параметров просмотра, определе-
на следующим образом:
<form method="POST" id="SearchForm">
<table border="0" cellpadding="5" cellspacing="0">
<tr>
<tdxtable cellspacing="5">
<tr>
<td><small><strong>HMfl, отчество или фамилия покупателя:
</strong></smallX/td>
<td><input type="text" name="LOGNAME" id="LogName" size="25">
</tdX/trXtr>
<td><smallxstrong>Aflpec E-Mail :</strongx/smallx/td>
<td><input type="text" name="EMAIL" id="CEMail" size="25"x/td>
</trxtr>
<td><small><input type="checkbox" name="USEDATE"
id="UseDataCheckBox"
onClick="hidecalendar();"
Т1Т1Е="Выключите для игнорирования даты регистрации"
value="ON"x/small><strong>HCKaTb по дате регистрации<Ыд>:
</bigx/strong></tdxtdx/tdx/tr>
</table>
Глава 2. Сценарии в страницах HTML и DHTML 31

</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>

В нем хранится большинство функций сценария JavaScript, обеспечивающих


работу календарей. Полный текст файла calendar.js Вы найдете в листинге 2-4.
Листинг 2-4 хранится в файле ch01/calendar.js на прилагаемом к книге ком-
пакт-диске.
Теперь мы расскажем о том, как устроен документ HTML CustomerSearch.html
(листинг 2-3). При внимательном изучении видно, что помимо всего прочего он
содержит две пустые таблицы для календарей:
<table border="0" cellpadding="0" cellspacing="0" id="calendarTable">
<tr align="middle" bgcolor="#ffffcc">
^(!><И2>Начальная дата</Ь2>
</td>
<tdxh2>KoHe4Haa дата</Ь2>
</td>
</tr>
<tr"valign="top">
<td><table border="0" cellpadding="0" cellspacing="5">
<tr>
<td align="left" ID="FromPrev"x/td>
<td align="middle"xb ID="FromYear"x/b></td>
<td align="right" ID="FromNext"x/td>
</tr><trxtd colspan="3"Xhr width="100X"> </tdx/tr>
<tr valign="top">
<td ID="FromMonths1"x/td>
<td ID="FromDays" valign="top"x/td>
<td ID="FromMonths2"x/td>
</tr>
</tabLe>
</td>
<tdXtable border="0" cellpadding="0" cellspacing="5">
<tr>
<td align="left" ID="ToPrev"x/td>
<td align="middle"><b ID="ToYear"x/bX/td>
<td align="right" ID="ToNext"x/td>
</trxtrxtd colspan="3"Xhr width="1QOr> </tdx/tr>
(см. след, стр.)
32 Базы данных в Интернете. Практическое руководство

<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

var today = new DateQ;


byear = today.getFullYear();
bmonth = today.getMonth()+1;
bday = 1;
eyear = today. getFullYearQ;
emonth = today.getMonth{)+1;
eday = today.getOate();
refbyO;
fillfmonthsQ;
fillfdaysC);
refeyO;
filltmonthsO;
filltdaysO;
document.all.UseDataCheckBox.checked=true;
}
Данная функция вызывается при завершении загрузки документа HTML в
окно браузера. В ее задачу входит определение текущей даты и ее составляю-
щих— номера гола, месяца и числа. Для этой цели вызываются функции get-
FullYear, getMontn и getDate.
Далее для заполнения полей таблицы начальной даты функция buildmap по-
следовательно вызывает функции refby, f i l l f m o n t h s и f i l l f d a y s :
function refby()

FromYear.innerText=byear.toString();

if(byear>1901) FromPrev.innerHTML='<a
href =" javascript :prevbyear();''>&lt;&lt;&lt;</a>';
else FromPrev.innerHTML="&lt;&lt;&lt;";
Глава 2. Сценарии в страницах HTML и DHTML 33

if(byear<eyear) FromNext. innerHTML='<a


href =" javascript: nextbyearf); ">&gt;&gt;&gt;</a>';
else FromNext. innerHTML="&gt;&gt;&gt; "
I

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;

var line="<table border=0 cellpadding=0 celZspacing=0>";


for(var j=6; j<12; j++) {
if(j+1==bmonth) month_selected=true:
if(byear==eyear && j==emonth) month^selected=false;
if(month_selected) {
line=line+'<tr><td bgcolor="JfFFFFCC11>1 ;
} else {
line=line+'<tr><td bgcolor="ffFFFFFF">' ;
I
if(j+1==bmonth) {
line=line+months[j];
} else {
line=line+'<a href=" javascript: selfmonth( '+j .toString()+' ); ">'
+ months[j] +'

line=line+X/tdx/tr>';
}
line=line+'</table>';
FromMonths2. innerHTML=line;

(см. след, стр.)


34 Базы данных в Интернете. Практическое руководство

function fillfdays()

var f rom_day=firstDay(byear, bmonth);


var max_day=monthDuration(byear, Qmonth);
var cur_day=0;
if(max_day<bday) bday=max_day;

if (byear==eyear && bmonth>=emonth) {


emonth=bmonth;
if (bday>eday) eday=bday;
}

var Hne="<table border=0 cellpadding=0 cellspacing=0>";

line=line+'<tr><td colspan=9 align="center" bgcolor="#CCCCEE"


width="*"><b>'+months[bmonth-1]+'</bx/td></tr>';

var markday=false;

for(var i=0; i<6 && f rom_day+max_.day>cur_day; i++) {


line=line+'<tr>' ;
line=line+'<td bgcolor="ttCCCCEE" width="8)!">&nbsp; </td>' ;
for(var j=0; j<7; j++) {
if((cur_day-f rom_day)+1==bday) markday=true;
if(f rom_day+max_day==cur_day) markday=false;
if(cur_day-f rom_day==eday ЗЛ (byear==eyear && bmonth==emonth))
rnarkday=false;
if(markday) {
line=line+'<td align="center" bgcolor="#FFFFCC" width="13X">T ;
}
else {
line=line+'<td align="center" width="13X">' ;
}
var printdate=(cur_day>=f rom_ijay && from_day+max_day>cur_day);
var makeref=(printdate && (cur_day-f rom_day)+1!=bday);
if(makeref) line=line+'<a href="javascript:selfday('+(cur_day-f rom_day+1).
toString()+');">';
else line=line+'<b>' ;
if (printdate) line=line+(cur_day-f rom_day+1).toString();
if(makeref) line=line+'</a>' ;
else line=line+'</b>' ;

line=line+'</td>';
cur_day++;
}
line=line+'<td bgcolor="SCCCCEE" width="*">&nbsp;
line=line+'</tr>' ;

Line=line+'</trXtd bgcolor="ffCCCCEE" width="8S!">&nbsp;</td>' ;


forfvar j=0; j<7; j++) {
line=line+'<td bgcolor="#CCCCEE" width="12X">' ;
Глава 2. Сценарии в страницах HTML и DHTML 35

line=line+daysOfWeek[j] ;
Hne=line+'</font>&nbsp;&nosp; </td>';
}
line=line+'<td bgcolor="#CCCCEE" width="*">&nbsp;</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(byear>1901) FromPrev.innerHTML='<a href="javascript:prevbyearf);">


&lt;&lt;&lt;</a>';
else FromPrev. innerHTML="&lt;&lt; Ш;";

if(byear<eyear) FromNext.innerHTML='<a href="javascript:nextbyear();">


&gt;&gt;&gt;</a>';
else FromNext.innerHTML="&gt;&gt;&gt;"

Присваивая новое значение свойству innerText, сценарий JavaScript спосо-


бен динамически изменять содержимое тегов страницы, отображаемой в окне бра-
узера. На этой возможности DHTML и строится вся работа данного сценария.
Последнее действие, выполняемое функцией b u i l d m a p , это активизация пе-
реключателя искать по дате регистрации:. Именно этот режим используется н
форме по умолчанию:
document.all.UseDataCheckBox.checked=true;
В файле calendar.js содержится определение функции buildmapEx — она по-
зволяет выполнить предварительную установку календарей для заданного диа-
пазона дат:
function buildmapEx(FY, FM, FD, TY, TM, TD, DateCheck)
{
byear = FY; bmonth = FM; bday = FD;
eyear = TY; emonth = TM; eday = TD;
refby();
fillfmonthsO;
fillfdaysO;
refeyO;
filltmonthsO;
filltdaysO;

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>&nbsp;
<a href=' javascript:go(); p >n.oHCK</a>&nbsp;</h1x/tdx/trx/table>";

Еще одна функция, представляющая интерес, определена в файле Customer-


Search.html (листинг 2-3). Это функция hidecalendar, предназначенная для того,
чтобы отображать и убирать с экрана календари при изменении состояния пе-
реключателя искать по дате регистрации:. Вот ее исходный текст:
var sCalendarPreserve;
var oCurrentNode;
var oCalendarTable;
var oPNode;
var oNewNode;

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>&nbsp;
<a href=']avascript;go(); '>floHCK</a>&nbsp;</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

Функция hidecalendar вызывается по событию onClick, когда пользователь


щелкает этот переключатель. Если флажок снят, функция h i d e c a l e n d a r сохра-
няет в переменной sCalendarPreserve содержимое кода HTML таблицы кален-
дарей с идентификатором calendarTable:
var sCalendarPreserve;
var oCurrentNode;
var oCalendarTable;
var oPNode;
var oNewNode;

if(document. all, UseDataCheckBox. checked == false)


{
oCurrentNode=docunient.getElementBy Id ("calendarTable");
sCalendarPreserve = oCurrentNode. innerHTML;

Далее функция h i d e c a l e n d a r создает новую пустую таблицу, сохраняя ссыл-


ку на соответствующий объект в переменной oNewNode, и замещает таблицу с
календарями этой пустой таблицей:
oNewNode = document. createElement("<table>");
DCurrentNode. replaceNode( oNewNode);
На следующем этапе мы замещаем содержимое таблицы, изменяя свойство
outer-HTML, и выключаем переключатель:
cNewNode. outer-HTML =
"<table id='TableReplaced' border='0' cellpadding='5 - cellspacing='0'>
<tr align='middle' bgcolor= T #ffffcc'xtd><h1>&nbsp;
<a h ref = ' j avascript : go( ) ; ' >Поиск</а>&пЬзр; </h1></td></t rx/table>" ;

document, all. UseDataCheckBox. checked=false;


Новое содержимое таблицы представляет собой одну строку ссылки в виде
слова Поиск. При выборе этой или аналогичной ссылки в исходной таблице с
календарями управление передастся функции до, выполняющей переход к стра-
н и ц е поиска покупателей.
Когда пользователь вновь активизирует переключатель искать по дате реги-
страции:, функция hidecalendar возвращает таблицу календарей в исходное со-
стояние:
oCurrentNode=document, getElementById("TableReplaced");
oPNode = document. createElement("<table>");
oCurrentNode. replaceNode(oPNode);
oPNode. outerHTML = '<table border="0" cellpadding="0" cellspacing="0"
id="calendarTable">' + sCalendarPreserve + x/table> - ;
Что же касается функции go, то мы рассмотрим ее в следующем разделе.

Загрузка новой страницы в окно браузера


Сценарий JavaScript способен загрузить в окно браузера (или в окно фрейма,
если документ содержит фреймы) новый документ HTML, заданный адресом
URL. В частности, если текущий документ содержит форму запроса к базе дан-
38 Базы данных в Интернеге, Практическое руководство

пых, сценарий JavaScript может загрузить в текущее окно браузера документ


HTML (или ASP) с результатами обработки этого запроса.
Именно так и работает функция до из примера, приведенного в листинге 2-3:
function go()

window. location. href = "GetSearchResults.asp?DATECHECK=" +


sUseDataCheckBox+"&FY=" + byear.toString()+
"&FM="+bmonth.toString()+
"&FD="+bday.toString()+"&TY="+eyear.toString() +
"&TM="+emonth.toString()+ "&TD="+eday.toString()+
"&LOGNAME="+sLogName+"&EMAIL="+sEMail+
"&FRCE="+Math . random( ) . toSt ring() ;
}
Здесь в свойство href объекта window, location записывается адрес страни-
цы ASP e названием Get Search Results, asp. Эта страница расположена в том же
каталоге, что и вызывающий ее документ HTML, поэтому вместо полного пути
мы указали только имя файла.
После имени файла проставлен разделяющий символ «?» и достаточно длин-
ная строка параметров, передаваемая странице ASP. Эта строка содержит началь-
ную и конечную дату, имя пользователя, адрес его электронной почты и еще один
параметр с именем FRCE, о котором мы пока умолчим. Строка параметров будет
прочитана и обработана серверным сценарием, встроенным в страницу Get-
Sear chRe suits, asp. Подробнее об этом мы расскажем в следующей главе.
Если Вы не собираетесь выполнять предварительную обработку формы при
помощи клиентского сценария, адрес данной страницы ASP нужно указать в
параметре ACTION тега <FORM>, как это делается при вызове расширений CGI и
ISAPI.
Перекодирование содержимого полей форм
Чтобы данные из полей формы были корректно переданы программам CGI,
ISAPI или странице ASP, они должны иметь так называемую кодировку URL.
В этой кодировке символы пробелов заменяются знаком <<+», а для представле-
ния кодов управляющих и некоторых других символов применяется последова-
тельность вида «Ххх», где символы «хх» представляют собой шестнадцатеричный
код исходного символа в виде двух символов ASCII.
Если Бы передаете программам CGI, ISAPI или странице ASP параметры из
клиентского сценария, преобразование текстовых строк в кодировку URL при-
дется выполнить явным образом. В сценариях JavaScript это легко осуществить
при помощи функции escape. Вот, например, как это делается в функции до из
только что рассмотренного примера:
var sLogName=escape( document. all. Sea rchForm. LogName. value);
var sEMail=escape( document, all. Sea rcnForm.CEMai Lvalue);
Если же вызов программ расширения сервера Web или страниц ASP выпол-
няется через параметр ACTION тега <FORM>, преобразование происходит автома-
тически.
Глава 2. Сценарии в страницах HTML и DHTML

Отключение кэширования страниц


Когда пользователь путешествует по сети Интернет с помощью браузера, посе-
щенные им страницы кэшируются либо самим браузером, либо промежуточным
прокси-сервером. Механизм кэширования заметно ускоряет повторный про-
смотр страниц, так как одни и те же данные не передаются по несколько раз
через медленные каналы Интернета.
К сожалению, механизм кэширования страниц иногда вызывает проблемы в
приложениях Web, создающих страницы динамически при помощи программ
CGI, ISAPI или средств ASP.
Проблемы проявляются, когда пользователь пытается просмотреть несколь-
ко раз одну и ту же страницу, созданную динамически. Даже если каждый раз
эти страницы имеют различное содержимое, браузер будет показывать только
первый вариант, который он загрузил в свой кэш при первом обращении к стра-
нице. В результате пользователю кажется, что страница не обновляется.
Как отменить кэширование страниц?
Едва ли стоит просить пользователя отключить кэширование страниц в его
браузере — это отрицательно скажется на скорости просмотра информации.
Кроме того, информация в некоторых случаях каптируется не локально, а в
прокси-сервере, настроить который пользователь не может.
Первый достаточно известный способ заключается в применении тега <МЕТА>
с параметром HTTP-EQUIV=Expi res, расположенного внутри заголовка документа
< Н EAD>. Этот параметр указывает дату, начиная с которой содержимое докумен-
та считается устаревшим и он подлежит перезагрузке, Например, Вы можете
использовать тег <ИЕТА> следующего вида;
<МЕТА HTTP-EQUIV=Expires CONTENT=Tue, 02 Jan 1996 01:00:00 GMT>
К сожалению, этот простой способ работает не всегда. Более надежный спо-
соб, который можно применить при загрузке динамически создаваемых страниц
с помощью клиентских сценариев, заключается в добавлении фиктивного пара-
метра со случайным значением. Мы применили этот способ в функции до, пол-
ный текст которой приведен в листинге 2-3:
window.location.href = "GetSearchResults.asp?DATECHECK=" +
sUseDataCheckBox+"&FY=" + byear,toString()+

"&FRCE="+Matn.random().toStringO;
Параметр с именем FRCE представляет собой случайное число, полученное от
функции Math, random и преобразованное в текстовую строку функцией toString.
Другие способы отключения кэширования страниц будут описаны в главе,
посвященной серверным сценариям ASP.

Модальные и немодальные диалоговые панели


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

3 Зак. 357]
40 Базы данных в Интернете. Практическое руководство

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

Модальные диалоговые панели


Модальными называются такие диалоговые панели, которые должны быть за-
крыты для продолжения работы приложения. Например, если приложение вы-
водит на экран модальную диалоговую панель с сообщением об ошибке, работа
приложения будет приостановлена до тех пор, пока пользователь не прочитает
сообщение и не щелкнет кнопку ОК,
Для создания окон модальных диалоговых панелей серверный сценарий ис-
пользует методы, перечисленные в таблице 2-2.
Таблица 2-2. Методы для создания окон модальных диалоговых панелей
Метод Параметры и описание
alert alert(sMsg)
Простейшая диалоговая панель с текстовым сообщением sMsg и
кнопкой ОК
confirm confirm(sMsg)
Диалоговая панель с текстовым сообщением sMsg и двумя
кнопками ОК и Cancel. В зависимости от того, какой кнопкой Вы
завершили работу панели, метод возвращает значение t r u e (соот-
ветствует кнопке ОК) или false (соответствует кнопке Cancel)
prompt prompt(sMsg [,sDefaultMsg])
Панель для запроса у пользователя одной текстовой строки. В
окне панели отображается сообщение sMsg. Если указан необяза-
тельный параметр sDefaultMsg, то соответствующая строка появля-
ется в поле ввода
showModalDialog showKodalDialog(sURL [, params [, features]])
Этим методом можно создавать сложные диалоговые панели на
базе документов HTML. Параметр sURL определяет адрес URL та-
кого документа. Необязательный параметр params позволяет пере-
давать в диалоговую панель произвольный набор параметров, а
параметр f e a t u r e s (также необязательный) определяет внешний
вид окна панели. Метод s h o w M o d a l D i a l o g , в отличие от метода
prompt, позволяет запрашивать у пользователя произвольную ин-
формацию, а не только одну строку
Теперь мы рассмотрим несколько практических примеров применения пере-
численных выше методов.

Вывод информационных сообщений


В первом примере мы покажем, как выводить на экран обычные текстовые со-
общения.
На рис. 2-7 изображено окно с двумя кнопками — Вход и Выход. Если щел-
кнуть кнопку Вход, на экране появится приветственное сообщение, показанное
на рис. 2-8, а если щелкнуть кнопку Выход — сообщение, показанное на рис. 2-9.
Глава 2. Сценарии в страницах HTML и DHTML 41

• E:\tRuniqn EtlitionVI Bouk Internet..

Вывод сообщений

Рис. 2-7. Панель для демонстрации способа выдачи сообщений


Исходный текст соответствующего документа HTML представлен в листин-
ге 2-5.
Листинг 2-5 Вы найдете в файле ch02/MessageBox.html на прилагаемом к кни-
ге компакт-диске.
Как видно из этого исходного текста, в документе имеется таблица с двумя
кнопками:
<table border="0" width="27X" cellpadding="2">
<tr><td width="22X">
<input type="button" onClick="msgBox('Добро пожаловать!');"
value="flxofl" name="B1">
</tdxtd width="78S!">
<input type="reset" onClick="msgBox('До свидания!\пЗаходите еще! 1 );"
value=" Выход" name="B2"x/td> </tr>
</table>
Для каждой из этих кнопок мы определили обработчик события onClick, за-
дача которого заключается в вызове функции msgBox.
Эта функция просто обращается к методу alert, передавая ему в качестве
параметра строку сообщения:
function msgBox(sMessage)

alert(sMessage);
,

Рис. 2-8. Сообщение, появляющееся в результате щелчка кнопки Вход

Л Шевиадняч!
Заходите еше!

Рис. 2-9. Сообщение, появляющееся в результате щелчка кнопки Выход


Базы данных в Интернете. Практическое руководство

В данном случае мы воспользовались для выдачи сообщения простейшим


методом alert, так как в ответ на это сообщение пользователю не нужно при-
нимать решение и выбирать одно из нескольких действий. В следующем примере
пользователю предоставлен выбор — он может отреагировать на сообщение раз-
личным образом.
Сообщение с выбором одной из двух возможностей
Иногда для выполнения какого-либо действия необходимо запросить подтвер-
ждение. Например, если администратор базы данных с помощью Вашего при-
ложения редактирует список пользователей, то перед удалением пользователя
было бы неплохо запросить у него дополнительное разрешение. Кроме того,
приложению может потребоваться дополнительная информация, например имя
пользователя.
Все эти задачи решают с помощью методов c o n f i r m и prompt. Покажем, как
это сделать на конкретном примере.
Предположим, нужно удалить данные о пользователе из базы данных. На
рис. 2-10 показано окно браузера, в которое загружен документ HTML, выпол-
няющий данную операцию.
3 E:\!Russion IdmonV! Book Interne! DB\ch02\OI
file £dt View Fff/cntes loeh Help

E iiRussion EoitronVl Book Internet DBtehO

Удаление пользователей из оазы


данных покупателей

ЮЛЬ ЭОВГ5Т6
^ " ЛЯ I

I My Com
.I
Рис. 2-10. Документ HTML, предназначенный для удаления данных о пользователе
Если Вы щелкнете кнопку Удалить пользователя, клиентский сценарий за-
просит имя «жертвы» (рис. 2-11). При этом он вызовет метод prompt.

Рис. 2-11. Ввод имени удаляемого пользователя


Чтобы удалить данные о пользователе, Вам надо ввести его имя и нажать
кнопку ОК. В реальных приложениях имена пользователей хранятся в базе дан-
ных. О том, как удалять данные из базы данных, Вы узнаете позже.
Перед удалением данных о пользователе наш сценарий выводит па экран со-
общение, показанное на рис. 2-12. Оно отображается с применением метода
confirm.
Здесь отображается имя удаляемого пользователя, и предлагается подтвер-
дить выполнение этой операции.
Глава 2. Сценарии в страницах HTML и DHTML 43

'cf\ Вы двйствнтялвиз вдаетв даэяягь попззсватеяя

MS бвзькваты* покцпвгеявй?

[ТИП! CanEd

Рис. 2-12. Запрос подтверждения на удаление пользователя


Если щелкнуть кнопку ОК, на экране появится сообщение об успешном уда-
лении данных о пользователе с заданным именем (рис. 2-13). Оно отображает-
ся методом alert.

успешно аяа<вн из базы мннья покупателей

Рис. 2-13. Сообщение об успешном удалении данных о пользователе


Исходный текст документа HTML со сценарием, выполняющим описанные
выше действия, Вы найдете в листинге 2-6.
Листинг 2-6 хранится в файле ch02/OtherModalMessageBox.html на прилагае-
мом к книге компакт-диске.
Когда пользователь щелкает кнопку Удалить пользователя, управление по-
лучает функция deleteUser:
f u n c t i o n deletetlser()
I
var sllserf4ame="";
sUserName=prompt("BBeflHTe имя пользователя, \пудаляемого из базы данных
покупателей", "<temp user>");
if(sUserName != null && sUserName != "")
{
if(confirm("Bd действительно будете удалять пользователя\п" + sUserName
+ "\лиэ базы данных покупателей?") == true)
I
// Удаление пользователя

тздВохС'Пользователь с именем\п" + sUserName + "\пуспешно удален


из базы данных покупателей");

Она вызывает метод prompt и указывает ему в качестве имени удаляемого


пользователя значение по умолчании] — <temp user>.
Если пользователь завершает работу диалоговой панели кнопкой ОК, метод
prompt возвращает строку имени пользователя. После щелчка кнопки Cancel
метод prompt возвращает значение null. Есть и еще один способ — пользователь
вводит кнопкой ОК пустую строку. При этом метод prompt также возвращает
пустую строку.
Базы данных в Интернете. Практическое руководство

После вызова метода prompt функция deletellser проверяет возвращенное


этим методом значение. Если это не пустая строка и не значение null, сценарий
выполняет действия по удалению пользователя. Б нашем случае эти действия
фактически не реализуются, но Вы можете добавить свой код в то место сцена-
рия, которое выделено строкой комментария,
Когда удаление данных о пользователе завершается удачей, функция delete-
User выводит на экран сообщение об этом, вызывая функцию msgBox:
function msgBox(sMessage)

alert(sMessage);

Диалоговая панель на базе документа HTML


Описанные выше средства создания модальных диалоговых панелей помогут
Вам только в простейших случаях, когда требуется лишь показать пользовате-
лю сообщение, предложить ему выбор из двух возможностей или запросить одну
текстовую строку. Однако иногда надо получить от пользователя обширную
информацию. Например, если Вы создаете электронный книжный магазин, то
при регистрации новых поступлений Вы должны ввести в базу данных такие
сведения, как название книги, имя автора, аннотация на книгу, название изда-
тельства и выходные данные книги, сведения о поставщике и т. д. В этом слу-
чае не обойтись без форм ввода, созданных на основе документов HTML.
Средствами метода showModalDialog Ваш клиентский сценарий может орга-
низовать вывод документов HTML с формой ввода на экран, получение и пред-
варительную обработку введенной информации.
Например, в документе HTML на рис. 2-14 имеется кнопка, предназначенная
для регистрации новых книг (наряду с кнопками регистрации других новых
товаров).
a EAIRussion EditionM! Book Internet D В Vch 02 \MciddlRegistfiiDia Ing. hi ml
£fe £dl View Favorites lools Halp

* E VRussionEdition\i! Book lntanetDB\ch02\Mod.ilRegisteiDialog html _£J •fr'Go-

Регистрация товара

Зарегистрировать нов^ю книгц

,5$ My СотрЫн

Рис. 2-14. Документ HTML для регистрации новых товаров


Если Вы щелкнете эту кнопку, на экране появится модальная диалоговая
панель регистрации новых книг, содержащая форму ввода с полями различных
типов (рис. 2-15).
Заполнив поля в этой форме, Вы можете зарегистрировать книгу в базе дан-
ных, щелкнув кнопку Добавить в базу данных, либо отказаться от регистрации
при помощи кнопки Отменить регистрацию. После этого окно с формой исчез-
нет с экрана.
Глава 2. Сценарии в страницах HTML и DHTML

Регистрация книги
Название |Бозыдвнныхи Интернет

Автор [ФроловА В.. Фролов


Аннотация Данная киига представляет собой i фактическое
руководство по созданию приложений ИеЬ с базами
данных для сети Интернет, а также: для
корпоративной интрасети. Б ней рассмотрены
современные технологии, созданные- Hictoao±t для
работы с базами данным. Основной упор при этом
делается на применение активный страниц Active

Издательство (русскаяРедакция

От менн т ь р е ги страцию

He;iVE:4Russk>n EdrtiwV! Beak Internet DВ\ch02\ModalRacJsterFoi J^ My t.ompiHffl

Рис. 2-15. Документ HTML с формой регистрации новых книг


В случае успешной регистрации на экране появится сообщение, содержащее
текст из всех полей формы (рис. 2-1К).
Microsoft Interne

Название- Б азы данных и Интернет


Автор. ФроловА.В.-'РроявеГВ.
Анигоания Данная кюсз прешгтавляет coewripaKrMHecaoMpiKi^QacrBanoccojiiwwonpbuicJK
базами данных дла сети Интернет, а такие в/га корпоративной интрасетн 3 ней рассмотрены современные
технологии, созданные Micioiott для работы с базами да^ык. Основной утр при этом делается на
применение ЭКТИЕНЫК страниц Aciivs Server Pages [ASP], расширений сервера Web на безе CGI и iSAPI,
объектов ActiveX Data Obpeis fAOOl срганоавпрет11М1ня AdivsX, гплвгон Java, ОЦЮЧ^ЛМЕ JavaSciipt н VB
Senpt. атапжесреиств01паг11сНТМи{ОНТМЦ Приведены е*пыв неоскоя^яыв сваа»шя ой истюль»м»нни
1! созоанииорганов кравла*1д AcSveX{c применением бийлнлек класс» *TL и STLL атак*я апявгов Java.
Иааатеяь Русская Редакция

Рис. 2-16. Просмотр информации о зарегистрированной книге


Если же регистрация отменена, Вы вновь вернетесь к документу HTML, по-
казанному на рис. 2-14.
Теперь обратимся к исходному тексту клиентского сценария, выполняюще-
го все перечисленные выше действия (листинги 2-7 и 2-8).
Листинг 2-7 Вы найдете в файле ch02/ModalRegisterDialog.html на прилагае-
мом к книге компакт-диске.
В листинге 2-7 приведен исходный текст документа HTML с кнопкой запуска
регистрации. Эта кнопка расположена в ячейке таблицы:
<table border="0" width="27X" cellpadding="2">
<trxtd width="22X">
<input type="button" onClick="registerNewBook();"
уа!ие="3арегистрировать новую книгу" name="B1"X/td></tr>
</table>
Щелчок этой кнопки вызывает функцию registerNewBook, выполняющую
отображение модельной диалоговой панели регистрации, прием из полей этой
панели введенной информации и отображение ее на экране:
function registerNewBook()
!
var regFields = new АггауО;
(см. след, стр.)
46 Базы данных в Интернете. Практическое руководство

regFields = window. showModalDialogC'ModalRegisterForm. html",


"Регистрация книги",
"dialogWidth:35em; dialogHeight:22em; resizable=yes");

if(re9Fields["Return"] == "OK")

var sBooklnfo = "Название: " + regFields[ "Title"] +


"\пАвтор: " + regFields["Author"] +
"\пАннотация: " + regFields["Annotation"] +
"\пИздатель: " + regFields["Publistier"];
alert(sBooklnfo);
>

Как работает эта функция?


Прежде всего, она создает массив regFields для сохранения значений из по-
лей формы регистрации:
var regFields = new ArrayO;
В этот массив будет записано значение, возвращенное методом. showModal-
D i a l o g , после того как пользователь завершит работу с модальной диалоговой
панелью:
regFields = window. showModalDialog("ModalRegisterForm. html",
"Регистрация книги",
"dialogWidth:35em; dialogHeight:22em; resizable=yes");
В качестве первого параметра мы передаем методу showModalDialog адрес
URL документа с формой Modal Register Form.ntml. В нашем случае этот доку-
мент лежит в том же каталоге, что и вызывающий его документ ModalRegister-
Dialog.html, поэтому вместо полного или относительного адреса URL мы огра-
ничимся именем файла.
Через второй параметр мы передаем текстовую строку, которая будет исполь-
зована в диалоговой панели как заголовок формы. Заметим, что Вы можете пе-
редать панели не только одно значение, а целый массив. Это полезно, например,
для начальной инициализации полей формы значениями по умолчанию.
И, наконец, третий параметр формы определяет ширину и высоту окна, в
котором будет отображена панель, а также диапазоны изменения размеров это-
го окна пользователем (эта возможность доступна только при использовании
браузера Microsoft Internet Explorer 5.0 или более новой версии).
Один из элементов возвращаемого массива — R e t u r n определяет, добавил ли
пользователь новую книгу в базу данных кнопкой Добавить в базу данных или
отказался от выполнения это операции, щелкнув кнопку Отменить регистрацию.
Следующий фрагмент кода позволяет определить, какой кнопкой была заверше-
на работа с диалоговой панелью:
if(regFields["Heturn"] == "OK")

Если панель закрыта кнопкой Добавить в базу данных, соответствующий


сценарий (размещенный в файле ModalRegisterForm.html) записывает в элемент
массива R e t u r n строку «ОК», а если Отменить регистрацию — строку «Cancel».
Глава 2. Сценарии в страницах HTML и DHTML 47

После успешной регистрации функция registerNewBook получает содержимое


всех полей формы регистрации, обращаясь к соответствующим элементам мас-
сива по именам, и формирует итоговую строку для отображения на экране ме-
тодом alert:
var sBooklnfo = "Название: " + regFields["Title"] +
"ХпАвтор: " + regFields["Author"] +
"\пАннотация: " + regFields["Annotation"] +
"\пИздатель: " + regFields["Publisher"];
alert(sBooklnfo);
Теперь мы рассмотрим исходный текст документа HTML с формой регист-
рации, который находится в листинге 2-8.
Листинг 2-8 хранится в файле ch02/ModalRegisterForm.html на прилагаемом к
книге компакт-диске.
Помимо полей для ввода сведений о книге, в форме регистрации имеется
таблица с кнопками:
<table width="497">
<trxtd width="273" valign="top" align="left">
<input type="button" onClick="addBook();"
уа!ие="Добавить в базу данных" name="B1"x/td>
<td width="216">
<input type="reset" onClick="addCancel();"
уа!ие="0тменить регистрацию" name="B2"x/tdx/tr>
</table>
Когда пользователь щелкает кнопку Добавить в базу данных, управление
передается функции addBook, а когда кнопку Отменить регистрацию — функции
addCancel.
Сначала мы рассмотрим функцию addBook:
fjnction addBook()
{
formFields["Title"]=document.RegFonn("Title").value;
formFields["Author"3=document.RegForm("Author").value;
formFields["Annotation"]=docunient.RegForm("Annotation").value;
formFieldst"Publisher"]=document. RegForm("Publisher").value;
formFields["Return"]="OK";

window.returnValue = formFields;
event.returnValue=false;
window,close();

Ее задача заключается в заполнении массива f o r m F i e l d s , определенном в до-


кументе ModalRegisterForm.html следующим образом:
var formFields = new A r r a y ;
Обращаясь последовательно ко всем полям формы, функция addBook фор-
мирует содержимое массива f o r m F i e l d s :
fоrmFields["Title"]=document.RegForm("~itle").value;
formFields["Author"]:=docunient.RegForffl( "Author"), value;
fоrmFields["Алnotation"]=document.RegForm("Annotation").value;
formFields["Publisher"]=document.RegForm("Publisher").value;
48 Базы данных в Интернете. Практическое руководство

Дополнительно в элемент массива R e t u r n функция addBook записывает стро-


ку «ОК», которая срабатывает при завершении работы диалоговой панели кноп-
кой Добавить в базу данных:
formFields["Return"]="OK";
Дальнейшие действия функции addBook показаны ниже:
window.returnValue = formFields;
event.returnValue=false;
window.closet);
Здесь мы сохраняем заполненный массив в свойстве w i n d o w . r e t u r n V a l u e , за-
писываем значение false в свойство event. r e t u r n V a l u e и закрываем окно диа-
логовой панели, вызывая метод window, close. Изменение значения event. return-
V a l u e позволяет не возвращать объект event (это происходит по умолчанию).
Что же касается функции addCancel, вызываемой щелчком кнопки Отменить
регистрацию, то она записывает в массив formFields только одно значение с
именем R e t u r n и возвращает это значение аналогично функции addBook:
function addCancel()
I
formFields["Return"]="Cancel";
window.returnValue = formFields;
event.returnValue=false;
window. closeO;

Таким образом, с помощью элемента массива formFields с именем R e t u r n мы


передаем код кнопки, использованной для завершения работы диалоговой па-
нели. Остальные элементы — Title, Author, Annotation и Publisher - переда-
ют значения из полей формы, введенные пользователем при регистрации книги.

Немодальные диалоговые панели


Немодальные диалоговые панели представляют собой дополнительные окна
браузера, существующие автономно от других окон.
Клиентский сценарий может открыть новое немодальное окно методом open
и загрузить в него для просмотра любой документ HTML.
Пример вызова метода open показан ниже:
open(sURLAddress [, sWindowName [, sFeatures [, replace]]]]);
Метод возвращает имя нового окна, которое можно использовать для ссыл-
ки на свойства и методы окна, а также на свойства и методы объектов, располо-
женных в этом окне.
Рассмотрим параметры метода open.
Первый обязательный параметр sURLAddress задает адрес URL документа
HTML, предназначенного для загрузки в новое окно.
Второй необязательный параметр sWindowName определяет имя окна для ис-
пользования в параметре target тега <А> или в теге <FORM>. Он может быть
пустой строкой.
Третий, тоже необязательный параметр f e a t u r e s , задает различные парамет-
ры, определяющие внешний вид создаваемого окна браузера. Он представляет
собой текстовую строку, где через запятую перечислены значения отдельных
параметров, например:
Глава 2. Сценарии в страницах HTML и DHTML 49

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.

Просмотр етарницы по сданному адресу TJRL

ес
Brawsertnfo.html

1Й Done

Рис. 2-17. Выбор адреса URL страницы для просмотра в немодальном окне
Здесь в поле Адрес URL Вы можете задать адрес любой страницы, располо-
женной как на локальном диске, так и в сети Интернет, Мы указали имя доку-
мента Browserlnfo.html, исходный текст которого уже рассматривался ранее в на-
шей книге.
После того как пользователь щелкнет кнопку GO!, на экране появится новое
окно, и в него будет загружена указанная Вами страница (рис. 2-18). Конечно,
загрузка выполняется только в том случае, если при вводе адреса URL Вы не до-
пустили ошибку.
50 Базы данных в Интернете. Практическое руководство

3 Информации ° HiitifM браузере Mii.iii.ult Internet Exubiei

Информация о вашем браузере


Название браузера; Micros*!) Internet Explorer
Кодовое имж браузера. MaiOla
4Л (•[ояфайЫе; MSIE5JI; Windows NT;
Версия брау
wwv.uirajneJia.ni; Digfot)
сопфаНЫс; MSIEJO; Windnvs NT;
• ;
i; DigExl)
Типпроцессора 1
«
Язык системы: ru
Язык пользователя: ш
Втораяцифраверсии _
браузера

Рис. 2-18. Страница загружена в немодальное окно


Обращаем Ваше внимание на то, что окно браузера, показанное на этом ри-
сунке, не совсем стандартное. Б нем нет ни меню, ни инструментальных лине-
ек, ни строки состояния. Мы это сделали намеренно, указав соответствующие
параметры при вызове метода w i n d o w , open. При необходимости Вы можете из-
менить вид этого окна по своему усмотрению.
Исходный текст документа HTML с клиентским сценарием, создающим но-
вое окно браузера, представлен в листинге 2-9.
Листинг 2-9 хранится в файле ch02/ShowURL.html на прилагаемом к книге
компакт-диске.
В форме, используемой для ввода адреса L'RL, имеется поле ввода адреса и
кнопка, предназначенная для выполнения перехода по заданному адресу:
<form name="GoForm">
<table border="0" width="665T cellpadding="2">
<trxtd width="2ir>Aflpec URL:</td>
<td width="79X">
<input type="text" name="URLAddress" size="40"x/tdx/tr>
<trxtd width="21!TX/td>
<td width="79X">
<input type="button" onClick="go();" value="GO!" narne="B1"X/tdX/tr>
</table>
</form>
Когда пользователь щелкнет кнопку GO!, управление будет передано функ-
ции до:
function go()

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" пате="Имя_фрейма">

<frame src="Aflpec_URL" пате="Имя_фрейма">


<noframe>
<body>

</body>
</noframe>
</frameset>
</html>
52 Базы данных в Интернете. Практическое руководство

Параметры rows и cols тега <FRAMESET> определяют размеры фреймов и


задаются в виде списка значений, разделенных запятой.
Вы можете определить обработчики событий onload и o n U n l o a d , получающие
управление соответственно при нагрузке содержимого в главное окно фрейма и
при замене текущего документа HTML в этом окне на другой документ. Фак-
тически эти обработчики относятся не к фреймам, а к окну, где эти фреймы ото-
бражаются.
Для тех браузеров, которые не могут работать с фреймами, необходимо под-
готовить документ HTML, расположив его тело между операторами <NOFRAME>
и </NOFRAME>. В этот документ стоит поместить сообщение о том, что для просмотра
данной страницы Web необходимо применять более современный браузер.

Параметры тега <FRAMESET>


Рассмотрим подробнее параметры оператора <FRAMESET>, предназначенного для
определения набора фреймов. Эти параметры описаны в таблице 2-4.
Таблица 2-4. Параметры тега <FRAMESET>
Параметр Описание
COLS Ширина колонки в процентах, пикселах или ее относительный размер
ROWS Высота строки в процентах, пикселах или ее относительный размер
FRAMEBORDER Если значение этого параметра равно 1, фреймы будут ограничены
трехмерной рамкой, ширина которой задается в пикселах, В том слу-
чае когда указано значение 0, рамка не создается
BORDER Используется только браузером Netscape Navigator. Задает толщину
рамки фрейма в пикселах
FRAMESPACING С помощью этого параметра задается дополнительное расстояние
между фреймами в пикселах
Параметры COLS и ROWS нужны в том случае, когда фреймы, определенные в
наборе, располагаются в виде таблицы. Первый из этих параметров указывает
ширину колонки, а второй — высоту строки. Если фреймы располагаются в од-
ном столбце, параметр COLS указывать не надо. Аналогично, если фреймы зани-
мают только одну строку, не нужно укалывать параметр ROWS.
Вы можете задать значения для параметров COLS и ROWS либо в процентном
отношении соответственно к ширине и высоте окна браузера, либо в пикселах.
Если вместо значения указан символ «*», колонка или строка занимают всю ос-
тавшуюся часть окна.
Например, в следующей строке задана высота первого фрейма, равная
90 пикселам, а второй фрейм занимает всю нижнюю часть окна браузера:
<FRAMESET ROWS="90,*">
В следующем примере два фрейма, расположенные рядом, занимают соответ-
ственно 20% и 80% ширины окна браузера.
<FRAMESET COLS="20K,8Qr>

Параметры оператора <FRAME>


Между тегами <FRAMESET> и </FRAMESET> располагаются теги <FRAME>, определя-
ющие параметры отдельных фреймов. Это параметры SRC и NAME. Первый зада-
Глава 2. Сценарии в страницах HTML и DHTML 53

ет адрес URL документа HTML, который будет загружен в данный фрейм, а


второй — имя фрейма, которое можно использовать в клиентском сценарии для
адресации объектов, расположенных во фрейме. Заметим, что адрес URL не
должен содержать анкеры (anchor).
Параметры тега <FRAME> приведены в таблице 2-5.
Таблица 2-5. Параметры тега <FRAME>
Параметр Описание
ALIGN Используется только для «плавающих» фреймов и браузере Microsoft
Internet Explorer. Задает выравнивание фрейма или текста, располо-
женного рядом с фреймом. Этот параметр может принимать следую-
щие значения; LEFT, CENTER, RIGHT, TOP, BOTTOM
HARGINHEIGHT Размер отступа (в пикселах) по вертикали от границ фрейма
HARGINWIDTH Размер отступа (в пикселах) по горизонтали от границ фрейма
FRAMEBORDER Если значение этого параметра равно 1, фреймы ограничены трехмер-
ной рамкой, ширина которой задается в пикселах. В том случае когда
указано значение 0, рамка не создается
NAME Этот параметр задает имя фрейма, которое используется в теге ссыл-
ки <А> для указания, в какой фрейм нужно загрузить новый документ
NORESIZE Если указан этот параметр, пользователь не сможет изменять размеры
фрейма, передвигая его границы мышью
SCROLLING Параметр SCROLLING определяет, нужно ли создавать полосы просмот-
ра для пролистывания содержимого фрейма. Для этого параметра
можно указывать следующие значения:
YES — полосы просмотра создаются всегда;
N0 — полосы просмотра не создаются;
AUTO — полосы просмотра создаются только при необходимости,
когда документ HTML не помещается полностью D окне фрейма
SRC Адрес URL файла с документом HTML, который загружается в окно
фрейма

Взаимодействие между фреймами


Средства клиентских сценариев, составленных на языках JavaScript или
VBScript, позволят Вам наделить фреймы возможностями, недостижимыми при
использовании одного лишь языка разметки гипертекста HTML. Например,
один из фреймов может содержать ссылки на документы, которые при активи-
зации этих ссылок загружаются в окно другого фрейма. Клиентский сценарий
позволит таким образом загружать не один документ, а одновременно несколь-
ко документов в разные фреймы. В следующем разделе мы на конкретном при-
мере расскажем о том, как передавать данные между объектами форм, располо-
женных в разных фреймах.

Десятично-шестнадцатеричный преобразователь
В качестве примера применения клиентских сценариев для передачи данных
между окнами различных фреймов мы рассмотрим десятично-шестнадцатерич-
ный преобразователь. Внешний вид этого преобразователя показан на рис. 2-19.
: Базы данных в Интернете. Практическое оуководство

а Десятично-тестнадцатеричный преобразователь

Десятично-шестнядцатеричный
преобразователь

Hex: J4GQ

"3 \Ay Compute*

Рис. 2-19. Десятично-шестнадцатеричный преобразователь, выполненный с использованием


фреймов
На этом рисунке видно, что окно преобразователя состоит из трех фреймов.
В верхнем размещен заголовок. Клавиатура преобразователя, предназначенная
для ввода десятичных чисел и запуска процесса преобразования, находится в
левом фрейме. Правый фрейм используется для отображения исходного деся-
тичного числа и результата преобразования.
Вы можете вводить исходное число не только с клавиатуры, но и непосред-
ственно в поле Dec, расположенное в правом фрейме. Поле Hex заблокировано
для ввода при помощи обработчика события onFocus.
Исходный текст файла описания фреймов показан в листинге 2-10.
Листинг 2-10 Вы найдете в файле ch02/Converter/index.html на прилагаемом к
книге компакт-диске.
В нем определены фреймы нашего калькулятора:
<frameset ROWS="85,*" frameborder=1>
<frame scrolling="no" name="title" src="title. html" marginheight="1">
<frameset COLS="180,*" FRAMEBORDER=1>
<frame scrolling="auto" name="toc" src="toc.html">
<frame scrolling="auto" name="mainpage" src="main,html">
</frameset>
<noframe>
<body bgcolor="#FFFFFF">
</body>
</noframe>
</frameset>
Наш сценарий будет работать с фреймами toe и #iainpage, хранящимися со-
ответственно в файлах с именами toc.html и main.html. В файле title.html содер-
жится заголовок (листинг 2-11).
Листинг 2-11 Вы найдете в файле ch02/Converter/title.html на прилагаемом к
книге компакт-диске.
Исходный текст документа, содержащего форму с полями Dec и Hex, пред-
ставлен в листинге 2-12.
Листинг 2-12 Вы найдете в файле ch02/Converter/main.html на прилагаемом к
книге компакт-диске.
Глава 2. Сценарии в страницах HTML и DHTML 55

Форма определена следующим образом:


<form name="Sel">
<table>
<trxtdxb>Hex: </b></tdxtd>
<input TYPE="text" NAME="hexValue 1 SI2E="20" onFocus="this.blur();">
</td></tr>
<t rxtdxb>Dec: </bx/tdxtd>
<input TYPE="text" NAME="decValue' SIZE="20">
</td></tr>
</form>
Для поля исходного десятичного числа при помощи параметра NAME мы за-
дали имя decValue. Поле результата называется hexValue. Эти имена использу-
ются сценарием JavaScript для адресации наших полей.
Документ toc.html (листинг 2-13) содержит форму с кнопками и функции
сценария.
Листинг 2-13 Вы найдете в файле ch02/Converter/toc.html на прилагаемом к
книге компакт-диске.
Рассмотрим работу сценария в нашем десятично-шестнадцатеричном преоб-
разователе.
Главная особенность этого приложения — то, что поля для ввода и отобра-
жения преобразуемых чисел располагаются в разных документах HTML, загру-
женных в отдельные фреймы.
Для того чтобы проинициализировать ноля h e x V a l u e и decValue, мы ссыла-
емся на форму, расположенную внутри фрейма mainpage:
parent.mainpage.document.forms[0].hexValue.value = "";
parent.mainpage.document.forms[0].decValue,value = "";
Здесь parent ссылается на объект, который является родительским по отно-
шению к документу HTML, содержащему сценарий. В данном случае это окно,
в котором отображаются все наши фреймы.
Для чтения содержимого поля d e c V a l u e применяется аналогичная техника:
szOld = parent.mainpage.document.forms[0].decValue. value;
Обратите внимание, что для текстового поля h e x V a l u e мы предусмотрели
обработчик событий onFocus. Он получает управление, когда пользователь пе-
редает полю фокус ввода. Задача обработчика заключается в том, чтобы снова
отобрать фокус ввода, предотвратив непосредственное редактирование числа
пользователем.
Теперь о том, как выполняется ввод преобразуемых десятичных чисел.
С каждой из кнопок, связанной с вводом десятичного числа, связан обработ-
чик события o n C l i c k , вызывающий функцию p u t N u m b e r , например;
INPUT TYPE="button" NAME="3" VALUE=" 3 " onClick="putNumber(this,this.form);">
Этой функции передаются два параметра — нажатая кнопка (как объект клас-
са b u t t o n ) и форма, в которой эта кнопка находится:
function putNumber(btn, form)
I
var szOld = "";
var szNew = "";
if(bNewNumber)
(см, след, стр.)
56 Базы данных в Интернете. Практическое руководство

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

dec2fiex(parent. mainpage. document . forms[0]. decValue. value) ;


bNewNumber = true;

Рассмотрим функцию dec2hex, преобразующую десятичное число в шестнад-


цатеричтюе. Результат преобразования эта функция возвращает в виде тексто-
вой строки:
function dec2hex(nDec)

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;

if(Math.floor(nTmp) < 16)


break;

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 .
Базы данных в Интернете. Практическое руководство

Алгоритм преобразования десятичного числа в шестнадцатеричное основан


на делении исходного числа на 16 в цикле. Если целая часть результата деления,
вычисляемая методом Math..floor, оказывается меньше 16, цикл завершается. В
противном случае остаток от деления рассматривается как значение текущего
шестнадцатеричного разряда.
Для того чтобы получить символическое представление шестнадцатерично-
го числа по его значению, мы извлекаем нужный символ из строки szHexTable,
вызывая для этого метод charAt:
szBuf=szHexTable.charAt(nRem);
szResult = szBuf.concat(szResult);
По завершении цикла функция вычисляет старшие разряды результата, а
также корректирует знак этого результата в соответствии с состоянием перемен-
ной bNegative.
Одновременная замена нескольких документов HTML в окнах разных
фреймов
Еще одна задача, для решения которой можно с успехом применять клиентские
сценарии, связана с одновременным отображением нескольких документов
HTML в окнах разных фреймов.
Рассмотрим следующую ситуацию.
Предположим, мы готовим страницу с тремя фреймами. Верхний фрейм со-
держит заголовок, левый — некоторое подобие оглавления и, наконец, правый —
показывает содержимое. Щелкая кнопки, расположенные в левом фрейме, Вы
можете просматривать в правой части окна различные документы HTML
(рис. 2-20).
> Авторский диск Александра Фролова и Григория Фролова - Microsoft Internet. HBO]

Александр Фролов, Григорий Фролов


П и с а т е л и т е х н и ч е с к о й лшературы и

Добро пожаловать!
Вы приобрели первый выпуск автор СЕОГО компакт-
диска тешичесгзс: писателей Александра Фролова
и Григория Фролова.

i 5000 страниц
различной информации, Б том числе:

исходные тексты отоло 20хниг ш серый


"Библиотека системного программиста" и

Д| My Ccinputa

Рис. 2-20. Главная страница с тремя фреймами


С помощью клиентского сценария мы отобразим аннотацию на материал,
представленный в правом фрейме, во фрейме заголовка, расположенного в вер-
хней части окна. Если Вы щелкнете кнопку Добро пожаловать, в заголовке
Глава 2. Сценарии в страницах HTML и DHTML

появится наш логотип. Если же щелкнете кнопку Книги или Статьи, заголовок
будет выглядеть так, как это показано на рис. 2-21 и 2-22 соответственно.
ий диск Александра Фролова и Григория Фролова MICH.soil Inlemel Hill El I

представляем Р4М две серии наших книг, в к эторых рассмотрены вопросы,


связанные с ве пошло в алиям компьютерны!технологий Надеемся что они
Будутвам полезны. Все книги из этю серийвышпиь издатель стае АО 'ДИАЛОГ-
„ МИФИ"

Наши книги

Ф Александр Фрояое, Григорий tyoitoe, 199Я-1996.

Эта серия книг предназначена как для начинающие


польз оьет глей пер с опального компьютера, таг и дш тег.
кто уже имеет опыт работы с компьютерами

Рис. 2-21. В результате щелчка кнопки Книги изменилось содержимое правого и верхнего фрейма

'3 Авторский диск Александра Фролова к Грш ория Фролова Miciotolt Inleinet... HI \

Вы можете прочитать некоторые из наших статей написанные дм различных


пе рио диче спи изданий

Статьи для raieibi КАПИТАЛ


1
© Александр Фролов, Григорий Фролов, 1996-1997
Рубрика "Три килобайта" нос редакцией Дмитрии
Инаашевича Лотияского

нет заменяет прес;у- ыыззиныи г

Важно прдвмано BjiEpaB ...посгщшжауспуг се

SJ

Рис. 2-22. Просмотр информации о статьях


В листинге 2-14 мы показали исходный текст файла описания фреймов
index.html.
Листинг 2-14 хранится в файле ch02/ourCD/index.html на прилагаемом к книге
компакт-диске.
Так же как и в предыдущем примере, здесь определены три фрейма:
<FRAMESET RQWS="85,*" FRAMEBORDER=1>
<FRAME SCROLLING="no" NAHE="title" SRC-"title.html" MARGINHEISHT="1">
<FRAMESET COLS="180,*" FRAMEBORDER=1>
(см. след, стр.)
Базы данных а Интернете. Практическое руководство

<FRAME SCROLLING="auto" NAME="toc" SRCX'toc.html">


<FRAME SCROLLIHQ="auto" NAME="mainpage" SRC="main.html">
</FRAMESET>
<NOFRAME>
<BOOY BGCOLOR="«FFFFFF"></BODY>
</NOFRAME>
</FRAMESET>

Документ HTML, загружаемый в окно фрейма заголовка и отображающий-


ся сразу после загрузки фрейма (а также после того, как пользователь щелкнет
к н о п к у Добро пожаловать), показан в листинге 2-15.
Листинг 2-15 Вы найдете в файле ch02/ourCD/title.html на прилагаемом к книге
компакт-диске.
Файл main.html представлен и листинге 2-16. Он не имеет никаких интерес-
ных особенностей.
Листинг 2-16 Вы найдете в файле ch02/ourCD/main.html на прилагаемом к кни-
ге компакт-диске.
Гораздо важней для нас файл toc.html. В этом файле находятся функции сце-
нария JavaScript и ссылки на другие документы HTML. Этот файл показан в
листинге 2-17.
Листинг 2-17 Вы найдете в файле ch02/ourCD/toc.html на прилагаемом к книге
компакт-диске.
Функция loadPage загружает в фреймы mainpage и title документы HTML,
адреса URL которых передаются ей через параметры:
function loadPagefszNewURL,szTitle)
!
parent.mainpage.window.location.href=szNewURL;
parent.title.window.location.href=szTitle;
!
Чтобы загрузить документ, мы устанавливаем свойство location, href окна
соответствующего фрейма:
parent.mainpage.window.location.href=szNewURL;
parent.title.window.location.href=szTitle;
Для вызова функции loadPage мы используем следующую конструкцию:
<А HREF="javascript:loadPage('main.html', 'title.html');">
<IMG SRC="pic/fcd_62.gif" BORDER=0 А1_Т="Добро пожаловать"х/А>
Здесь в параметре HREF тега ссылки <А> после ключевого слова javascript
мы расположили строку вызова функции. Обратите внимание на применение
одинарных и двойных кавычек. Так как в сценариях JavaScript вложение оди-
наковых кавычек недопустимо, для строк, передаваемых функции в качестве
параметров, мы применяем одинарные кавычки. Значение же параметра HREF
выделено при этом двойными кавычками.

Использование растровых изображений


Растровые изображения в виде файлов формата GIF и JPEG широко применя-
ются в документах HTML, так как они позволяют значительно улучшить внеш-
ний вид страниц серверов Web.
Глава 2. Сденарии в страницах HTML и DHTML 61

К сожалению, возможности HTML не позволяют добиться достаточно слож-


ных видеоэффектов без применения дополнительных средств — апдетов Java,
элементов управления ActiveX и клиентских сценариев. Фактически, кроме ста-
тической графики, Вы можете использовать только анимационные изображения,
построенные с применением многосекционных файлов GIF, и сегментированные
графические изображения, нужные для создания ссылок.
В этом разделе мы рассмотрим некоторые возможности графического офор-
мления страниц серверов Web, которые станут Вам доступны при использова-
нии клиентских сценариев. Снова заметим, что применять описанные здесь при-
емы нужно осмотрительно, так как пользователь может запретить работу сцена-
риев при настройке своего браузера.

Растровое изображение как объект


Для того чтобы встроить растровое изображение в документ HTML, необходи-
мо использовать тег <IMG>. Общий «ид этого тега показан ниже:
<IMG 8РС="Адрес_файла_иэображения"
НАМЕ="Имя_изображения"

ИЮТН="Ширина" НЕШТ="Высота">
Здесь мы указали только три параметра. Полный список параметров тега
<IMG> с кратким их описанием Вы найдете в таблице 2-6.
Таблица 2-6. Параметры тега <IMG>
Параметр Описание
SRC Адрес URL файла с растровым графическим изображением
NAME Имя объекта, соответствующего растровому графическому изображе-
нию. Это имя может быть использовано для ссылки на объект в кли-
ентском сценарии
ALT Текстовая строка, которая отображается в тех случаях, когда браузер
не может показывать графические изображения или когда такая воз-
можность отключена
ALIGN Выравнивание текста относительно графического изображения:
LEFT — 1ЕО левой границе;
RIGHT — по правой границе;
ТОР — по верхней границе;
MIDDLE — по центру изображения;
ВОТТОН — по нижней границе;
ТЕХТТОР — выравнивание по верхней границе относительно самых вы-
соких символов в текстовой строке;
ABSMIDDLE — выравнивание середины текстовой строки относительно
середины изображения;
BASELINE — выравнивание нижней рамки изображения относительно
базовой линии текстовой строки:
ABSBOTTOM — выравнивание нижней границы изображения относитель-
но нижней границы текущей строки
62 Базы данных а Интернете. Практическое р/ководстео

Таблица 2-6. Параметры тега <IMG> (продолжение)


Параметр Описание
HEIGHT Высота изображения в пикселах
WIDTH Ширина изображения в пикселах
BORDER Ширина рамки (в пикселах) вокруг изображения (используется толь-
ко браузером Netscape Navigator)
HSPACE Ширина (в пикселах) свободного пространства, отделяющего изобра-
жение от текста по горизонтали
VSPACE Ширина (в пикселах) свободного пространства, отделяющего изобра-
жение от текста по вертикали
USEMAP Адрес URL файла, содержащего так называемую карту изображения,
которая используется для сегментированной графики
ISMAP Этот параметр указывает, что данное изображение является сегменти-
рованным
Параметры тега <IHG> определяют адрес файла с изображением, выравнива-
ние текста, расположенного возле изображения, и т. д. С помощью параметров
HEIGHT и WIDTH выполняется масштабирование графических изображений. Зна-
чение этих параметров указано в процентах от ширины окна просмотра.
Масштабирование позволяет подготовить графический файл весьма неболь-
шого размера: он занимает значительную площадь в окне браузера, но быстро
передается через Интернет. Вы, однако, не вправе масштабировать сегментиро-
ванные графические и фоновые изображения.
Если в документе HTML размещено несколько растровых изображений, то
Вы можете адресовать соответствующие объекты как элементы массива docu-
m e n t , images. Например, первое изображение адресуется следующим образом;
d o c u m e n t . images[0]. Однако в некоторых случаях удобнее пользоваться имена-
ми изображений, определенными параметром NAME оператора <IMG>.
Объект-изображение имеет свойство src, соответствующее параметру SRC
оператора <IMG>. Адресуясь к этому свойству, Вы можете не только определять
текущий адрес URL изображения, но и задавать новый, Этим мы и воспользу-
емся далее.

Динамическая замена растровых изображений


Одна из наиболее интересных возможностей, доступных при использовании
клиентских сценариев, заключается в динамической замене графических
изображений, указанных в параметре SRC оператора <IMG>.
Например, в следующей строке сценария мы указываем, что изображение с
именем btnl должно иметь адрес «pic/aurap.gif»:
document.btnl.src="pic/aurap.gif"
Здесь мы указали неполный адрес URL, однако, можно указывать и полный
адрес.
Что произойдет при выполнении этой строки сценария?
Область, выделенная в окне браузера для растрового изображения btnl, бу-
дет заполнена изображением pic/aurap.gii. Если до этого там было другое изоб-
ражение, оно будет заменено новым.
Глава 2. Сценарии в страницах HTML и DHTML

Какие возможности предоставляет динамическая замена растровых изобра-


жений?
Во-первых, Вы можете создавать графические ссылки, которые изменяют
свой вид, когда над ними установлен курсор мыши.
Во-вторых„заменой растровых изображений в клиентском сценарии Вы смо-
жете создавать анимационные изображения.
Рассмотрим примеры сценариев, иллюстрирующих эти возможности.
Изменение внешнего вида графических ссылок
В документе HTML, изображенном на рис. 2-23, находятся две кнопки, создан-
ные как растровые графические изображения. Они используются для активиза-
ции ссылок, соответствующих двум другим документам HTML.
E:\tRuttian EdHionMnlernelDB\Sic\ch...H(Sl63

в] «КЛУЁЙВШ : Ej My Computer

Рис. 2-23. Кнопка с надписью «АУРАМЕДИА» изменила свой цвет


Если установить курсор над одной из кнопок, надпись на соответствующей
кнопке изменит свой цвет. Это достигается динамической заменой графическо-
го изображения кнопки при помощи клиентского сценария.
Обратите внимание на исходный текст документа HTML, представленный в
листинге 2-17.
Листинг 2-17 хранится в файле ch02/grbutton/grbutton,htmt на прилагаемом к
книге компакт-диске.
Для создания ссылок мы воспользовались тегом <А>. Он применяется здесь
совместно с тегом <IMG>, поэтому ссылка отображается как графическое изобра-
жение.
Для тега ссылки <А> мы определили обработчики событий o n M o u s e O v e r и
onMouseOut:
onMouseOver="document.btn1.src='pic/aurap.gif""
[>nMouseOut="document. btnl. src='pic/aura, gif"
Когда курсор мыши оказывается над ссылкой (то есть над графическим изоб-
ражением ссылки), управление получает обработчик события onMouseOver. Этот
обработчик загружает изображение pic/aurap.gif, где слово «АУРАМЕДИА» на-
писано черным цветом (для второй кнопки в аналогичной ситуации загружает-
ся изображение pic/softcatp.gif).
После того как пользователь отодвигает курсор мыши от кнопки, в дело
включается обработчик события onMcuseOut. Он восстанавливает исходное изоб-
ражение, указанное в параметре SRC тега <IMG>.
64 . Базы данных в Интернете. Практическое руководство

Создание анимационных изображений


Б следующем примере мы покажем, как, используя динамическую смену растро-
вых графических изображений в клиентском сценарии, можно получить эффект
анимации,
На рис. 2-24 мы показали документ HTML именно с такой анимацией.
•Ежд^ш^шяцаеьудтдицясы!^-^!-*!
На £dt View Favales ~ock d' M

j(6')i;VRufsi9nEdition\j;] f^Go .Links

..
iejDone ^ My Cf«puSa

Рис. 2-24. Анимация с помощью клиентского сценария


В окне браузера последовательно отображаются кадры анимационного изоб-
ражения (рис. 2-25), причем сначала в прямой, а затем — в обратной последо-
вательности. Это выглядит так, как будто слово «Noise» периодически тонет в
цветном «шуме» и затем проявляется вновь,

Рис. 2-25. Изображения отдельных кадров анимационной последовательности


Также обратите внимание вот на что: чтобы получить подобный эффект при
помощи многосекционного файла GIF, размер этого файла надо удвоить. При-
чина заключается в том, что Вам придется включить в файл кадры вначале в
прямой, а затем в обратной последовательности. Клиентский сценарий позволя-
ет более тонко управлять процессом отображения кадров, причем относитель-
но простыми средствами, в результате этого Вам станут доступны достаточно
сложные визуальные эффекты.
Исходный текст документа HTML, в котором имеется сценарий, выполняю-
щий анимацию, показан в листинге 2-18.
Листинг 2-18 Вы найдете в файле ch02/noise/noise.html на прилагаемом к кни-
ге компакт-диске.
В теле документа с помощью тега <IMG> мы разместили первый кадр анима-
ционной последовательности:
<IMG SRC="img01.gif" NAHE="Img">
С помощью параметра NAME мы задали имя Img. Его использует сценарий для
ссылки на объект.
Кроме того, в теле нашего документа находится вызов функции showNextlmage:
<body BGCOLOR=white>
<img SRC="img01.gif" NAME="Img">
<SCRIPT LANGUAGE="JavaScript">
Глава 2. Сценарии в страницах HTML и DHTML 65

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";
Базы данных в Интернете. Практическое руководство

Имя файла, в котором хранится изображение кадра, конструируется из стро-


ки «imgO», номера кадра, и строки «.gif>>.
Последнее, что делает функция showNextlmage, перед тем как возвратить уп-
равление, — вызывает функцию setTimeout:
11
setTimeoutC'showNextlmageO , 100);
Напомним, что функция setTimeout устанавливает таймер. Задержка сраба-
тывания таймера определяется вторым параметром и в нашем случае равна
100 миллисекундам.
Когда таймер включится, начнется выполнение строки сценария, которая
была передана функции setTimeout в качестве первого параметра. Мы вызыва-
ем после окончания задержки функцию s h o w N e x t l m a g e и, таким образом, обес-
печиваем вызов этой функции в бесконечном цикле.
Ожидание загрузки всех изображений
Если Вы собираетесь размещать в своем документе HTML анимационное изоб-
ражение, состоящее из отдельных кадров, которые, в свою очередь, расположе-
ны в отдельных файлах, возникает одна проблема. Она связана с трудностью
определения времени загрузки всех изображений анимационной последователь-
ности через медленные и нестабильные каналы Интернета.
Чтобы анимационное изображение отобразилось без искажений, необходи-
мо вначале дождаться завершения процесса загрузки файлов отдельных кадров
и лишь затем запускать анимацию.
В листинге 2-19 мы привели исходный текст документа HTML со сценари-
ем, который работает подобным образом.
Листинг 2-19 хранится в файле ch02/noise/noise2.html на прилагаемом к книге
компакт-диске.
В теле документа HTML расположен сценарий, вызывающий последователь-
но функции loadAHImages и showNextlmage:
loadAllImages(nNumberOf Images);
showNextlmageO;
Функции loadAHImages в качестве параметра передается общее количество
изображений в анимационной последовательности. В нашем случае оно равно 18.
Задача функции loadAHImages — заполнить массив объектов класса Image:
function loadAHImages{nNumberOf Images)
1

var i;
for(i=0; KnNumberQf Images;

imgArray[i] = new Image();


imgArray[i].src = "imgO" + (i+1) + ".gif";

Этот массив определен в области заголовка нашего документа HTML:


var imgArray = new Аггау(18);
Заполнение массива выполняется в цикле:
Глава 2. Сценарии в страницах HTML и DHTML 67

var i;
for(i=0; KnNumberOf Images;

imgArrayti] = new Image();


imgArray[i]. src = "imgO" + (i+1) + ".gif";
} •
Для каждого элемента массива вначале с помощью ключевого слова new со-
здается объект класса Image, а затем устанавливается значение свойства src этого
объекта. Последняя операция и приводит к инициированию загрузки файла
изображения, выполняемой асинхронно.
После того как массив заполнен, можно вызывать функцию showNextlmage:
var nNumberOflmages = 18;
var i=1;
var bForward=true;
var intgArray = new Array(18);

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;

Наложение фильтра на графическое изображение


Способ подмены графического изображения, описанный ранее в разделе «Изме-
нение внешнего вида графических ссылок», имеет один существенный, на наш
взгляд, недостаток. Для каждого изображения Вам придется подготовить два
файла, первый из которых будет соответствовать невыделенному состоянию
графической ссылки, а второй — выделенному. Если таких ссылок много, это
Базы данных в Интернете, Практическое руководство

увеличит время загрузки страницы, а также объем файлов, хранящихся в ката-


логе Вашего сервера Web.
Между тем средствами DHTML Вы можете подсветить выделенные мышью
графические ссылки без удвоения объема файлов с графикой. Этого достигают,
изменяя одно из свойств стиля ячейки таблицы, содержащей графическую ссылку.
На рис. 2-26 показана основная страница нашего сервера Web, расположен-
ного в Интернете по адресу http://www.frolov.pp.ru.
«Alesiandi Frolov t Giigoiy Ftoluv - Micioinft Internet ExpkHei
£dt

У вас появилась условно-бесп,


утнлат-а вое стан овлення стерт
EraseUndo для NTFS, доступн
загрузхи. Если вы случайно уд;
документ или вам потребовали
предыдущая версия, заходите з—^
сервер, загружайте эту утилит}
Босстановливайте файл. 11уД2_:

ми завершена разработка технологии


:o;i гангвленил файлов из раэрушеннык р,
TTFS. В результате создан набор програмг*й

, .•! • • - > . . , ( - , - . IV I,. -, : И

Рис. 2-26. Подсвечивание элементов графического меню средствами DHTML


В левой части этой страницы расположено достаточно длинное графическое
меню. Оно представляет собой набор зеленых кнопок с текстом белого цвета.
Когда пользователь помещает курсор мыши над такой кнопкой, она изменяет
свой цвет на серый.
Как достигается такой эффект?
В листинге 2-20 мы привели исходный текст файла toc.html, образующего
левый фрейм главной страницы нашего сервера Web.
Листинг 2-20 хранится в файле ch02/ColorFilter/toc.html на прилагаемом к кни-
ге компакт-диске.
Вот фрагмент этого файла:
ctable border="0" width="130" cellspacing="0" cellpadding="Q">
<tr><td width="100K">
<img src="../images/nenult.gif" alt="[Ha нашем сервере]"
border="0" WIDTH="13Q" HEIGHT="16"></td> </tr>
<tr><td width="100r onMouseOver="GON(>" onMouseOut="GOFF()">
<a href="main.html" target-"mainpage">
<img src="../iniages/menu1_welcome.gif" alt="[flo6po пожаловать]" border="0"
name="btn11" WIDTH="130" HEIGHT="16"></a></tdx/tr>

</table>
Глава 2. Сценарии в страницах HTML и DHTML

Обратите внимание, что в теге <td> ячейки таблицы, содержащей графичес-


кую ссылку на файл main.html, мы определили обработчики событий onMouseOver
и onMouseQut (выделено в листинге 2-20 жирным шрифтом). Первое из них воз-
никает, когда курсор мыши располагается над областью окна браузера, занима-
емой этой ячейкой, а второе — когда курсор покидает данную область.
В ответ на эти события вызываются функции GON и GOFF, исходные тексты
которых приведены в листинге 2-20.
Обращаясь к объекту события event. s r c E l e m e n t , эти функции вначале про-
веряют свойство t a g N a m e , которое содержит имя тега объекта, возбудившего со-
бытие. В случае изображения имя тега будет представлять собой текстовую стро-
ку «IMG».
Далее функция определяет фильтр event. srcElement. style, f i l t e r , который
используется при рисовании графического объекта.
Функция 60N устанавливает фильтр «Gray», действие которого заключается
в преобразовании цветного изображения в черно-белое с градациями серого:
function GON()
{
if(event.srcElement.tagName == "IMG")
event.srcElement.style.filter = "Gray";
I
Функция GOFF отменяет действие фильтра, записывая в соответствующее
свойство пустую строку.
function GOFFO
{
if(event.srcElement.tagName == "IMG")
event.srcElement.style.filter = "";
}
Помимо «Gray», Вы можете использовать и другие фильтры, описанные в
документации по DHTML, например фильтр «Invert», инвертирующий цветное
изображение.
Заметим, что описанный в этом разделе способ подсветки работает только в
браузере Microsoft Internet Explorer. Что же касается Netscape Navigator, то он
имеет другую объектную модель. Если загрузить страницу toc.html в этот брау-
зер, подсветка перестанет работать (тем не менее ссылки будут функциониро-
вать нормально).

Применение Cookie в клиентских сценариях


В этом разделе мы расскажем Вам о таком важном элементе, как Cookie. Возмож-
но, Вы слышали о нем и даже знаете, что с его помощью недобросовестные ад-
министраторы серверов Web получают несанкционированный доступ к файлам
пользователей.
Что такое Cookie?
Скажем сразу, что с кулинарным искусством это связано мало, хотя перево-
дится с английского языка как «печенье» или «булочка». Говоря кратко, Cookie
представляет собой свойство документа HTML. Данные Cookie физически хра-
нятся локально на компьютере пользователя, загрузившего этот документ, в виде
70 Базы данных в Интернете. Практическое руководство

специальных файлов. Средствах™ Cookie пользователь может настроить, или


«приготовить», по собственному вкусу документ HTML, если для него предус-
мотрена такая настройка.
Проще всего представить себе Cookie как набор строковых параметров, каж-
дый из которых имеет имя и значение. Клиентский сценарий может создавать
Cookie для документа HTML, определяя в нем произвольное количество пара-
метров и задавая для них произвольные значения. Далее такой набор парамет-
ров становится принадлежностью данного конкретного документа HTML и мо-
жет быть проанализирован, изменен или удален сценарием.
Как мы уже говорили, основное, для чего нужен Cookie, — так это для того,
чтобы предоставить пользователю возможность настроить «под себя» интерфейс
активных документов HTML. Эти параметры могут анализироваться или не
анализироваться сервером Web, но в любом случае они хранятся у пользовате-
ля. И, разумеется, пропадут, если пользователь, скажем, отформатирует свой
жесткий диск. После этого для документа HTML их придется задавать заново.
Конечно, задачу индивидуальной настройки параметров страниц можно ре-
шить и другими способами, например при помощи активных серверных страниц
ASP или расширений сервера Web — типа программ CGI и приложений ISAPI.
Для этого на сервере Web надо установить базу данных, хранящую параметры
для всех зарегистрированных в ней пользователей. В этом случае расширение
сервера Web способно динамически создавать настраиваемые документы HTML,
используя для определения внешнего вида страниц параметры, хранящиеся в
базе данных.
Возлагая задачу хранения параметров отдельных документов HTML на поль-
зовательский браузер, Вы сильно упрощаете задачу организации настройки ди-
алогового интерфейса. А для этого как раз нужны Cookie и клиентские сце-
нарии.
Еще одно практическое применение Cookie — хранение товара, выбранного
посетителем Вашего виртуального электронного магазина. Покупатель отмеча-
ет разный товар на разных страницах сервера. При этом полный заказ вначале
сформирован и сохранен в Cookie, а затем по явному запросу пользователя пе-
реправляется на сервер.
Среди других применений Cookie можно отметить сетевые игры. В Cookie
хранится, например, текущее состояние игры или другие параметры.
При разработке приложений Web помните, что приложения ни при каких
условиях не должны хранить в Cookie такую информацию, как идентификато-
ры пользователей и пароли, а также номера кредитных карточек. Дело в том, что
эта информация может оказаться доступной администраторам серверов Web и
попасть в третьи руки. Обнаружив, что Ваше приложение пытается записывать
в Cookie конфиденциальную информацию, предусмотрительные пользователи,
скорее всего, откажутся от работы с ним.

Выполнение основных операций с Cookie


Рассмотрим основные операции с Cookie, например создание Cookie, получение
и изменение значений параметров Cookie, а также удаление Cookie.
Глава 2. Сценарии в страницах HTML и DHTML 71

Создание 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 Базы данных в Интернете. Практическое руководство

ф Мее — английское трехбуквенное сокращение названия месяца (например,


Jim);
4 ГГ — две последние цифры года;
ф Ч Ч — часы;
ф ММ — минуты;
ф СС ™ секунды.
Например, дату можно указать так:
Mon. 07-Jun-93 14:45:00 GMT
Сделаем небольшое замечание относительно полей domain и path, определя-
ющих условие, при котором выполняется установка Cookie.
Когда браузер загружает документ HTML с сервера Web и среди заголовков
HTTP этого документа есть заголовок «Set-Cookie», он проверяет возможность
установки Cookie. В процессе проверки анализируется адрес URL, откуда был
загружен этот документ, а также содержимое полей domain и path,
Если эти поля не указаны, то по умолчанию считается, что они соответству-
ют адресу URL, по которому находится загруженный документ HTML. В этом
случае выполняется установка Cookie.
Когда же указано поле d o m a i n , установка Cookie выполняется, только если
документ загружен с сервера Web, принадлежащего данному домену.
Средствами параметра p a t h можно установить ограничение на адреса URL в
рамках домена, для которых выполняется установка Cookie. При этом значение
«/» соответствует всем адресам данного домена.
Одновременно сервер Web способен создать несколько параметров Cookie,
включив в заголовок документа HTML несколько заголовков Set-Cookie.
Создание Cookie в клиентском сценарии
Второй способ предполагает использование свойства document, cookie. Это свой-
ство мы упомянули, рассказывая о свойствах и методах объекта document, со-
здаваемого для документа HTML, загруженного в окно браузера.
В общем виде сценарий создает Cookie следующим образом:
document.cookie = "Иня=3начение";
Здесь мы просто записываем в свойство cookie объекта document текстовую
строку, определяющую Cookie.
Для пересчета текущей даты в формат GMT в клиентских сценариях можно
использовать встроенные функции, о чем мы расскажем чуть попозже.
В качестве примера ниже показан исходный текст функции addCookie, кото-
рую мы используем в своих сценариях для создания Cookie.
function addCooki&(szName,szValue,dtDaysExpires)
{
var dtExpires = new Date();
var dtExpiryDate = "";

dtExpires.setTime(dtExpires.getTime() +
dtDaysExpires * 24 * 60 * 60 * 1000);

dtExpiryDate = dtExpires. toGMTStringO;


Глава 2. Сценарии в страницах HTML и DHTML 73

document.cookie =
szName + "=" + szValue + "; expires=" + dtExpiryDate;

Функция addCookie получает три параметра.


Через параметр s z N a m e передается имя параметра, хранящегося в Cookie.
Параметр s z V a l u e определяет значение этого параметра Cookie. Что же касает-
ся последнего параметра — dtDaysExpires, то он задает интервал времени по
отношению к моменту создания Cookie, когда этот Cookie необходимо удалить.
Самое сложное в функции a d d C o o k i e — это определение даты удаления
Cookie и преобразование этой даты в формат GMT. Данная задача решается
следующим образом.
Прежде всего, функция addCookie создает объект класса Date с помощью клю-
чевого слова new:
var dtExpires = new Date();
Записанная таким образом в переменную dtExpires дата соответствует момен-
ту вызова функции addCookie.
Далее с помощью метода getTime функция addCookie определяет текущую дату
в миллисекундах и прибавляет к результату значение параметра dtDaysExpires,
полученное функцией, умноженное на константу (24*60*60*1000):
dtExpires, getTimeO + dtDaysExpires * 24 * 60 * 60 * 1000
Константа — это количество часов в сутках, умноженное на количество ми-
нут в часе, затем на количество секунд в минуте и, наконец, на количество мил-
лисекунд в секунде.
Результат вычислений записывается при помощи метода setTime в перемен-
ную даты dtExpires. Теперь здесь хранится дата автоматического уничтожения
Cookie браузером. Осталось лишь преобразовать эту дату в формат GMT.
Такое преобразование нетрудно сделать с помощью специально предназна-
ченного для этого метода t o G M T S t r i n g , возвращающего текстовую строку в нуж-
ном нам формате:
dtExpiryDate = dtExpires. toGMTStringQ;
Теперь нам остается только сформировать текстовую строку определения
Cookie и записать ее в свойство document, cookie:
document.cookie =
szName + "=" + szValue + "; expires=" + dtExpiryDate;
На этом создание Cookie завершено.
Теперь, когда в Вашем распоряжении есть функция addCookie, создание
Cookie представляет собой очень простую задачу. Например, в следующей строке
мы создаем Cookie с именем Count и значением 0, причем через 10 дней брау-
зер автоматически удалит этот Cookie:
addCookie("Count","О",10);
При необходимости использования других параметров Cookie, например path
или domain, Вы можете немного дополнить текст функции addCookie. С этой за-
дачей Вы легко справитесь самостоятельно.
74 Базы данных в Интернете. Практическое руководство

Получение значения Cookie


Итак, Вы научились создавать Cookie в клиентских сценариях. Теперь попробу-
ем решить другую задачу — определение значения параметров Cookie.
Она сводится к простому сканированию текстовой строки, полученной сле-
дующим образом:
var szCookieString = document. cookie;
В этой строке Вам нужно найти подстроку «Имя=3начение; », а затем извлечь
полученное значение.
Для облегчения этой работы Вы можете воспользоваться функцией f i n d -
Cookie. Исходный текст этой функции приведен ниже:
function findCookie(szName)
{
var i = 0;
var nStartPosition = 0;
var nEndPosition = 0;
var szCookieString = document. cookie;

while(i <= szCookieString. length)


{
nStartPosition = i;
nEndPosition = nStartPosition + szName. length;

if (szCookieString. substring(
nStartPosition, nEndPosition) == szName)

nStartPosition = nEndPosition +• 1;
nEndPosition =
document. cookie, indexOf ("; ", nStartPosition);

if(nEndPosition < nStartPosition)


nEndPosition = document. cookie. length;

return document. cookie. substring(


nStartPosition, nEndPosition);
break;

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

Если же поиск неудачен, функция f i n d C o o k i e возвращает пустую строку.


Какие возможности предоставляет функция findCookie?
Во-первых, она позволит Вам проверить, установлен ли для данного докумен-
та Cookie с заданным именем:
Lf(findCookie("Visit") == "")
I
// cookie с именем Visit установлен

}
else
{
// cookie с именем Visit не установлен

Для того чтобы записать в текстовую переменную значение параметра Cookie


с заданным именем, Вы должны сделать следующее:
var szVisitValue = findCookie("Visit");
Как видите, пользоваться функцией findCookie достаточно просто.
Изменение значения параметра Cookie
Для изменения значения параметра Cookie с заданным именем Вы можете про-
сто вызвать функцию addCookie еще раз:
addCookieC'Count", "О", 10);
// Значение параметра Count равно О

addCookieC'Count ","5", 10);


// Значение параметра Count равно 5
Здесь мы вначале установили для параметра Count значение 0, а затем изме-
нили это значение на 5.
Удаление Cookie
Самый простой способ удалить Cookie — установить для него такое время авто-
магического удаления, которое уже прошло. Для этого нужно получить текущую
дату, уменьшить ее, например, на одну микросекунду, а затем изменить значение
document.cookie.
Все это делает функция removeCookie:
"unction removeCookie(szName)
ji
var dtExpires = new DateQ;
dtExpires.setTime(dtExpires.getTime() - 1);

var szValue = findCookie(szName);

document.cookie = szName + "=" + szValue +


"; expires=" + dtExpires.toGMTStringC);
}
В последней строке этой функции мы указали такое значение параметра
expires, при котором Cookie будет немедленно удален браузером.
Базы данных в Интернете, Практическое руководство

Ограничения на использование Cookie


На использование Cookie накладываются определенные ограничения, перечис-
ленные ниже:
ф всего можно создать не более 300 Cookie;
f необходимо, чтобы размер каждого Cookie не превышал 4 кб;
ф для каждого домена может быть создано не более 20 Cookie
- Если указанные ограничения будут нарушены, браузер может удалить самые
старые Cookie или обрезать значения параметров Cookie.

Примеры использования Cookie


В этом разделе на примере конкретных клиентских сценариев мы покажем, как
можно использовать Cookie для решения различных практических задач.
Фиксация повторных посещений страницы
В нашем первом примере документ HTML содержит форму с двумя кнопками
(рис. 2-27).
Cookies demo . Miciosoll Internal Explorer НЙ1131


JJ
в * * E . U R u s s i a n EdiMonMnteri

Добро пожаловать!

Рис. 2-27. Кнопки для перехода к динамически создаваемому документу HTML


и для удаления Cookie
Если нажать на кнопку GO!, сценарий создаст новый документ HTML. Вне-
шний вид его зависит от того, сколько раз пользователь нажимал на эту кнопку.
3 E:\!Hussian EdilionMnlemelD8\Src\ch02..

Добро пожаловать!

Рис. 2-28. Такой документ HTML создается динамически при первом посещении
Глава 2. Сценарии в страницах HTML и DHTML ,,

Кнопка Удалить cookie предназначена для удаления Cookie, созданного в


нашем документе HTML.
Когда Вы нажимаете на кнопку GO! в первый раз, Cookie еще не создан. При.
этом генерируется документ HTML, изображенный на рис. 2-28.
Во второе и последующие посещения внешний вид документа изменяется
(рис. 2-29).
mE:\'RusiianEdilionMrternetDB\Sic\ch02\,aga.- ВНЕ]

File £Й1 Y** F^orites Toats Д

Рады видеть Вас СНОВА!


Visit=Alexandr_Frolov; Count=l;
mfo=ip&194 135240 ll&visits&lSud&922710,

Рис. 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 для хранения произвольного тек-
ста, набранного пользователем в многострочном окне редактирования (рис. 2-30).
При первой загрузке документа HTML с записной книжкой окно редактиро-
вания остается пустым. Вы можете набрать здесь любой текст и записать его в
Cookie, щелкнув кнопку Сохранить текст. Если теперь Вы закроете документ
HTML и откроете его ниовь, набранный Вами ранее текст появится в окне ре-
дактирования.
Для того чтобы удалить текст и Cookie, достаточно щелкнуть кнопку Удалить
текст.
Глава 2. Сценарии в страницах HTML и DHTML

3 Cookie Notepad - Мн msufl Internet Explmei


File Ей View Fflvaites loots He

Cookie Notepad
помощью cookie вы
но зге те хранить у
пользователя ела
локальные настройки

зхранить текст Удалить текст

Рис. 2-30. Документ с записной книжкой Cookie Notepad


Исходный текст документа HTML с записной книжкой Cookie Notepad по-
казан в листинге 2-22.
Листинг 2-22 Вы найдете в файле ch02/N otebook.html на прилагаемом к книге
компакт-диске.
Функция addCookie, использованная нами здесь, имеет одну особенность:
перед записью текстовой строки в параметр Cookie она выполняет ее кодиров-
ку о формате URL, вызывая для этого функцию escape:
document. cookie = szName + "=" + escape(szValue) + "; expires=" + dtExpiryDate;
Это необходимо по той причине, что введенный в окне редактирования текст
может содержать пробелы и любые другие символы.
Аналогичные изменения мы внесли и в функцию f indCookie. Она возвраща-
ет значение, перекодированное в обычный текст функцией unescape, выполня-
ющей действия противоположные действиям ф у н к ц и и escape:
szTemp = document. cookie. substring(
nStartPosition, nEndPosition);
return unescape(szTemp);
Когда пользователь нажимает кнопку Сохранить текст, вызывается функция
btnClick:
function btnClickO

addCt>okie( "MyText" , TestForm. Comment . value, 10);

Эта функция просто записывает в параметр Cookie с именем MyText тексто-


вую строку, извлеченную из многострочного поля редактирования TestForm. Com-
ment. value.
При удалении текста кнопкой Удалить текст вызывается функция remove-
Cookie, удаляющая параметр Cookie с именем MyText, а также записывается пу-
стая строка в окно многострочного редактирования:
<.INPUT TYPE="button" VALUE="Clear text"
onClick = "removeCookief 'MyText' ); TestForm. Comment. value=' ' " >
80 Базы данных в Интернете. Практическое руководство

В самом конце тела документа HTML находится небольшой фрагмент сце-


нария, запускающийся сразу после загрузки этого документа:
var szMyText="";
szMyText = findCookie("MyText");

if(szMyText != "")

TestForm. Comment. value = szMyText;

Этот фрагмент пытается получить значение параметра Cookie с именем


MyText. Если это ему удается и функция f indCookie возвращает непустую стро-
ку, полученная строка записывается в окно многострочного поля редактирова-
ния TestForm. Comment. value. В результате сразу после загрузки документа это
окно оказывается заполненным текстом, сохраненным в Cookie.
Вы можете посмотреть системный файл, хранящий данные Cookie. Для это-
го откройте каталог Temporary Internet Files, расположенный в системном ката-
логе Microsoft Windows 95 или в каталоге %$\т81етгоог%\РгогПе8\<имя_поль-
зователя>\СооЫе$ операционной системы Microsoft Windows NT (здесь %sys-
temroot% — системный каталог Microsoft Windows NT).
Вы можете скопировать файлы Cookie, например, на рабочий стол и открыть
для просмотра любым текстовым редактором. То, что Вы увидите, показано ниже
(содержимое файлов Cookie меняется в зависимости от операционной системы
и версии браузера):
Visit
Alexandr_Frolov
""local""/E:\! Russian Edition\InternetDB\Src\ch02\
0
321921100B
29310152
404023264
29308141

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

В самом начале файла видно имя Visit параметра Cookie. На следующих


строках находятся значения других параметров, отделенные друг от друга стро-
кой —local—/E:\!Russian Edition\InternetDB\Src\ch02\. Для записи символов
кириллицы здесь использована кодировка URL.
Если удалить этот файл и затем открыть документ HTML, многострочное
окно редактирования будет пустым. Удалив этот файл, мы удалим и расположен-
ный в нем Cookie.
Настройка параметров документа HTML
Следующий пример демонстрирует, как можно использовать Cookie- для на-
стройки пользователем параметров документа HTML.
На рис. 2-3} показан документ HTML с двумя кнопками и переключателем,
имеющим зависимую фиксацию.
•д| Customize youi page - MiciUiOll tntemel Exploi

> - Li** **

I
Посетите вашу персональную
страницу
Переход we страниц.

Настройка параметров персональной страницы

Цвет фона:

& Белый
<~ Желтый
f~ Зеленый
<"* Малнновыа

.._ Параметры по i|Mpnuamit€_

Рис. 2-31. Главный документ HTML, позволяющий выполнить настройку


Если Вы нажмете верхнюю кнопку, то в окне браузера появится документ
HTML, созданный динамически клиентским сценарием. В первый раз этот до-
кумент будет таким, как показано на рис. 2-32.
Переключатели позволят Вам выбрать один из четырех цветов фона докумен-
та. Это значение будет храниться в Cookie. Для того чтобы вернуться к цвету,
принятому по умолчанию в документе HTML, показанному на рис. 2-31, нуж-
но нажать кнопку Параметры по умолчанию.
При последующих посещениях внешний вид документа изменится (рис. 2-33).
Базы данных в Интернете, Практическое руководство

1 E-VIRussian Edil.on\BSP 34U)isk lor BSP34\CHAPTERAC .. ШШ


file £fS View Favorites Xools $*

«•]«'. EVRussian Edilion\B5P34\Disk for BSP34\С J*j c^So . Lmks

}
Добро пожаловать!
Бы можете настроить "цвет фона этой страницы при
помощи переключателе?:, расположенных на главной
странице.

Настройки будут использованы, когда вы посетите эту


странйцув следующий раз

у Щ My CompMer

Рис. 2-32. Внешний вид документа при первом посещении


31 \ 4 t u s •ЛИЫР 34\Disk loi В5Р34Ч:НАР

Рады видеть вас снова!


Вы посетили эту страницу в 3 раз.

Рис. 2.33. Внешний вид документа при третьем посещении


Его фон будет иметь такой цвет, какой был задан при помощи переключателей,
Исходный текст документа HTML показан в листинге 2-23.
Листинг 2-23 Вы найдете в файле ch02/CustomPage.html на прилагаемом к
книге компакт-диске.
Помимо функций addCookie, f indCookie и removeCookie, предназначенных для
работы с Cookie, в сценарии определена переменная szColor, предназначенная
для хранения выбранного пользователем цвета фона, а также функции btnGo,
c h k R a d i o и setDefault.
Функция btnGo прежде всего проверяет наличие параметра Cookie с именем
Count:
function btnGo()

if(findCookie("Count") == "")
{
addCookie("Count", "0",10);
addCookie("bgColor",szColor,10);

document. writeC"<Н2>Добро пожаловать!</Н2>");


document.write( '<Р>Вы можете настроить цвет фона этой"};
document.writef страницы при помощи переключателей,");
document. writeC расположенных на главной странице,");
document. writeC"<Р>Настройки будут использованы, когда Вы"):
document.write( посетите эту страницу в следующий раз.");
Глава 2. Сценарии в страницах HTML и DHTML

else

var szCnt = findCookieC'Count");


var 1=0;

if(szCnt != "")

i = szCnt;
i++;
szCnt = i. toStringO;

addCookie( "Count", szCnt, 10);

document. write("<H2>Paflbi видеть Вас снова! </Н2>");


document. write("Bbi посетили эту страницу в " + szCnt.boldO + " раз.");
document. bgColor=findCookie("bgColor");
>
;
Если такого параметра нет, сценарий считает, что пользователь просматри-
вает этот документ в первый раз. В этом случае функция btnGo добавляет два
параметра Cookie с именами Count и bgColor:
addCookie( "Count", "О", 10);
addCookie( "bgColor", szColor, 10);
Первый из них предназначен для хранения счетчика посещений, а второй —
для хранения цвета фона. Далее функция b t n G o формирует документ IITML с
приглашением для пользователя, просматривающего документ в первый раз, и
завершает свою работу,
В том случае, когда сразу после вызова функция btnGo нашла параметр Cookie
с именем Count и п о л у ч и л а его значение, это значение увеличивается па е д и м и -
iy и записывается обратно. Кроме того, оно отображается в тексте документа:
document. write("<H2>Paflbi видеть Вас снова! </Н2>");
document. write( "Вы посетили эту страницу в " +
szCnt. bold()+" раз. ");
Затем функция b t n G o устанавливает цвет фона сформированного документа
HTML в соответствии со значением, извлеченным из параметра Cookie с име-
нем bgColor:
document. bgColor=findCookie(" bgColor");
Функция chkRadio вызывается, когда пользователь отмечает один из переклю-
чателей выбора цвета:
function chkRadio(form, value)
{
szColor = value;
addCookie("bgColor",szColor, 10);
-
Эта функция записывает значение выбранного цвета в переменную szColor,
;i также в параметр Cookie с именем bgColor.
Базы данных в Интернете. Практическое оуководство

И, наконец, функция s e t D e f a u l t удаляет параметр Cookie с именем C o u n t и


устанавливает в переменной szColor белый цвет фона, принятый по умолчанию:
function setDefault(form)
<
1
removeCookie('Count };
szColor="White";

Эта функция вызывается, когда пользователь нажимает кнопку с надписью


Параметры по умолчанию:
<INPUT TYPE="reset" УА1_иЕ="Параметры по умолчанию"
onClick="setDefault(this,form);">
Обратите внимание, что данная кнопка имеет тип reset. Когда пользователь
ее щелкает, в форме отмечается переключатель, задающий белый цвет фона. Это
происходит потому, что указанный переключатель определен с параметром
CHECKED, а кнопка типа reset устанавливает все органы управления формы в
исходное состояние.

Настройка браузера для работы с Cookie


Хотя Cookie не представляют особой опасности для пользователей, не каждый
захочет, чтобы хранящаяся в них информация передавалась на сервер Web. В
сочетании с другими полями заголовка HTTP данные Cookie позволяют соби-
рать определенную статистику о пользователях, а это нравится далеко не всем.
Настраивая браузер соответствующим образом, пользователи могут ограни-
чить или вовсе отключить взаимодействие с Cookie. Вы должны это учитывать,
если при создании активных документов HTML надеетесь на использование
Cookie.
Браузер Microsoft Internet Explorer версии 5.0 допускает установку различ-
ных режимов работы с Cookie. Эти режимы указываются в панели Security
Settings на вкладке Security блокнота Internet Options (рис. 2-34), доступного
из меню Tools.

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

Рис. 2-34. Установка режимов работы с Cookie


Глава 2. Сценарии в страницах HTML и DHTML

По умолчанию активизирован переключатель Enable, разрешающий приме-


нение Cookie. Переключатель Disable полностью запрещает использование
Cookie. Если Вы пометите переключатель Prompt, при попытке установить
Cookie на экране появится предупреждающее сообщение.

Отладка клиентских сценариев


Если Вы установили Microsoft InterDev 6.0, отладка клиентских сценариев не
вызовет у Вас особых сложностей.
Запустите браузер Microsoft Internet Explorer и выберите из меню View стро-
ку Script Debugger. Далее в меню второго уровня укажите строку Break at Next
Statement. Теперь браузер подготовлен к запуску сценария под отладкой.
Загрузите в окно браузера документ HTML с отлаживаемым сценарием. Это
можно сделать перетаскивая мышью пиктограмму документа в окно браузера
или средствами меню File браузера.
Как только будет выполнена любая строка клиентского сценария, запустит-
ся отладчик сценариев, встроенный в Microsoft InterDev 6.0. Вы увидите диало-
говую панель Microsoft Development Environment, в которой Вам придется под-
твердить необходимость запуска сценария под отладкой, щелкнув кнопку Yes.
Когда Вы увидите вторую панель с запросом на открытие файла проекта, отка-
житесь от этой операции, щелкнув кнопку No.
В результате отобразится окно отладчика Microsoft InterDev 6.0, в которое
будет загружен исходный текст документа со сценарием. Строка, на которой
произошел останов, выделена цветом. Используя кнопки инструментальной
панели, Вы сможете поэтапно выполнять сценарий, устанавливать дополнитель-
ные точки останова, просматривать содержимое локальных переменных и т. д.
Подробное описание приемов работы с отладчиком выходит за рамки нашей
книги. При необходимости воспользуйтесь электронной справочной системой,
встроенной в Microsoft InterDev 6.0.
Г Л А В А 3

Применение технологии ASP

Существует два способа создания активного сервера Web на базе Microsoft


Internet Information Server — с применением программ расширения CGI или
1SAPI либо с помощью активных серверных страниц ASP.
Первый способ предполагает составление достаточно сложных программ на
языке C++ или использование интерпретируемых языков наподобие Perl. Про-
граммы расширения сервера Web представляют собой обычные приложения,
исполняемые на компьютере сервера Web. Они могут обращаться к базам дан-
ных таким же образом, как и обычные автономные приложения.
Главный недостаток этого способа заключается, на наш взгляд, в сложности
разработки и отладки программ расширений сервера Web, что значительно за-
медляет процесс разработки больших и сложных проектов. Особенно тяжело при
этом сопровождать проекты, которые постоянно изменяются.
Что же касается активных серверных страниц ASP, то они значительно про-
ще, хотя позволяют решать те же самые задачи, что и программные расширения
сервера Web. Страницы ASP представляют собой текстовые файлы с конструк-
циями языка HTML и сценариями, составленными на таких языках программи-
рования, как JScript и VB Script. Фактически, если Вы уже владеете этими язы-
ками программирования, изучение технологии ASP не отнимет у Вас много вре-
мени.
Возможность составления серверных приложений с применением высоко-
уровневых языков сценариев и технологии компонентов СОМ значительно уп-
рощает создание сложных приложений Web. При этом от разработчика не тре-
буется глубокого понимания тонкостей программирования на уровне интерфей-
са операционной системы или прикладных интерфейсов базы данных. Все раз-
работки ведутся в терминах интерфейсов и свойств высокоуровневых компонен-
тов СОМ.
Отметим, что сценарии, расположенные в страницах ASP, могут быть сервер-
ными и клиентскими. Серверные сценарии выделяются специальным образом
и исполняются на сервере, в то время как клиентские сценарии работают на
компьютере пользователя под управлением браузера, например Microsoft Inter-
net Explorer.
Результатом работы серверных сценариев ASP является динамически фор-
мируемый текст документа HTML, отсылаемый пользователю. Этот текст за-
гружается в окно браузера. Если сформированный документ HTML содержит
клиентские сценарии, они будут выполнены браузером.
Глава 3. Применение технологии ASP

Основы 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

Virtual Diieetciy | Document] ОггесвдSeeo*s»| HTTPHeaders


When competing to this resource. iVe corient streuliicomeiTorn.
$:$e'''"f"-J"'' •='"-С:Г| "т- cf^1-1^
17

f Д ^aie tocaied on arw*he! cCTigJet


С A redipffilion la^ URL

tooalPate p Mr-etPub\wwwpoot\Book

Access Ре1ггв4;юп! Content Centre1


17 Reed fi" Log access
Г~ Write Г Difectoriiljrow^ng all
Applicati°n Sellings -
' Name jBookSlray

; SlattJngPwt (DelaultWebStei/BoohS

*"* Egectte ( scrip!|

H*

Рис. 3-1. Настройка доступа к виртуальному каталогу BookStory


Переключатель Run in separate memory space (isolated process) позволяет
запускать нее приложения, относящиеся к данному каталогу, в отдельном адрес-
ном пространстве. Несмотря на некоторое снижение быстродействия сервера
Web, .чанный режим будет полезен при отладке новых приложений. Если уста-
новить флажок у этого переключателя, сбои в работе данного приложения не
скажутся на общей работоспособности сервера Web.
Панель, показанная на рис. 3-1, позволяет настроить и другие важные пара-
метры приложения. Например, можно запретить кэширование, отключить меха-
низм сеансов, разрешить отладку серверных и клиентских сценариев и т. д. Пол-
ную информацию о настройке параметров виртуальных каталогов Вы найдете
в электронной справочной системе или в документации к Microsoft Internet
Information Server.
Базы данных в Интернете. Практическое руководство

Вернемся к нашему приложению.


Подготовив каталог на сервере Web, скопируйте в него файл HelloASP.asp,
содержимое котор'ого Вы найдете в листинге 3-1.
Листинг 3-1 хранится в файле сНОЗ/HelloASP.asp на прилагаемом к книге ком-
пакт-диске.
Рассмотрим подробно содержимое этого файла.
Первая строка содержит оператор ©LANGUAGE, который указывает, что это сер-
верный сценарий на языке JScript:
<%9 LANGUAGE = "JScript" %>
Этот оператор, как и все конструкции серверного сценария в документах ASP,
должен быть выделен при помощи последовательностей символов «<%» (откры-
вающая последовательность) и <$%>» (закрывающая последовательность).
Далее в файле определяется переменная sHelloMsg. Ей присваивается значе-
ние текстовой строки «Hello, ASP W o r l d ! » :
<%
var sHelloMsg = "Hello, ASP World!";
%>
Конструкция <3!=sHelloMsgX> вставляет в код динамически создаваемого до-
кумента HTML содержимое переменной s H e l l o M s g :
<HTML>
<BODY>
<H1><X=sHelloMsgX></H1>
</BODY>
</HTML>
В результате в окно браузера (рис. 3-2) будет загружен документ HTML,
исходный текст которого приведен ниже:
<HTML>
<BODY>
<H1>Hello, ASP World!</H1>
</BODY>
</HTHL>
Вы можете увидеть этот текст, выбрав строку Source из меню View в глав-
ном окне браузера. Обратите внимание, что в документе нет последовательно-
стей символов «<Х» и «Х>». Все конструкции, содержащие такие последователь-
ности, интерпретируются сервером. Они никогда не попадают в создаваемый
документ HTML.
Э Ml|i //IrolovZSIi.'Hoc.kSlory/HelloAsp asp - Mkiosolt In .. HI

Hello, ASP World!


, Loca! intpan?

Рис. 3-2. Просмотр страницы HelloASP.asp в окне браузера


Глава 3. Применение технологии ASP

Пример с циклом
Теперь мы рассмотрим более сложный пример страницы 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.

Таблица квадратов целых


чисел от 0 до 5

и] Dorm

Рис. 3-3. Просмотр страницы ForASP.asp в окне браузера

Обработка формы
Наше следующее приложение Web запрашивает у пользователя регистрацион-
ную информацию, а затем показывает ее пользователю в окне браузера. Цель
данного примера — показать, как с помощью серверных сценариев в страницах
ASP можно получить доступ к данным, введенным при помощи форм.
На рис. 3-4 изображена форма запроса идентификатора и пароля пользова-
теля. Она предназначена для подключения к нашему приложению Web. Пред-
полагается, что пользователь введет данные регистрации и щелкнет кнопку
Подключиться. Кнопка Отменить предназначена для удаления содержимого из
полей формы.
5 bNi. //riolov235/B<iokSlor)iSLoginFaim.hlml Microsoft I" -

Регистрация пользователя
Идентификатор; jfrolov

Пароль: р* —""-I

Jisj Locaf ml ran

Рис. 3-4. Форма для запроса идентификатора и пароля пользователя


Глава 3. Применение технологии ASP

В нашем простом примере никакого подключения не происходит. Вместо


этого сервер Web создает и отправляет пользователю страницу HTML с регис-
трационной информацией (рис. 3-5).
-3hH|> //frDlo¥235/BootSloiv/LoflinCheck.asp?Logm=fiolo..

Г?
Данные регистрации
Идентификатор frolov

Пароль MyPrivateP as sword 1424

;bj Local intranet

Рис. 3-5. Просмотр регистрационной информации


Форма находится в обычном документе HTML (листинг 3-3) и не имеет ни-
каких особенностей. В частности, здесь мы не применяем клиентских сценари-
ев для проверки правильности введенных данных.
Листинг 3-3 Вы найдете в файле ch03/LoginFortn.html на прилагаемом к книге
компакт-диске.
В качестве значения параметра ACTION тега <FORM> мы указали адрес URL
страницы ASP (страница находится в том же каталоге, что и файл Login-
Form.html, поэтому адрес URL задан просто именем файла):
<form action="LoginCheck.asp" method="POST">
Для передачи данных формы мы использовали метод POST, Можно приме-
нить и метод GET, однако, как Вы знаете, ему свойственны ограничения по дли-
не передаваемых данных.
Теперь наша задача — составить серверный сценарий, принимающий данные
из полей формы и отправляющий их назад пользователю в виде динамически
созданного документа HTML. Исходный текст такого сценария приведен в ли-
стинге 3-4.
Листинг 3-4 Вы найдете в файле ch03/ Log in Check, asp на прилагаемом к книге
компакт-диске.
Для извлечения значений параметров, переданных странице ASP (в нашем
случае это значения нолей формы) мы применили объект R e q u e s t . Этот объект
предназначен специально для получения информации от пользователя:
<» LANGUAGE = "JScript" *>
-I : Ч,

<HEAD>
<%
var sLogin=flequest.Form("Login");
war sPassword=Request.Form("Password");
!!>
</HEAD>
Мето/i. Form объекта R e q u e s t позволяет извлечь содержимое полей формы,
переданные методом POST. Наш сценарий сохраняет извлеченные таким образом
значения идентификатора пользователя и его пароль и затем записывает их в
92 Базы данных в Интернете. Практическое эуководство

переменные с именами sLogin и sPassword соответственно. Содержимое этих


переменных отображается в ячейках таблицы:
<TABLE>

<TRXTD>naponb</TDXTDxbxX=sPasswordJEx/bx/TDX/TR>
</TABLE>

Комбинирование клиентского и серверного сценариев


Если Вы разрабатываете административную часть приложения Web (например,
программу, предназначенную для просмотра и редактирования списка клиентов
электронного магазина) наряду с серверными сценариями можно широко ис-
пользовать клиентские. В нашем следующем примере мы так и поступаем.
В листинге 3-5 представлены исходные тексты модифицированной формы
регистрации, изображенной ранее на рис. 3-4.
Листинг 3-5 Вы найдете в файле ch03/LoginFormEx.html на прилагаемом к кни-
ге компакт-диске.
Теперь мы добавили к этой форме функцию checkForm, составленную на языке
сценариев JavaScript. Эта функция получает управление по завершении ввода
данных в поля формы, чтобы проверить, что пользователь указал идентифика-
тор и пароль:
<script LANGUAGE="JavaScript">
<!-
function checkFormO
I
if(document. RegForm. Login. value == "" ||
document. RegForm. Password. value == "")
{
а!ег±("Укажите идентификатор и пароль"); return;
'
document. RegForm. submit О;
!
// ->
</script>
Обращаем Ban Ее внимание на изменения, которые претерпел исходный текст
формы по сравнению с листингом 3-3.
Во-первых, кнопка Подключиться имеет тип b u t t o n , а не s u b m i t . Теперь она
применяется не для непосредственного отправления данных формы на сервер,
а только для вызова функции c h e c k F o r m , определенной как обработчик события
onClick:
<form action="LoginCheckEx. asp" method="PQST" name="RegForm">
<table border="0" widtn="40iT>
<tr>
<td width="29X">MfleHTH(|)HKaTop:</td> <td width="71X">
<input type-"text" name="Login" size="20"x/tdx/tr>
<tr>
<td width="29K">napoflb:</td><td width="7n">
<input type=" password" name="Password" size="20"x/td> </tr>
<trxtd width="29X">
Глава 3. Применение технологии ASP 93

<input type="button" onClick="checkForm(); "


уа!ие="Подключиться" name="Ok"X/tdxtd width="71iT>
<input type="reset" уа!ие="0тменить" name="Cancel"x/td>
</tr>
</table>
</form>
Во-вторых, к тегу <FORM> мы добавили параметр NAME, определяющий имя
формы как RegForm. Это имя потребуется нам для ссылки на поля формы в кли-
ентском сценарии.
И наконец, в-третьих, теперь к параметре ACTION указано имя другой страни-
цы ASP.
Функция c h e c k F o r m выполняет простейшую проверку — указал ли пользова-
тель идентификатор и пароль. Если что-то пропущено, на экране пользователя
появляется сообщение об ошибке. Если же пользователь все данные ввел пра-
вильно, функция checkForm отправляет их на сервер при помощи метода docu-
ment. RegForm. submit.
Эта незамысловатая операция уменьшает количество обращений на сервер
Web, отсекая пользователей, не указавших свои пароли или имена.
Страница ASP, получаст управление, когда пользователь щелкнет кнопку
Подключиться. Она содержит как серверный, так и клиентский сценарий (лис-
тинг 3-6).
Листинг 3-6 Вы найдете в файле ch03/LoginCheckEx.asp на прилагаемом к
книге компакт-диске.
Задача серверного сценария, строки которого выделены символами «<Х» и
<<%>», — извлечение идентификатора пользователя и пароля из данных формы с
целью отображения в окне браузера (как и в предыдущем примере):
<%
var sLogin=Request. Form( "Login");
var sPassword=Request. Form("Password");
!(>
Кроме того, серверный сценарий используется для формирования таблицы:
<ТАВ1_Е>
<ТР><ТО>Идентификатор:</ТО><ТОХЬ><Х-81од1п!(></Ь></ТВ><ЛР>
<TRxTD>naponb:</TDXTDXbxX=sPassword*x/bx/TDX/TR>
<TR><TD COLSPAN="2"><input type="button" onClick="goBack(); "
value=" Назад" name="back"x/TD></TR>
</TABLE>
Что же касается клиентского сценария, то он состоит из одной функции
goBack:
-cscript LANGUAGE^" JavaScript ">
<! -
function goBack()
\
window. location, href =
"LoginFormEx.htrnl?"+"FRCE="+Math. random().toString();

<:/script>
Базы данных в Интернете. Практическое руководство

Эта функция получает управление после щелчка пользователем кнопки На-


зад (рис. 3-6). Она загружает в окно браузера документ с формой регистрации,
расположенной в файле LoginFormEx.html.
Для отключения механизма кэширования мы применяем здесь технику, опи-
санную ранее и основанную на добавлении к имени документа HTML фиктив-
ного параметра в виде произвольной текстовой строки. В этом случае каждый
раз при загрузке этого документа браузер будет считать его новым и не станет
восстанавливать из кэша предыдущую копию.
flh»lp:j'/liolov?3&/BookSlorr,'LpgmCheckEn.aip - Miciotoft I... HI"! El I

Данные регистрации
Идентификатор Frolov
Пароль My Password

Рис. 3-6. Кнопка Назад предназначена для возврата к форме регистрации

Приложения ASP и сеансы


До сих нор при создании простейших приложений Web средствами ASP мы
допускали некоторые упрощения. Теперь настало время определить такое поня-
тие, как приложение ASP.
Фактически это подмножество приложений Web, созданных с использовани-
ем технологии активных серверных страниц ASP и сервера Microsoft Internet
Information Server.
Приложение ASP должно располагаться в отдельном виртуальном каталоге
сервера Web или занимать корневой каталог виртуального сервера Web. В этом
каталоге необходимо разместить;
ф документ HTML или страницу ASP, которая загружается по умолчанию, ког-
да посетитель указывает адрес, URL данного каталога в своем браузере;
ф специальный текстовый файл с именем global.asa, определяющий обработчи-
ки событий, возникающих в ппоцессе работы приложения ASP;
ф каталоги, содержащие обычные документы HTML, активные страницы ASP,
графические изображения, элементы управления ActiveX, аплеты Java и дру-
гие объекты, имеющие отношение к приложению.
Что касается документа HTML или страницы ASP, загружаемой по умолча-
нию при доступе к каталогу приложения ASP, то его имя определяется при на-
стройке параметров сервера Web. По умолчанию сервер Microsoft Internet Infor-
mation Server использует для этой цели имя default.htm, однако Вы можете из-
менить его на более привычное index.html или любое другое.
Файл global.asa, строго говоря, создавать не обязательно. Однако он необхо-
дим, сои Вы собираетесь отслеживать события, имеющие отношение ко всему
приложению, или сохранять информацию в глобальных переменных приложения.
Глава 3. Применение технологии ASP

Когда пользователь загружает страницу приложения ASP в свой браузер,


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

Файл 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 Базы данных в Интернете. Практическое руководство

ненадежным каналам Интернета могут возникать значительные задержки. Если


задержка превышает 20 минут (время можно задать самостоятельно), сервер
Web считает, что сеанс завершен. При этом возникает событие Session_OnEnd.
Таблица 3-1. События, обрабатываемые и файле global.asa
Событие Когда возникает
Session_OnStart Пользователь впервые загружает любую страницу приложения
Session_OnEnd Пользователь завершает работу с приложением, или истекает
период ожидания
Application_OnStart Любой пользователь загружает впервые любую страницу при-
ложения
Application_OnEnd Сервер Web завершает свою работу
В общем случае логика обработки событий в файле global.asa во многом оп-
ределяется логикой работы Вашего приложения. Вполне вероятно. Вам не по-
требуется отслеживать моменты начала и завершения сеансов пользователей,
хотя лучше это все-таки делать. Например, если сеанс завершился по истечении
периода ожидания, приложение может отключить пользователя и запросить
пароль при попытке повторного подключения.
Событие Application_QnStart удобно применять для инициализации и загруз-
ки элементов управления ActiveX, которые в результате выполнения такой опе-
рации станут доступными всем пользователям.
Заметим, что файл global.asa может быть составлен и с: применением языка
сценариев JavaScript. В этом случае в нем должны быть определены функции с
именами S e s s i o n _ O n S t a r t , S e s s i o n _ O n E n d , A p p l i c a t i o n _ O n S t a r t и Applica-
t i o n _ O n E n b . В документации и примерах, подготовленных Microsoft, почти по-
всеместно демонстрируется использование в этом файле сценариев VB Script.
Мы же, напротив, будем работать со сценариями JavaScript и JScript.
Переменные приложений ASP
Приложения Web, составленные с применением ASP, могут создавать глобаль-
ные переменные, доступные всем пользователям данного приложения, а также
переменные сеанса, доступные только отдельным клиентам во время их текуще-
го сеанса с сервером.
Глобальные переменные приложения создаются и инициализируются обра-
ботчиками событий в файле global.asa. Их применяют для хранения значений,
необходимых всем пользователям приложения. Так, для электронного магази-
на это может быть, например, текущий курс обмена валют, или значения скидок,
действующих для пользователей определенной категории, или ссылки на какие-
либо глобальные объекты.
Переменные сеанса более предназначены для хранения информации, связан-
ной с пользователями, — идентификатора текущего пользователя и его пароля,
кода места проживания, нужного для вычисления стоимости доставки товара,
прав пользователя или другой аналогичной информации.
Переменные сеанса также годятся для передачи данных между различными
страницами ASP. Например, на одной странице Вы задаете параметры запросов,
а на другой конструируете форму запроса в зависимости от этих параметров. Так
Глава 3. Применение технологии ASP 97

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

Практическое применение ASP


На примере нескольких несложных приложений ASP мы покажем технику ис-
пользования важнейших средств ASP, таких, как переменные сеанса и приложе-
ния. Мы также приведем исходные тексты примера приложения ASP, исполь-
зующего файл global.asa для хранения количества текущих сеансов и текстовой
строки в глобальной переменной приложения.

Создание мастера средствами ASP


Вначале мы покажем Вам, как пользоваться переменными сеанса, попутно ре-
шая практическую задачу — создание мастера платежей средствами ASP.
Наш мастер платежей, как и мастера в обычных приложениях Windows, со-
стоит из набора страниц, связанных между собой кнопками просмотра. Пользо-
ватель может перемещаться по ним в обоих направлениях, устанавливая на стра-
ницах различные параметры. Нам мастер поможет решить простейшую задачу —
выбрать валюту и тип кредитной карточки для оплаты товара. Применяя исполь-
зованные нами решения, Вы сможете создать мастера для выполнения любых
других операций, например: для выбора способа доставки товара, для создания
системы поиска товара в каталогах и г. д.
Первая страница мастера платежей показана на рис. 3-7.
Она играет роль главной страницы мастера платежей и представляет собой
обычный документ HTML с формой, содержащей одну-единственную кнопку
Вперед. Если Вы щелкнете эту кнопку, на экране появится следующее окно
мастера, показанное на рис. 3-8.
В нем заданы три переключателя, позволяющие выбрать для оплаты товара
рубли, доллары или немецкие марки (Вы, конечно, можете добавить евро и
любые другие валюты).
Если Вы щелкнете кнопку Назад или Отменить, на экране появится главная
страница мастера (рис. 3-7), если же кнопку Вперед — перейдете к третьей стра-
нице нашего мастера платежей.
Третья страница показана на рис. 3-9. Список и кнопки на ней аналогичны
элементам на второй странице.
Базы данных в Интернете. Практическое руководство

Шр://fiolоv235/ВооkStory/MasteiO.html - Micioiolt Internet


£йе Tools

= ,

Мастер платежей
Мастер платежей поможет вам выбрать способ, которым
вы будете расплачиваться за приобретенный товар
Следуйте указаниям, появляющимся на экране

Рис. 3-7. Первое окно мастера платежей


3ht^V/liolov235/BookSloir/Mastei1.asp?BZ=?C2:£EF*E5:№0..

Выберите валюту
Бы можете оплатить покупку в рублях, в долларах или Е
немецких маркая
Выберите подходящую валюту н щелкните кнопку Вперед.
Для отмены покупки щелкните кнопку Отменить

<~ рубли
- 1* ; доллары
Г немецкие марки
•—1
»|

«Назад 0-цеш-пъ

•i

Рис. 3-8. Окно для выбора валюты


Если, находясь на третьей странице мастера. Вы щелкнете кнопку Назад, то
снова окажетесь на предыдущей странице, предназначенной для выбора валюты.
Заметим, что после возврата к предыдущей странице указанный Вами ранее
тип валюты будет сохранен. Этот эффект достигается с помощью записи кода
выбранной валюты в переменную сеанса.
В следующей странице мастера наше приложение Web показывает, какая
валюта и какая кредитная карточка использованы при покупки (рис. 3-10). Здесь
Вы также можете разместить кнопку для перехода на страницу, завершающую
процесс покупки снятием денег со счета посетителя Вашего магазина. Нажимая
кнопку, пользователь отправляется на страницу сервера Web процессингового
центра, где и происходит автоматическое снятие денег (с одобрения пользовате-
ля и при его участии). Там он, в частности, вводит номер кредитной карточки.
Глава 3. Применение технологии ASP

•a'i4p-,'/lrolov235/BooMit<Hy,'MasteiZasp - Microsoft Internet E..

Выберите способ платежа


1
Выбрана валюта доллары

Теперь вам н у ж н о выбрать тип кредитной карточки и


щелкнуть по кнопке Вперед Для отмены покупки
воспользуйтесь кнопкой Отменить

«Назад

Рис. 3-9. Выбор типа кредитной карточки


Кнопка Назад, как и раньше, позволяет вернуться к предыдущей странице
мастера. Если Вы щелкнете ее, то увидите, что в появившемся списке типов
кредитных карточек выделен ранее выбранный Вами тип. Как Вы уже, наверное,
догадались, указанный посетителем магазина тип кредитной карточки мы хра-
ним в переменной сеанса.
Кнопка Отменить отсылает посетителя на главную страницу мастера платежей,
htlp;//lioK>v235/B(,okStoiy/MaB|ei3.aip - Micmsofl Internet Е... ШЩЕ

Подтверждение платежа
Поздарвляем с покупкой!

Для оплаты вы использовали Доллары


Покупка была сделана с помощью кредвтвой карточки
American Express.

Отменить
:

Рис. 3-10. Последнее окно мастера платежей


Теперь, когда мы рассказали Вам о назначении и работе страниц мастера,
перейдем к описанию исходных текстов этих страниц.
Для обеспечения максимальной совместимости мастера платежей с обозре-
вателями различных типов мы решили совсем не использовать в нем клиентские
сденарии, ограничившись только средствами серверных сценариев ASP. В ре-
зультате отсылаемые посетителям страницы представляют собой простые до-
100 Базы данных о Интернете. Практическое руководство

кументы HTML, не «отягощенные* удобными и мощными, но сомнительными


с точки зрения совместимости конструкциями на языке сценариев JavaScript.
Исходный текст главной страницы мастера показан в листинге 3-7.
Листинг 3-7 Вы найдете в файле ch03/Master0.html на прилагаемом к книге
компакт-диске.
Она содержит обычную форму с кнопкой типа submit:
<form action="Master1.asp">
<table border="Q" width="300" cellpadding="2">
<tr><td width="100">
<input type="submit" уа!ие="Вперед &gt;&gt;" name="B2">
</td></tr>
</table>
</form>
Если щелкнуть ее мышью, в окно браузера загрузится документ из файла
Masterl.asp, указанный в параметре ACTION тега <FORM>.
Текст файла Masterl.asp представлен в листинге 3-8. Помимо таблиц и форм,
он содержит серверный сценарии, составленный на языке JScript.
Листинг 3-8 Вы найдете в файле ch03/Masterl.asp на прилагаемом к книге
компакт-диске.
Первая строка файла Masterl.asp сообщает серверу Web о том, что данный
файл содержит серверный сценарий, составленный на языке JScript. С этой кон-
струкцией Вы уже встречались. А вот вторая строка для Вас новая:
OSResponse. Expires=0!E>
Записывая нулевое значение в свойство Expires объекта R e s p o n s e , мы тем
самым сообщаем обозревателю, что полученная им страница уже устарела. В
результате он не будет ее кэшировать. Это еще один способ отменить кэширо-
вание страниц, мешающее обновлению динамически создаваемых документов
HTML в окне браузера. К сожалению, данное решение работает не всегда. Бо-
лее надежный способ предполагает добавление к адресу URL страницы при ее
загрузке фиктивного параметра в виде случайного числа.
Следующее действие серверного сценария — считывание содержимого пере-
менной сеанса с именем sessCurrency. Эта операция выполняется при помощи
объекта Session:
var sCurrency = Session("sessCurrency">;
Когда посетитель попадает на эту страницу в первый раз, такой переменной
сеанса еще не существует. Поэтому в локальную переменную s C u r r e n c y занесе-
но значение n u l l . Когда же пользователь вернется на вторую страницу мастера
с последующих страниц, переменная сеанса sessCurrency уже содержит код вы-
бранной валюты.
Чтобы при повторных отображениях был помечен переключатель, соответ-
ствующий выбранной ранее валюте, необходимо при генерации кода HTML
указать в соответствующем теге <INPUT> параметр checked. Для этого мы предус-
мотрели в сценарии ASP три локальные переменные с именами sChkRub, sChk-
Dollar и sCfikOM. С помощью конструкции if-else в одну из этих переменных
записывается текстовая строка «checked». В дальнейшем мы используем выра-
жения вида <u=sChkflubu>, чтобы определить параметры тегов <INPUT>. Это иозво-
Глава 3. Применение технологии ASP 101

ляет нам отметить только один из трех переключателей, осуществляющих вы-


бор валюты:
а
var sCurrency = Session("sessCurrency");
var sChkRub="";
var sChkDollar="";
var sChkDM="";

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 Базы данных в Интернете. Практическое эуководство

Рассмотрим исходный текст второй страницы мастера платежей, приведен-


ный в листинге 3-9.
Листинг 3-9 Вы найдете в файле ch03/Master2.asp на прилагаемом к книге
компакт-диске.
В этом листинге после обычных строк, определяющих язык серверного сце-
нария и отменяющих кэширование, находятся строки, извлекающие код выбран-
ной валюты. Он извлекается из параметров, переданных вместе с данными фор-
мы предыдущей страницей мастера, и записывается к переменную sCurrency:
<%
Response.Expires=Q;
var sCurrency;
if(Request.Form("Currency").Count != 0)

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)

Когда пользователь «листает» страницы мастера в прямом направлении, этот


набор содержит один такой элемент е номером 1. Мы извлекаем его при помо-
щи объекта Request и сохраняем в сеансовой переменной sessCurrency:
sCurrency=Request.Form("Currency")(1);
Session("sessCurr&ncy")=sCurrency;
Когда же пользователь «листает» страницы мастера в обратном направлении,
сеансовая переменная sessCurrency уже создана и проинициализирована. Паш
сценарий извлекает из нее код выбранной валюты обычным образом и записы-
вает в переменную s C u r r e n c y :
sCurrency=Session("sessCurrency");
На следующем этапе в зависимости от содержимого переменной s C u r r e n c y
сценарий записывает ту или иную текстовую строку с названием валюты в пе-
ременную sCurrencyName, которую мы используем для подготовки текста сооб-
щения с названием валюты (в виде конструкции <3S=sCurrencyName!(>).

ifCsCurrency == "rub")
Глава 3. Применение технологии ASP 103

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 Базы данных в Интернете. Практическое руководство

Если щелкнуть эту кнопку, в окно обозревателя начнет загружаться следую-


щая страница мастера платежей из файла MasterS.asp.
Что же касается кнопок Назад и Отменить, то соответствующие им формы
ссылаются на страницы Masterl.asp и Master0.html соответственно:
<form id="formO" name="formO" action="Master1.asp">
ctable border="0" width="100" cellpadding="2">
<trxtd width="1" align="left">
<input type="submit" value="« Назад" name="B1"X/td></tr>
</table>
</form> </td><td>
<form id="form2" name="form2" action="MasterO.html">
<table border="0" width="102" cellpadcjing="2">
<trxtd width="1" ali8n="left">
<input type="submit" value-''Отменить" name="B3"x/td></tr>
</table>
</form>
Последняя страница нашего мастера платежей (листинг 3-10) предназначе-
на для отображения выбранной валюты и типа кредитной карточки, использо-
ванной для покупки.
Листинг 3-10 Вы найдете в файле ch03/Master3.asp на прилагаемом к книге
компакт-диске.
Однако помимо этого, данная страница выполняет и еще одну важную фун-
кцию она сохраняет в переменной сеанса sessWirecard тип выбранной кредит-
ной карточки:
<%
Respoose.Expires=0;
var sWirecard;
if(Request.Form("PayMethod").Count != 0)
{
sWi reca rd=Request.Fo rm("PayMe thod"}(1);
Session("sessWirecard")=sWirecard;

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

j] Dare ~;kj iced Mianet

Рис. 3-11. Четыре сеанса находятся в активном состоянии


В качестве главной страницы приложения выступает файл default.asp (лис-
тинг 3-12), расположенный в корне виртуального каталога SessCounter. Если
загрузить данный файл в окно браузера, Вы увидите заголовок «Session counter»
и количество активных сеансов. На рис. 3-11 показан внешний вид окна после
активизации четырех сеансов одновременно.
10б Базы данных в Интернете. Практическое руководство

Чтобы активизировать несколько сеансов, Вы можете открыть эту страницу


в нескольких окнах браузера на одном компьютере или на нескольких компью-
терах в своей локальной сети.
Помимо файла default.asp, в корне виртуального каталога SessCounter нахо-
дится файл global.asa, исходный текст которого показан в листинге 3-11.
Листинг 3-11 Вы найдете в файле ch03/Counter/global.asa на прилагаемом к
книге компакт-диске.
Первая строка определяет язык сценария, а также указывает, что сценарий
должен исполняться на сервере:
<SCRIPT LANGUAGE=JSCRIPT RUNAT=Server>
Обратите внимание, что он написан на языке сценариев JScript, а не VB Script,
что характерно для всех примеров приложений Microsoft и других книг, посвя-
щенных ASP.
Функция Application_OnStart получает управление, когда любой пользова-
тель в первый раз загружает главную страницу нашего приложения:
function Application_OnStart()
{
Application("sess_counter") = 0;
Application("appl_name") = "Session counter";

В се задачу входит создание и инициализация переменных приложения


sess_counter и appl_name. Первая из этих переменных содержит счетчик актив-
ных сеансов, а вторая — текстовую строку «Session counter», которая будет ото-
бражаться в окне браузера.
Когда очередной пользователь загружает главную страницу нашего приложе-
ния в окно браузера, управление передается ф у н к ц и и Session_OnStart:
function Session_OnStart()

Application("sess_counter") = Application("sess_counter") + 1;

Эта функция увеличивает значение переменной приложения sess_counter на


единицу.
При отключении пользователя от приложения функция Session_OnEnd умень-
шает значение счетчика sess_counter на единицу:
function Session_OnEnd()

Application("sess_counter") = Application("sess_counter") - 1;

Что же касается функции Application_OnEnd, то она определена как заглуш-


ка и не выполняет никаких действий:
function Applicat:Lon_OnEnd()
{

Файл default.asp (листинг 3-12) отображает в окне браузера количество ак-


тивных сеансов.
Глава 3. Применение технологии ASP 107

Листинг 3-12 Вы найдете в файле ch03/Counter/default.asp на прилагаемом к


книге компакт-диске.
<%
var sHelloMsg = Application("appl_name");
К>
<HTML>
<BODY>
<H1><X=sHelloMsgJ[></Hl>
Количество активных сеансов.извлекается из переменной приложения
sess_counter:
<р>Количество активных сеансов: <^=Application("sess_counter")X>

Использование Cookie в сценариях ASP


В предыдущей главе мы рассказывали о Cookie и привели примеры клиентских
сценариев, работающих с Cookie. Серверные сценарии также способны выпол-
нять операции с Cookie, причем использование их вместо клиентских сценари-
ев благоприятно скажется на совместимости Вашего приложения с браузерами
различных типов.
Для установки значения Cookie Вам придется воспользоваться свойством
Cookies объекта Response:
Response.Cookiesf'bgColoг"}="White";
Эту строку надо расположить на странице ASP перед тегом <HTML>.
Если Вам нужно получить значение Cookie, это легко сделать в серверном
сценарии, обратившись к свойству Cookies объекта Request, например:
var sCount;
sCount=Request.Cookies("Count");
Устанавливая свойство R e s p o n s e . C o o k i e s . E x p i r e s , можно задавать дату, по
истечении которой Cookie будет удален.
Ниже мы привели фрагмент кода, удаляющий Cookie с именем bgColor:
var d, s;
d = new Date();
cl. setTime(d. getTime( )-1);
s = d.toLocaleStringO;
Response.Cookies("bgColor").Expires = s;
Здесь мы вначале получаем текущую дату, создавая объект класса Date. За-
тем при помощи методов getTime и setTime мы уменьшаем текущую дату на одну
микросекунду и преобразуем ее методом toLocaleSt r i n g в формат, соответству-
ющий текущим локальным параметрам. Только после такого преобразования
можно записывать строку даты в свойство Expires объекта Cookies.
В качестве примера обращения к Cookie из серверных сценариев рассмотрим
приложение ASP, сохраняющее параметры фона на странице. Во второй главе мы
уже создавали аналогичное приложение с применением клиентских сценариев.
На первой странице нашего приложения (рис. 3-12) пользователь может
выбрать цвет фона или задать цвет фона по умолчанию.
Если щелкнуть кнопку Параметры по умолчанию, на экране появится стра-
ница со счетчиком количества посещений и счетчиком пользователей, работа-
ющих в настоящий момент времени с Вашим приложением (рис. 3-13).
I Базы данных в Интернете. Практическое руководство

'3 Customize your page - Miuusull Internet Explor


Go Urks

Посетите вашу персональную


страницу
- ^ Перебоя на страницу

Настройка параметров персональной страницы

Цвет фона:

Р Белый
Г Желтый
Г Зеленый
С Малиновый

Рис. 3-12. Выбор цвета фона страницы

I
Добро пожаловать!
Бы были здесь 0 раз
Цвет фона страницы: White
Всего активных посетителей: 4
I
"!i, Lota1, intranet

Рис. 3-13. Первое посещение страницы пользователем


Ihttp /;fiolov235;Deinc.Cciokiej'inai<i.aSp?RESET-... Bfi-IO

!
Добро пожаловать СНОВА!
Вы были здесь 1 раз
Цвет фона страницы. Yellow
Всего аетквнкх посетителей: 4

И 0°гё ~ " !|в| Local pilra'nel

Рис. 3-14. Второе посещение страницы


Глава 3. Применение технологии ASP 109

При втором и последующих посещениях внешний вид этой страницы изме-


нится (рис. 3-14).
Для подсчета активных пользователей, работающих с нашим приложением,
мы определили в файле global.asa (листинг 3-13) обработчики событий Appli-
cation_OnStart, Session_OnStart и Session_OnEnd.
Листинг 3-13 Вы найдете в файле ch03/DemoCookie/global.asa на прилагаемом
к книге компакт-диске.
Первый из них создает и инициализирует переменную приложения с именем
3ess_counter:
f u n c t i o n Application_QnStart()

Application("sess_counter") = 0;
V

Обработчики событий Session_OnStart и Session_OnEnd соответственно уве-


личивают и уменьшают значение этого счетчика на единицу,
function Session_OnStart()

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 Базы данных в Интернете. Практическое руководство

Обратите внимание, как мы перелаем этот параметр из формы в документ


ASP, записывая его имя после знака вопроса. Форма с кнопкой Параметры по
умолчанию тоже вызывает страницу main.asp, но задает при этом значение па-
раметра RESET, равное 1:
<FORM ACTION="main.asp?RESET=1" METHOD="POST">
<INPUT TYPE="submit" УАЦ)Е="Параметры по умолчанию" id=reset1 name=reset1>
Если Вы хотите передать таким образом несколько параметров, их нужно
отделить друг от друга символами «&>>, например:
ACTION="main.asp?RESET=0&MODE=Force&Age=18"
Так, Вы можете добавить параметр в виде символьного представления слу-
чайного числа. Это позволит эффективно отключить кэширование загружаемой
страницы.
Параметр RESET анализируется серверным сценарием ASP, расположенным на
странице main.asp (листинг 3-15).
Листинг 3-15 Вы найдете в файле ch03/DemoCookie/main.asp на прилагаемом
к книге компакт-диске.
Чтобы получить значение параметра RESET, наш сценарий обращается к объек-
ту Request:
var nCount=0;
if(Request("RESET")(1) == 1)

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;

В том случае, когда значение этого параметра равно 1, выполняется запись в


Cookie значений по умолчанию:
Response.Cookies("bgColor"}="White";
Response.Cookies("Count")="0";
Здесь Cookie с именем b g C o l o r хранит цвет фона страницы, а с именем
C o u n t — количество посещений этой страницы пользователем.
Если же страница вызывается с параметром RESET, равным 0, наш серверный
сценарий определяет, какой цвет выбрал пользователь, запрашивая содержимое
Глава 3. Применение технологии ASP 111

поля с именем Color. Полученное значение затем сохраняется в Cookie с именем


bgColor:
var sColor=Request.Form("Color")(1);
Response. Cookies("bgColor")=sColor;
Далее серверный сценарий запрашивает содержимое Cookie с именем Count
(здесь хранится количество посещений страницы пользователем), увеличивает
его на единицу и записывает обратно.
Заголовок страницы при первом и последующих посещениях страницы бу-
дет разным. Это достигается размещением на странице следующего фрагмента
кода:
<%
if(nCount == 0)
Ц>
<:Н2>Добро пожаловать! </Н2>
<*} else {*>
<Н2>Добро пожаловать СНОВА! </Н2>

Анализируя содержимое переменной n C o u n t , в которой записано текущее


значение Cookie с именем C o u n t , серверный сценарий помещает на страницу
одни из двух заголовков.
Далее в документе располагаются фрагменты серверного сценария, вставля-
ющие в него количество посещений страницы, название цвета фона страницы и
количество пользователей, работающих в данное время с приложением:
<%
if(nCount == 0)
{*>
<Н2>Добро пожаловать! </Н2>
<%} else U>
<Н2>Добро пожаловать СНОВА! </Н2>

<р>Вы были. здесь <X=Request.Cookies("Count")X> раз<Ьг>


Цвет фона страницы: <X=Request.Cookies("bgColor")XXbr>
Всего активных посетителей: <X=Application("sess_counter")K>

Регистрация пользователей
При создании Интернет-магазина или другого коммерческого приложения для
Интернета Вам придется решать проблему аутентификации пользователей: как
правило, для того чтобы работать со страницами Вашего приложения, пользо-
вателям придется ввести свой идентификатор и пароль.
В этом разделе мы приведем исходные тексты фрагмента приложения, реша-
ющего задачу аутентификации пользователей. По результатам аутентификации
им либо разрешается, либо запрещается работать с приложением.
Для ввода идентификатора (имени) и пароля мы используем панель, пока-
занную на рис. 3-15.
Базы данных в Интернете. Практическое эуководство

3 Подключение пользователя - Miciosufl Internet ExpUrei ИН Еа

Добро пожаловать!
Емя jad-nJr

гт
Пароль I1^,

Рис. 3-15. Панель для ввода идентификатора и пароля


Предполагается, что данная страница ASP располагается в файле с именем
default.asp — он загружается автоматически, когда пользователь просматривает
содержимое главного каталога приложения. Напомним, что при настройке сер-
вера IIS Вы можете задать имя документа, загружающегося по умолчанию в окно
браузера при этих условиях.
В случае успешной аутентификации в окне браузера появится основная стра-
ница приложения, расположенная в файле main.asp (рис. 3-16). В ней отобража-
ется идентификатор и пароль подключившегося пользователя.
a b!lp://iralov235yi_oginTest/main.asp - Miciosolt Inteir)

Добро пожаловать!
Вы успешно подключились к нашему приложению

Ваш идентификатор admin


Ваш пароль 1 root

Рис. 3-16. Пользователь успешно подключился к нашему приложению

a http:/^holov235/Log>nTest/enter asp - Microsoft Inl

Доступ запрещен

Рис. 3-17. Результат ввода неправильного идентификатора или пароля


Глава 3, Применение технологии ASP 113

Если же идентификатор или пароль введен неправильно, пользователь ока-


жется на странице enter.asp, показанной на рис. 3-17. На этой странице имеется
ссылка, с помощью которой можно вернуться к панели ввода идентификатора
и пароля.
Работа данной системы аутентификации основана на применении перемен-
ных сеанса, заданных отдельно для каждого пользователя приложения ASP.
Исходный текст страницы с формой аутентификации показан в листинге 3-16.
Листинг 3-16 Вы найдете в файле ch03/LoginTest/default.asp на прилагаемом к
книге компакт-диске.
Когда пользователь вводит идентификатор и пароль, эти данные передают-
ся на страницу enter.asp в соответствии с параметрами тега <FORM>:
<FORM ACTION="enter.asp" METHOD="post" TARGET="_top">
<Н1>Добро пожаловать!</Н1>
<TABLE BORDEfl=0 CELLPADDING=5 CELLSPACING=0>
<TRXTD>HMfl</TDXTD>
<INPUT SIZE=10 TYPE="EDIT" NAME="USR"X/TDX/TR>
<ТВХТО>Пароль</ТОХТОхпоЬг>
<INPUT SIZE=10 TYPE="password" NAME="PWD"XINPUT TYPE="submit"
VALUE="Bxofl"X/nobrX/TDX/TR>
</TABLE>
</FORM>
Исходный текст страницы enter.asp Вы найдете в листинге 3-17.
Листинг 3-17 хранится в файле ch03/LoginTest/enter.asp на прилагаемом к кни-
ге компакт-диске.
Сценарий на странице enter.asp получает введенные пользователем иденти-
фикатор и пароль, сохраняя их в переменных sUser и sPassword соответственно.
var sUser=Request("USR")(1);
vaг sPassword=Request("PWD")(1);
Далее выполняется процесс аутентификации. В нашем демонстрационном
примере мы просто сравниваем имя пользователя со строкой «admin», а пароль —
со строкой «root»:
if(sUser == "admin" && sPassword == "root")
I
Session("0k")="0k";
Session("UserID")=sUser;
Session("UserPwd")=sPassword;
Response.Redirect("main. asp");

else
I
Session("0k")="";
В следующей главе мы покажем, как выполнять аутентификацию с примене-
нием базы данных пользователей.
Если идентификатор и пароль введены правильно, наш сценарий создает
переменную сеанса с именем Ok и записывает к нее текстовую строку «Ok». До-
полнительно мы сохраняем идентификатор и пароль пользователя в переменных
сеанса с именами U s e r l D и UserPwd.
114 Базы данных в Интернете. Практическое оуководство

На следующем этапе мы отправляем успешно подключившегося пользовате-


ля на главную страницу приложения main.asp, вызывая для этого метод Redirect
объекта Response:
Response.Redirect("main.asp");
В том случае, когда пользователю не удалось подключиться к приложению,
серверный сценарий на странице entor.asp формирует документ HTML со ссыл-
кой в виде строки Доступ запрещен. Эта ссылка позволит пользователю перейти
на страницу default.asp, где он сможет повторить попытку:
<HTML>
<BODY>
<CENTER><Hl><a href=default.asp>flocTyn запрещен</ах/Н1х/СЕМТЕВ>
</BODY>
</HTML>
Когда Вы изучите наш сценарий подключения, у Вас может возникнуть один
вопрос: а что помешает злонамеренному пользователю, знающему адрес URL
главной страницы нашего приложения main.asp, попасть туда непосредственно,
минуя страницу аутентификации default.asp?
Хороший вопрос. Однако мы предусмотрели такой вариант.
Обратите внимание на исходный текст главной страницы приложения
main.asp, представленный в листинге 3-18.
Листинг 3-18 Вы найдете в файле ch03/LoginTest/main.asp на прилагаемом к
книге компакт-диске.
В отличие от документов HTML, в страницы ASP можно вставлять другие
страницы при помощи оператора tfinclude. Главная страница нашего приложе-
ния main.asp, как и все остальные страницы (кроме default.asp и enter.asp), вклю-
чают в себя документы header.asp и footer.asp. Документ header.asp помещается
в самое начало любой другой страницы нашего приложения, а документ
footer.asp — в конец:
<!- «include file="header.asp" ->
<п2>Добро пожаловать!</h2>
<р>Вы успешно подключились к нашему приложению.
<р>Ваш идентификатор: <X*Session("Us0rID")X><br>
Ваш пароль: <X=Session("UserPwd")XX/p>
<!- flinclude file="footer.asp" ->
Что представляет собой документ header.asp?
Его исходный текст приведен в листинге 3-19.
Листинг 3-19 Вы найдете в файле ch03/LoginTest/header.asp на прилагаемом к
книге компакт-диске.
При загрузке любой страницы, в начало которой вставлен файл header.asp,
выполняется проверка содержимого переменной сеанса с именем Ok:
<%
if(Session("Ok")!="Ok") {
х>
- ••'
<HEAD>
Глава 3. Применение технологии ASP 115

<МЕТА HTTP-EQUIV="Refresh" CONTENT="0; URL=default.asp; target=_top">


</HEAD>
<BODY>
Snbsp;
</BODY>
</HTML>
<% } else { %>
<HTML>
<;BOOY>
Если аутентификация пользователя прошла у с п е ш н о , в этой переменной
находится строка «Ok», а если нет — пустая строка (либо такая переменная не
определена вовсе).
В случае успешной аутентификации сценарий файла header.asp вставляет в
документ теги <HTML> и <BODY>, а при неудаче — тег <МЕТА>, принудительно на-
правляющий браузер на страницу аутентификации default.asp.
Таким образом, попытка прямой загрузки любой страницы нашего приложе-
ния без предварительной аутентификации приведет лишь к тому, что в окне
браузера появится предложение ввести идентификатор и пароль. Только после
успешной аутентификации определяется переменная сеанса с именем Ok и ста-
новится возможной работа с другими страницами приложения.
Файл footer.asp (листинг 3-20) просто вставляет в страницу закрывающие
теги </BODY> и </HTML>, а также закрывающую фигурную скобку оператора if-
else, проверяющего результат аутентификации в файле header.asp.
Листинг 3-20 Вы найдете в файле ch03/LoginTest/footer.asp на прилагаемом к
книге компакт-диске.

Передача параметров через скрытые поля форм


IB предыдущих примерах мы показали, как передавать параметры из одной стра-
ницы в другие с использованием переменных сеанса, Cookie и параметров в стро-
ке адреса URL страницы. Все эти способы, за исключением последнего, предпо-
лагают, что пользователь разрешил в своем браузере использование Cookie,
Однако осторожные пользователи могут отказаться от применения Cookie, и
тогда приложение будет работать неправильно.
Есть еще один способ передачи параметров между страницами ASP, доступ-
ность которого не зависит от параметров браузера пользователя. Этот способ
основан на применении скрытых полей в формах.
Рассмотрим конкретный пример.
Пусть в нашем приложении есть три страницы, предназначенные для после-
довательного выбора некоторых параметров. На первой из них (рис. 3-18) поль-
зователь должен выбрать цвет, размер и указать некоторое слово.
Выбрав параметры и щелкнув кнопку Дальше >», пользователь попадает на
вторую страницу (рис. 3-19). Здесь ему нужно ввести свое имя.
При помощи кнопки Дальше »>, расположенной на второй странице, поль-
зователь попадает на последнюю, третью, страницу, отображающую все выбран-
ные ранее параметры (рис. 3-20).
116 Базы данных в Интернете. Практическое руководство

hHp://ialuin/EookStoieyHidJe4l html - Microsoft

^Go
Любнкыйцьет Зеленый j

Лю Бимый р азмер TJ
Волшебное слово [Пожалуйста

il intranet

Рис. 3-18. Первая страница, предназначенная для выбора параметров

Д htlpi/j'satum/eookStore/hiddenZ.asu - Micioso*t I.

[Иван Сидоров]

Рис. 3-19. Вторая страница, предназначенная для выбора параметров

3 htl р:/Ла tuWBook Store/hid den 3. asp - Ни

.
Итог

Любимо; ш е-.- Зележий


Любнмыйра^мер Большой
Вапшебное слово Пожалуйста
Бет; им» Иван Сидоров

IT; Local intranet

Рис. 3-20. Просмотр выбранных параметров


Обратите внимание, что параметры, указанные в первой и второй странице,
были переданы в третью с/грани г [у. Чтобы осуществить это, мы применили скры-
тые поля форм, невидимые на экране.
Рассмотрим исходные тексты приложения.
Первая страница (листинг 3-21) содержит обычную форму с двумя списка-
ми и текстовым полем, а также кнопку Дальше >» типа s u b m i t .
<input type="submit" уа!ие="Дальше »>" name="B1"x/p>
Здесь пока не использованы никакие скрытые поля.
Глава 3. Применение технологии ASP 117

Листинг 3-21 Вы найдете в файле ch03/Hidden1.html на прилагаемом к книге


компакт-диске.
Когда Вы щелкнете эту кнопку, данные из формы будут переданы методом
POST в страницу hidden2.asp, указанную в параметре ACTION тега <FORM>:
<form method="POST" action="hidden2.asp">
Исходный текст этой страницы представлен в листинге 3-22.
Листинг 3-22 Вы найдете в файле ch03/Hidden2.asp на прилагаемом к книге
компакт-диске.
На странице hidden2.asp находится форма с однострочным текстовым полем
Name, а также три скрытых текстовых поля с именами FavoriteColor, FavoriteSize
и KeyWord:
<form method="POST" action="hidden3. asp">
11
<table border="0 width="332">
<trxtd width="171" height="21">Baiue HMfl</td>
<td width="153" height="21">
<input type="text" name="Name" size="20"></td>
</tr>
</table>
<pxinput type="submit" уа1ие="Дальше >»" name="B1"X/p>
<input type="hidden" value="<5(=Request. Form("FavoriteColor")X>"
name="FavoriteColor"x/p>
<input type="hidden" value="<X=Request. Forfn("FavoriteSize")!(>"
name="FavoriteSize"x/p>
<input type="hidden" value="<X=Request.Form("KeyWord")X>" name="KeyWord"X/p>
</form>
Значения этих полей, определяемые параметром value, зависят от данных,
принятых из полей формы, расположенной на предыдущей странице. Таким
образом, содержимое скрытых полей формы второй страницы зависит от выбо-
ра, сделанного пользователем в форме первой страницы.
После того как пользователь щелкнет на второй странице кнопку Даль-
ше »>, вызывается третья страница hiddenS.asp. Ей будут переданы все поля
из формы второй страницы, в том числе и скрытые.
Исходный текст третьей страницы представлен в листинге 3-23.
Листинг 3-23 Вы найдете в файле ch03/Hidden3.asp на прилагаемом к книге
компакт-диске.
Здесь мы просто извлекаем содержимое всех полей формы, расположенных
на странице hidden2.asp, и показываем в ячейках таблицы:
<table>
<1:г><±й>Любимый UBer</td>
<tdXb><X=Request. Form("FavoriteColor" )%x/bx/tdx/tr>
<trXtd>.flio6MMbifi pa3Mep</td>
<td><bx?(=Reqijest.Form( "FavoriteSize" )XX/b></tdx/tr>
<trxtd>Boruie6Hoe croeo</td>
<tdXb><X=Request. Form( "KeyWo rd" )X></bx/td></tr>
<tr><td>Bauie HMa</td>
<tdxb><X=Request. Form( "Name" )X></b></td></t r>
</table>
118 Базы данных в Интернете. Практическое руководство

Описанную технику используют и при создании мастеров, упрощающих вы-


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

Отладка сценариев в страницах ASP


Интегрированная среда разработки Microsoft InterDev 6.0 предоставляет отлич-
ные средства для отладки не только клиентских, но и серверных сценариев. Без
этой возможности разработка сложных приложений ASP была бы очень затруд-
нена.
Если серверный сценарий написан на языке JScript, то для вызова отладчи-
ка Вам необходимо вставить в исходный текст сценария оператор d e b u g g e r (за-
метим, что недостаточно просто поставить точку останова, нужно вызвать отлад-
чик явным образом).
На рис. 3-21 мы показали процесс отладки одного из серверных сценариев,
показанных в этой главе.
Microsoft Development Г nvirnnmenl Ibieakl mam.asp IRead Link I
49 File Edt view froied

- 3»*w» var nCount-0; £9 Solution 'Solution!' (0 pro|ect:


_J Uteri Serf > if | Request ("MSET" M l ) "ж D ] Miscellaneous Files
_J Server Obi
_J Server =c-i Response. Cookies ( "bgColor ") ="Uhil
Response.Cookies i"Count")="0";

vat sColor"Regucsc.rorm( P I ColDc"J


Resoonsc . Caokies I "boColor "I •
'•I f ']
_J^J

Serve.,, -
Ty[K ^
DBSIgn-Tt.,,
Long
ActiveX C..
General '

Рис. 3-21. Отладка серверного сценария


Отладчик Microsoft InterDev 6.0 позволяет устанавливать дополнительные
точки останова, просматривать содержимое переменных и выполнять множество
других действий. Подробное описание процесса отладки выходит за рамки на-
шей книги. Заметим только, что для использования этого отладчика Вам при-
дется настроить соответствующим образом сервер Microsoft Internet Information
Server. По умолчанию отладка сценариев отключена, и Вам следует ее включить.
Глава 3. Применение технологии ASP

Запустите приложение Microsoft Management Console и откройте панель


свойств сервера Web (или отдельного виртуального каталога), выбрав строку
Properties из контекстного меню. Щелкните к н о п к у Configuration на вкладке
Virtual Directory, чтобы открыть панель Application Configuration (рис. 3-22).
Anplic.itinn Lrinlinuratn

Script Его; Messages-

<•" Send deiailedASP erroi messages to clier*


f~ Send text еле* message to clieri.

help

Рис. 3-22. Панель Application Configuration


Здесь Вам нужно открыть вкладку Арр Debugging и пометить переключате-
ли Enable ASP server-side script debugging и Enable ASP client-side script
debugging. После этого закройте панель и перезагрузите компьютер. Теперь Вы
можете приступить к отладке серверных сценариев.
Г Л А В А 4

Связь приложений с базами данных


через ADO

В предыдущих главах мы рассказывали о создании страниц HTML и ASP, отве-


чающих за интерактивное взаимодействие с пользователем через окно браузе-
ра. Теперь настало время обратиться к другому, не менее важному аспекту раз-
работки приложений Web — организации взаимодействия этих приложений с
базами данных.
Существуют различные способы связи приложений с базами данных. На се-
годняшний день наиболее перспективный из них, несомненно, связан с приме-
нением интерфейса ActiveX Data Objects (ADO). Посредством этого интерфейса
приложения (как обычные, так и ориентированные на использование техноло-
гий Интернета) могут подключаться к базам данных, извлекать, обрабатывать и
обновлять информацию в них.
Эта глава посвящена применению интерфейса ADO для связи приложений
ASP с базами данных, созданными на основе Microsoft SQL Server. В качестве
примера приложения Web мы выбрали Интернет-магазин, торгующий книгами.

Основы 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

Рис. 4-1. Объект Connection

Команды Command имеют отношение к вполне конкретному источнику данных,


открытому для объекта Connection. Таким образом, Вы создаете объект Command
для использования с выбранным источником данных.
После успешного выполнения команды может быть создан набор записей
R e c o r d s e t . On тоже генерируется в контексте того сеанса связи с источником
данных, который был открыт в рамках объекта C o n n e c t i o n .
Если при ныполнении команды возникли ошибки, создается объект E r r o r s ,
представляющий собой набор (collection) объектов E r r o r . Все эти объекты име-
ют отношение к конкретному объекту C o n n e c t i o n и должны обрабатываться в его
контексте.
Рассмотрим маленький пример — фрагмент серверного сценария JScript,
расположенного на странице ASP:
var connect;
connect = Server.CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect. CommandTinieout = 10;
connect.Open("DSN=BookStore", "dbo", "password");
В первой строке мы определяем переменную c o n n e c t , предназначенную для
хранения объекта C o n n e c t i o n .
Далее мы создаем объект C o n n e c t i o n , вызывая метод CreateObject объекта
Server (объект Server является встроенным объектом ASP).
Перед тем как установить соединение с источником данных, мы задаем два
свойства объекта C o n n e c t i o n — тайм-аут сеанса C o n n e c t i o n T i m e o u t и тайм-аут
Глава 4. Связь приложений с базами данных через ADO 123

выполнения команды CommandTimeout. Первое из них определяет время ожида-


ния установления канала связи с источником данных (в секундах), а второе —
время ожидания выполнения команды. Если тайм-аут истек, устанавливается
состояние ошибки.
Зачем задаются эти параметры?
Просто для того, чтобы сервер Web не ожидал бесконечно соединения или
выполнения команды. Соединение будет разорвано также и в случае сильной
загрузки сервера SQL Server, когда он не успевает справиться с поступающими
запросами. Зная среднее время выполнения команд, Вы можете в своем прило-
жении соответственным образом настроить тайм-ауты.
Помимо свойств Connect ion Timeout и CommandTimeout, объект Connection имеет
и другие свойства, определяющие параметры соединения. Однако пока мы ог-
раничимся применением только этих свойств.
Открытие канала связи с источником данных в нашем примере выполняет-
ся при помощи метода Open, определенного в интерфейсе объекта Connection. Мы
передаем этому методу три параметра, определяющих имя источника д а н н ы х ,
идентификатор пользователя и пароль, необходимые для получения доступа.
Чтобы закрыть канал связи, используйте метод Close объекта Connection. Все
неиспользованные каналы связи следует закрывать для экономии ресурсов сер-
вера. В частности, сервер SQL Server может иметь ограниченное количество
лицензий на соединения с клиентами. Если вовремя не закрывать неиспользу-
емые соединения, можно быстро исчерпать лимит таких лицензий, в результа-
те чего приложение перестанет работать.
Объект Command
Как мы уже говорили, объект Command посылает команды в базу данных с целью
проведения таких операций, как запуск хранимых процедур или исполнение
строк программы Transact-SQL.
Прежде всего, необходимо создать объект Command, обратившись для этого к
методу CreateObject объекта Server:
var cmd;
cmd = Server.CreateObject("ADODB.Command");
Как видите, объект Command создается аналогично объекту Connection.
После создания объекта Command необходимо установить как минимум три
свойства этого объекта — ActiveConnection, CommandText и CommandType:
cmd.ActiveConnection = connect;
cmd.CommandType = adCmdStoredProc;
cmd.CommandText = "ClientLogin";
Сначала мы расскажем о свойстве A c t i v e C o n n e c t i o n .
Вы уже знаете, что любой объект Command имеет отношение к конкретному
соединению Connection. Чтобы указать, что команда предназначена для источ-
ника данных, доступ к которому выполняется через соединение connect, необ-
ходимо записать ссылку на объект Connection в свойство A c t i v e C o n n e c t i o n .
Свойство CommandType задается как константа и определяет тип выполня-
емой команды. При этом назначение свойства CommandText полностью определя-
ется типом команды, как это показано в таблице 4-1.
124 Базы данных в Интернете. Практическое руководство

Таблица 4-1. Константы типов команд


Константа Назначение свойства CommandText
adCmdText Текстовое определение команды
adCmdTable Имя таблицы
adCmdStoredProc Имя хранимой процедуры SQL Server
adCmdUnknown Неизвестный тип команды (установлен по умолчанию). Если тип
команды явно не указан. ADO обращается к провайдеру с запро-
сом на определение типа команды. Выполнение этого запроса, свя-
занное с анализом свойства adCmdText, отнимает время. Поэтому
для увеличения производительности рекомендуется указывать тин
команды явным образом
Чтобы выполнить такую строку программы SQL, как «select * from clients»,
следует записать в свойство C o m m a n d T y p e константу adCmdText, а в свойство
CommandText — строку программы SQL, например:
cmd.CommandType = adCmdText;
cmd.CommandText = "select * from clients";
Ранее приведен пример использования константы adCmdStoredProc для запус-
ка хранимой процедуры.
В наших приложениях мы будем выполнять все обращения к базе данных
исключительно с применением хранимых процедур. Таким образом, в серверных
сценариях, расположенных на наших страницах ASP, Вы не найдете ни одной
строки SQL. Однако в разделе, посвященном вызову методов ADO в приложе-
ниях C++, показан пример программы, которая непосредственно запускает стро-
ку SQL, выполняющую выборку данных из таблицы.
А теперь небольшое отступление от темы: мы объясним, почему отказались
от кодирования программ SQL непосредственно в серверных сценариях (хотя
это вполне допустимо).
Главная причина этого — стремление отделить данные от программ.
Сложные проекты обычно создаются группой разработчиков. При этом кто-
то отвечает за дизайн страниц Web, кто-то разрабатывает сценарии ASP, а кто-
то ведет базу данных. Отделение данных от программ позволяет разработчику
базы данных не вникать в программирование серверных сценариев ASP, а созда-
телю страниц ASP, в свою очередь, не владеть в совершенстве языком SQL. Со-
здавая интерфейс между приложением и базой данных на уровне хранимых
процедур, мы разделяем задачи программирования сценариев и разработки базы
данных, упрощая процесс разработки и сопровождения.
Объект Parameters
С командой можно передать один или несколько параметров. Параметры пере-
даются в виде набора Parameters, содержащего объекты P a r a m e t e r (рис. 4-2).
В наших приложениях мы будем передавать параметры хранимым процеду-
рам SQL Server, причем параметр],! будут как входными, так и выходными. Ниже
приведен фрагмент программы серверного сценария JScript, создающий два
входных параметра и один выходной:
cmd.Paramete rs.Append(cmd.С reatePa ramete r(
"User", adVarChar, adParamlnput, 50, "admin"});
Глава 4. Связь приложений с базами данных через ADO 125

cmd. Parameters. Append(cmd. CreateParatneterf


"Pass", adVarChar, adParamlnput, 50, "adm_password"));

var ParamOut = cmd.CreateParameter{


"Rights", adVarChar, adParamOutput, 50, " ");

cmd.Parameters.Append(ParamOut);
В первой строке мы обращаемся к методу C r e a t e P a r a m e t e r , определенному в
объекте cmd класса Command (напомним, параметры имеют отношение к командам).

Command

Parameters
Parameter

Рис. 4-2. Набор Parameters


Через первый параметр метода C r e a t e P a r a m e t e r передастся имя параметра
команды c m d . В нашем случае это U s e r — имя пользователя.
Второй параметр метода C r e a t e P a r a m e t e r определяет тип параметра коман-
ды cmd (строка, число, время, деньги и т. д.) и задается в виде константы. Мы
передаем в хранимую процедуру имя пользователя типа v a r c h a r , поэтому тип
параметра указан как a d V a r C h a r . Другие типы параметров приведены в табли-
це 4-2.
Таблица 4-2. Константы для указания типов параметра команды
Константа Тип данных
adArray Эту константу можно складывать с другими при помощи опера-
ции ИЛИ для указания, что параметр содержит массив указан-
ных типов данных
adBiglnt Знаковое целое длиной 8 бит
adBinary Бинарное значение (байт)
adBoolean Булево значение
adByRef Эта константа складывается с другими, если данные передаются
через указатель
adBSTR Строка Unicode, закрытая двоичным нулем (тип BSTR)
adChar Строка символов, закрытая нулем
adCurrency Денежная сумма. Этот тип данных представляет собой 8-байто-
вое знаковое целое. Хранит значение с четырьмя цифрами справа
от десятичной точки
adDate Дата. Хранится в формате double. Целая часть представляет со-
бой количество дней, прошедшее с 30 декабря 1899 года, а дроб-
ная — дробную часть текущего дня
Базы данных в Интернете. Практическое руководство

Таблица 4-2. Константы для указания типов параметра команды (продолжение)


Константа Тип данных
adDBDate Дата в формате ГГГГММДД, где:
ГГГГ - год;
ММ — месяц;
ДД - день
adDBTime Время в формате ЧЧ.ММСС, где:
ЧЧ — часы;
ММ — минуты;
СС — секунды
adDBTimeStamp Отметка о времени (date-time stamp) в формате
ГГГГММДДЧЧММСС плюс дробная часть
adDecimal Десятичное целое с фиксированной точкой
adDouble Значение двойной точности с плавающей точкой
adEmpty Пустое(не заполненное)значение
ad Error Код ошибки (32-разрядный)
adGUID Глобальный уникальный идентификатор (GUID)
adIDispatcti Указатель на интерфейс I D i s p a t c h объекта OLE
adlnteger Четырсхбайтовое знаковое целое
adIUnknown Указатель на интерфейс ILJnknown объекта OLE
adLongVarBinary Тип long (только для объекта P a r a m e t e r )
adLongVarChar Строка переменной длины (только для объекта P a r a m e t e r )
adLongVarWChar Строка переменной длины, состоящая из многобайтовых симво-
лов (только для объекта P a r a m e t e r )
adNumeric Численное значение с фиксированной точностью
adSingle Плавающее значение с одинарной точностью
adSmalllnt Двухбайтовое знаковое целое
adTinylnt Однобайтовос знаковое целое
adUnsignedBiglnt 8-байтовое беззнаковое целое
adUnsignedlnt 4-байтовое беззнакоиое целое
adUnsignedSmalllnt 2-байтовое беззнаковое целое
adUnsignedTinylnt Однобайтовое беззнаковое целое
adUserDefined Переменная, определенная пользователем
adVarBinary Бинарное значение (только для объекта P a r a m e t e r )
adVarChar Строка символов (только для объекта Parameter)
adVariant Тип автоматизации V a r i a n t
adVector Эта константа складывается с другими, если данные имеют
структуру DBVRCTOR. определенную в OLE DB, которая содер-
жит счетчик элементов и указатель на данные другого типа
adVarWChar Строка Unicode, закрытая двоичным нулем (только для объекта
Parameter)
adWCnar Строка Unicode, закрытая диоичным нулем
Глава 4. Связь приложений с базами данных через ADO 127

Третий параметр метода C r e a t e P a r a m e t e r за/част направление передачи дан-


ных через соответствующий параметр команды end. Параметры User и Pass вход-
ные, a R i g h t s — выходной. Для обозначения входных параметров используется
константа adParamlnput. Выходные параметры обозначаются константой adParam-
Output.
Полный список констант направления передачи данных приведен в табли-
це 4-3.
Таблица 4-3. Константы для указания направления передачи данных
Константа Направление передачи данных
adParamlnput Входной параметр. Используется по умолчанию
adParamOutput Выходной параметр
adParamlnputOutput Параметр используется и как входной, и как выходной
adParamReturnValue Через этот параметр передастся но.чвращасмое значение
Через последние два параметра методу C r e a t e P a r a m e t e r передаются соответ-
ственно размер области памяти, занимаемой параметром, и значение этого па-
раметра. В нашем случае все параметры представляют собой текстовые строки
размером не более 50 байт. Параметры U s e r и Pass имеют значение a d m i n и
iadm_password соответственно, а параметр Rights задается как символ пробела (это
выходной параметр).
После создания очередного параметра класса P a r a m e t e r его нужно добавить
в набор Parameters. Эта задача выполняется мри помощи метода Append, опреде-
ленного в объекте Parameters.
В приведенном нами примере последовательно создаются и добавляются три
параметра. Выходной параметр добавляется в два приема, хотя можно было бы
создавать его.таким же образом, как и входные параметры.
Как получить значение выходных параметров после выполнения команды?
Это делается простым обращением к свойству v a l u e параметра:
cmd. ExecuteO;
var sRights=ParamOut.value
Здесь мы запустили на выполнение команду cmd, а затем получили значение
выходного параметра (обработка ошибок опущена для простоты, этот процесс
описан позже).
Объект Recordset
Хотя результатом реализации х р а н и м о й процедуры может быть заполнение
выходных параметров, чаще это таблица, созданная в процессе выполнения
строк SQL, расположенных в данной процедуре.
Когда Вы запускаете команду методом Execute, метод возвращает Вам объект
Recordset, созданный в результате ее выполнения (конечно, если данная команда
генерирует упомянутую выше таблицу).
Объект R e c o r d s e t — это набор, состоящий из набора Fields. Последний, в
свою очередь, состоит из объектов Field (рис. 4-3).
Вы можете извлечь любую запись из набора Recordset, пользуясь набором
Fields и объектом Field..
Как это сделать?
Базы данных в Интернете. Практическое руководство

Recordset

Fields
Field

Рис. 4-3. Набор Recordset


При создании объекта Recordset автоматически создается курсор, связанный
таблицей, полученной в результате выполнения команды. Методы объекта Record-
set позволяют определять тип курсора (предназначенный только для чтения,
допускающий просмотр в одном направлении, статический, динамический, уп-
равляемый ключами), а также перемещать курсор для извлечения полей таблицы.
Предположим, нам нужно отобразить па странице ASP список книг, отобран-
ных посетителем Интернет-магазина. Мы подготовили хранимую процедуру,
получающую в качестве входного параметра имя клиента ClientID и возвраща-
ющую список отобранных книг как результат выполнения запроса оператором
SELECT:
CREATE PROCEDURE ListOrders ©ClientID varchar(50) AS
DECLARE @nClientID INT
SELECT @nClientID=clients.ClientID FROM clients WHERE UserID=@ClientID
SELECT books.booksID, books.Author, books.Title, books,Publisher, books.Price
FROM orders
JOIN books ON orders.booksID=books.booksID
WHERE orders.ClientID=@nClientID
Эта процедура будет подробно рассмотрена позже. Сейчас нам важно толь-
ко то, что она получает один входной параметр ClientID, а возвращает таблицу,
содержащую пять столбцов таблицы books: booksID, A u t h o r , Title, P u b l i s h e r и
Price (это как рая тот интерфейс между приложением и базой данных, который
нужен для отделения программы от данных).
Для вызова хранимой процедуры ListOrders мы используем следующий фраг-
мент серверного сценария:
var connect, rs, cmd, ClientID;

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

Обратите внимание на последнюю строчку: значение, возвращенное методом


Execute, присваивается переменной rs. Эта переменная хранит объект Recordset,
созданный в результате выполнения хранимой процедуры ListQrders.
Мы знаем, что хранимая процедура ListOrders возвращает таблицу с пятью
колонками. Чтобы было удобнее обращаться к колонкам, мы определили в сво-
ем сценарии пять переменных:
var fieldbooksID = 0;
var fieldAuthor = 1;
var fieldTitle = 2;
var fieldPublisher = 3;
var fieldPrice = 4;
Теперь мы должны получить все строки возвращенной таблицы, передвигая
курсор и обращаясь к объектам Field:
*>
<HTML>
<BODY>
<М2>Вы отобрали для покупки</Ь2>
<TABLE BORDER=1>

while (!rs.EOF)
U>
<tr>
<td>
<*=rs.Fields(fieldAuthor)j;>. <*=rs. Fields(fieldTitle)!H><br>
<X=rs,Fields(fieldPublisher>X></td>
<td>
<K=rs. Fields(f ieldPrice)a;> y. e. </td>
</tr>
<*
rs.MoveNext();
:
X>
</TABLE>
<X
Здесь мы создаем таблицу и записываем в ее ячейки содержимое полей те-
кущей записи (на которую указывает курсор), обращаясь к четырем из пяти
столбцов.
Перемещение курсора на следующую запись выполняется методом MoveNext,
определенным в объекте Recordset. Чтобы проверить условие завершения цик-
ла, наш сценарий обращается к свойству rs, EOF.
Таким образом, при каждом перемещении курсора мы получаем доступ к
очередной строке таблицы, созданной в результате вызова хранимо процедуры
L i s t O r d e r s . Для извлечения содержимого отдельных полей текущей строки мы
используем набор Fields. Элементы этого набора (представляющие собой объек-
ты Field) соответствуют полям текущей строки: первый элемент (с индексом 0)
соответствует первому столбцу (booksID), второй — второму и т. д.
Заметим, что во многих случаях удобнее обращаться к полям набора записей
не по номерам колонок, а по именам:
130 Базы данных в Интернете. Практическое руководство

<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

До недавних времен язык сценариев JScript не позволял обрабатынать исклю-


чения, однако, начиная с JScript версии 5.0, ситуатшя изменилась.

Рис. 4-4. Набор Errors


Рассмотрим следующий фрагмент кода, в котором выполняется обработка
ошибок при использовании интерфейсов ADO:
try
{
connect = Server,CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");
cmd = Server.CreateObjectC'ADOQB.Command");
cmd.CommandText = "AddToOrder";
cmd.CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;

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>");

(см. след, стр.)


132 Базы данных а Интернете. Практическое руководство

Response.Redirect(
"error.asp?ERROR=books.asp"+"&ERRMSG="+serrMessage);

else
throw ex;

Строки сценария JScript, в которых выполняется обращение к интерфейсу


ADO, расположены в блоке try. Когда при вызове методов этих интерфейсов или
просто в процессе выполнения сценария происходит ошибка, управление пере-
дастся блоку catch. Заметим, что в сценарии JScript каждому блоку try может
соответствовать только один блок catch, а не несколько, как в программах, со-
ставленных на языках C++ или Java.
В качестве параметра в блок catch передается объект, содержащий информа-
цию о возникшей ошибке. Если ошибка произошла при вызове методов интер-
фейсов ADO, этот объект имеет тип E r r o r .
Наш обработчик ошибок перехватывает только такие ошибки, передавая ос-
тальные системе интерпретации серверных сценариев ASP с помощью ключе-
вого слова throw. Для проверки принадлежности переменной ех классу E r r o r мы
используем ключевое слово i n s t a n c e o f .
На следующем этапе проверяется содержимое свойства Count объекта Errors^
В нем находится счетчик объектов E r r o r в наборе E r r o r s , то есть, попросту го-
воря, количество ошибок. Если оно равно нулю, мы считаем, что произошла
неожиданная ошибка, и передаем ее системе интерпретации серверных сцена-
риев ASP.
Далее в цикле перебираются вес элементы набора Errors, формируя в тексто-
вой переменной serrMessage итоговое сообщение об ошибке в формате фрагмен-
та документа HTML. Это сообщение затем передается странице error.asp через
параметр ERRMSG. Параметр ERROR мы используем для передачи имени страни-
цы ASP, в которой произошла ошибка.
Как лучите всего реализовать обработку подобных ошибок в приложении
Web?
Очевидно, пользователю не интересна развернутая диагностика ошибки, воз-
никшей в Вашем приложении. Достаточно, если при возникновении подобной
ситуации он получит сообщение о том, что произошла внутренняя ошибка, с
рекомендацией попробовать выполнить операцию еще раз через некоторое время.
С другой стороны, информация об ошибках нужна Вам как разработчику
приложения для его отладки и ликвидации возникших проблем. Поэтому мож-
но попросить пользователя отправить вам диагностику по электронной почте,
подготовив заранее форму с сообщением. А лучше, если приложение будет ав-
томатически записывать сообщения об ошибках в файл базы данных для после-
дующего анализа. Допустимо, разумеется, комбинировать оба способа.
Объект Properties
Объекты Connection, Command, Recordset и Field содержат в себе объекты Proper-
ties. Объект Properties представляет собой набор объектов Propertie, представ-
ляющих параметры объектов Connection, Command, Recordset и Field. Взаимосвязь
этих объектов показана на рис. 4-5.
Глава 4. Связь приложений с базами данных через ADO 133

Connection

Command

Properties
Property

Recordset

Field

Рис. 4-5. Набор Properties


Объекты ADO имеют встроенные и динамические объекты Properties. Пер-
1ше реализуются в рамках ADO и доступны всегда, вторые обеспечиваются про-
вайдером данных.
Б таблице 4-5 перечислены встроенные объекты Properties.
Таблица 4-5. Встроенные объекты Properties
Объект Описание
Name Текстовая строка, идентифицирующая свойство
Туре Целое число, определяющее тип данных свойства
Value Переменная типа V a r i a n t , содержащая значения свойства
Attributes Целое число типа long, идентифицирующее характеристики свойства,
специфические для провайдера данных
Объекты Properties применяются, например, в случае необходимости опре-
деления или изменения времени тайм-аута или других параметров соединения.
Но во многих случаях Вы вполне можете обойтись и без этого.
Константы ADO
При вызове некоторых методов ADO из серверных сценариев JScript необходи-
мо указывать константы, определяющие, например, направление передачи дан-
ных через параметры или тип данных. Однако прежде чем использовать эти
константы, необходимо позаботиться об их определении.
Здесь возможны два способа.
Первый — включать в каждую страницу ASP, вызывающую методы ADO,
специальный файл определения констант. После установки сервера SQL Server
Бы найдете этот файл в каталоге Program Files\Common Files\System\ado под
именем adojavas.inc. Вот небольшой фрагмент, взятый нами из этого файла:

//— ParameterDirectionEnum Values -


var adParamUnknown = 0x0000;
(см. след, стр.)
I
134 Базы данных в Интернете. Практическое руководство

var adParamlnput = 0x0001;


var adParamOutput - 0x0002;
var adParamlnputOutput = 0x0003;
var adParamReturnValue = 0x0004;

//-- Command Type En urn Values -


var adCmdUnknovm = 0x0008;
var adCmdText = 0x0001;
var adCmdTable = 0x0002;
var adCmdStoredProc = 0x0004;
var adCmdFile = 0x0100;
var adCmdTableDirect = 0x0200;

Однако этот способ иногда вызывает некоторое замедление интерпретации


страниц сервером Web, так как файл adojavas.inc увеличивает количество строк
сценария, подлежащих интерпретации.
Лучшее решение — импортировать библиотеку типов ADO в файле global.asa,
выполняемое с помощью тега METADATA с параметром TYPE="typelib":
<!- METADATA TYPE="typelib 11
FILE="d:\program files\common files\system\ado\msado20.tlb" ->
<SCRIPT LANGUAGE=JSCRIPT RUNAT=Server>
function Session_OnStart(){}
function Session_OnEnd(){}
function Applicatlon_OnStart(){}
function Application_OnEnd(){}
</SCRIPT>
В результате импорта библиотеки типов ADO все константы, имеющие от-
ношение к ADO, будут автоматически доступны во всех страницах Вашего при-
ложения ASP.
Изучая содержимое каталога Program Files\Common Files\System\ado, обра-
тите внимание на подкаталог Docs. В нем находится подробный справочник по
использованию ADO (на английском языке), подготовленный в формате HTML.
В каталоге ADO Вы также найдете файл adovbs.inc с определениями констант
для серверных сценариев VBScript, а также текстовые файлы с описанием осо-
бенностей текущей версии ADO и другой полезной информацией.

Проект Интернет-магазина
Теперь, когда Вы предварительно познакомились с 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,

Наитию (He size

Cancel

Рис. 4-6. Создание базы данных


Здесь на вкладке General в поле Name введите имя базы данных, которое мы
будем использовать в нашем проекте, — BookStore. Щелкнув кнопку с многото-
чием, расположенную в поле Location, выберите путь для размещения файла
базы данных.
Хотя для нашего проекта это и не требуется, Вы можете указать на вкладке
General параметры, влияющие на рост файла базы данных.
По умолчанию при создании покой базы данных на этой вкладке отмечается
переключатель Automatically grow file, поэтому по мере добавления в таблицы
базы данных новых записей размер файла базы данных будет увеличиваться.
Величина прироста может быть указана либо в мегабайтах, либо я процентах.
Пометив переключатель Restrict filcgrowth в поле Maximum file size, Вы огра-
ничите рост файла базы данных.
Вкладка Transaction Log позволяет указать размер, прирост и ограничение
для файла транзакций. О том, как правильно выбирать начальные параметры
при создании базы данных, в деталях описано в документации SQL Server.
Теперь, после создания базы данных, нам нужно определить в ней четыре
таблицы, в которых будут храниться сведения о книгах, покупателях и персо-
нале магазина.

(Лак. 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.

iron cable [dbo] , [managers"

-TREUTE TABLE [dbo] . [managers] ,


[BaaagerlD] [ int] IDENTITY ;l,
[Маме] [varchac] r S O . MO" .NLH,!,
[Password] [varchat] i S O j N O T
[LaatLogin] [clatetirae] I HILL ,
[Rights] [varehar] ;i6; TOLL
' OK [PRIHAP.Y]

Рис. 4-7. Приложение SQL Server Query Analyzer


Следующий шаг очень важен.
Выберите в списке DB, расположенном в правой части инструментальной
панели окна Query, базу данных Bookstore, как это показано на рис. 4-7. Теперь
мы будем запускать программы SQL и контексте именно этой базы данных.
Глава 4. Связь приложений с базами данных через ADG

Далее воспользуйтесь строкой 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 Базы данных в Интернеге. Практическое руководство

Некоторые поля этой таблицы предусмотрены для будущего расширения


проекта. Например, поле L a n g u a g e Вы можете использовать в том случае, если
страницы Интернет-магазина написаны на нескольких языках. В этом случае
посетитель при регистрации может указать, какой язык должен быть использо-
ван при отображении страниц.
Поле Status позволяет проверить, какие посетители зарегистрировались, но
долго не делали никаких покупок (например, больше года). Выяснив это, Вы
можете удалить их регистрационные записи. В этом ноле удобно отмечать ак-
тивность покупателя, например, символом «А». Отмеченные таким образом за-
писи удалять не следует.
Поле spam применяют для opi анизации автоматической рассылки рекламных
сообщений покупателям Вашего магазина. Если при регистрации покупатель
проявил желание получать такие материалы, в это поле можно записать строку
YES, а если пет — строку N0.
Поля RegisterDate и RegisterIP полезны для <<разборок» с недобросовестными
посетителями. Средствами утилит трассировки можно определить по адресу
регистрации IP доменное имя сервера поставщика услуг Интернета, которым
пользуется покупатель, а затем с его помощью вычислить и самого покупателя.
Хотя это и не предусмотрено в нашем проекте, Вы можете собирать и хранить
в таблице clients другую информацию о посетителях, например версию исполь-
зованной ими операционной системы, название и версию браузера, доменное
имя рабочей станции. Все эти данные поступают на cepitep Web в заголовках
HTTP, их легко получить при помощи сценария ASP.
Для создания в базе Bookstore таблицы clients воспользуйтесь приложени-
ем SQL Server Query Analyzer и файлом сценария SQL с именем dbo.clients.TAB
(листинг 4-2).
Листинг 4-2 Вы найдете в файле ch4\BookShopScripts\dbo.clients.TAB на при-
лагаемом к книге компакт-диске.
Обратите внимания на ограничения CONSTRAINT, определенные в таблице
clients. Они задают начальные значения при добавлении в таблицу clients
новых записей:
CREATE TABLE [dbo].[clients] (
[ClientID] [int] IDENTITY (1, 1) NOT NULL ,
[UserlD] [varchar] (50) NOT NULL ,
[Password] [varchar] (50) NOT NULL ,
[Language] [varchar] (50) NOT NULL ,
[Money] [money] NOT NULL
CONSTRAINT [DF_clients_Money] DEFAULT (0),
[Status] [char] (1) NOT NULL
CONSTRAINT [DF_clients_Status] DEFAULT ('N'),
[LastLogin] [datetime] NOT NULL
CONSTRAINT [DF^clients.LastLogin] DEFAULT (0),
[LoginCount] [int] NOT NULL
CONSTRAINT [DF_clients_LoginCount] DEFAULT (0),
[UserName] [varchar] (50) NOT NULL ,
[Email] [varchar] (80) NOT NULL ,
[mail] [varchar] (80) NULL ,
[spam] [char] (3) NOT NULL
Глава 4. Связь приложений с базами данных через ADO 139

CONSTRAINT [DF_clients_spam] DEFAULT ( ' n o ' ) ,


[RegisterDate] [datetime] NULL
CONSTRAINT [DF_clients_RegisterDate] DEFAULT (getdateO),
[Register-IP] [varchar] (15) NULL ,
CONSTRAINT [PK_clients] PRIMARY KEY NONCLUSTERED
С
[ClientID]
) ON [PRIMARY]
) ON [PRIMARY]
Например, поле spam инициализируется строкой по, а в поле R e g i s t e r D a t e
автоматически заносится текущая дата, полученная от встроенной функции
getdate.
Состояние пользователя, хранящееся в поле Status, отмечается символом «N».
Когда посетитель сделает первую покупку, Вы можете записать сюда другое
значение, например, «А».
Мы сделали поля UserlD и Email уникальными:
CREATE UNIQUE INDEX [idxUserlD] ON [dbo].[clients]([UserID]) ON [PfllMARY]
GO

CREATE UNIQUE INDEX [idxEMail] ON [dbo].[clients]([Email]) ON [PRIMARY]


В результате мы исключили дублирование регистрационных записей поку-
пателей с одинаковыми идентификаторами и адресами электронной почты. Если
пользователь пытается зарегистрироваться и при этом выясняется, что выбран-
ный им идентификатор или адрес электронной почты уже есть в базе данных,
ему предлагается выбрать другой идентификатор и другой адрес электронной
почты.
Таблица books
В таблицу books сотрудники Вашего Интернет-магазина будут заносить сведе-
ния о продаваемых книгах, используя для этого административное приложение.
Покупатели также будут обращаться к этой таблице, по с помощью другого при-
ложения, созданного специально для них. В таблице books покупатели выбира-
ют нужные книги и кладут их в свою виртуальную электронную «корзину».
Поля таблицы books описаны в таблице 4-8.
Таблица 4-8. Поля таблицы books
Поле Тип Описание
booksID Int Идентификатор записи таблицы books (ключевое поле)
Author Varchar(SO) Автор книги
Title Varchar(200) На.чнаиие книги
Publisher Varchar(50) Название издательства, выпустившего книгу
Price Money Стоимость книги в условных единицах
AddDate Datetime Дата и время добавления информации о книге в таблицу
books
Annotation Varchar(2048) Аннотация на книгу
Цена к н и г и указана в условных единицах, но это не потом\ ', что курс нацио-
нальной валюты может вдруг измениться. Так как Вы создаете магазин в Интер-
140 Базы данных в Интернете, Практическое руководство

нете, то покупатели могут использовать для оплаты различные валюты, и тор-


гующему приложению придется выполнять пересчет стоимости. Тут-то и при-
годятся паши условные единицы.
Обратите внимание на поле A n n o t a t i o n , предназначенное для хранения анно-
тации на книгу. Его максимальная длина составляет 2 048 байт, что стало воз-
можным только с появлением SQL Server версии 7.0. Максимально поле типа
v a r c h a r может содержать 8 000 символов, что позволяет хранить в поле A n n o t a -
tion довольно объемистые аннотации.
Создавая реальный проект Интернет-магазина, Вы можете добавить в табли-
цу books и другие поля, например номер ISBN, сведения о поставщиках и т. п.
Сценарий SQL, предназначенный для создания таблицы books, находится в
файле dbo.books.TAB (листинг 4-3).
Листинг 4-3 хранится в файле ch4\BookShopScripts\dbo.books.TAB на прилага-
емом к книге компакт-диске.
Ограничение CONSTRAINT на поле AddDate автоматизирует процесс записи даты
добавления книги в таблицу books:
CREATE TABLE [dbo].[books] (
[bOOksID] [int] IDENTITY (1, 1) NOT NULL ,
[Author] [varchar] (50) NOT NULL ,
[Title] [varchar] (200) NOT NULL ,
[Publisher] [varchar] (50) NOT NULL ,
[Price] [money] NOT NULL ,
[AddDate] [datetime] NOT NULL
CONSTRAINT [DF_books_AddDate] DEFAULT (getdateO),
[Annotation] [varchar] (2048) NOT NULL ,
CONSTRAINT [PK_books] PRIMARY KEY NONCLUSTEflED
(
[booksID]
) ON [PRIMARY]
) ON [PRIMARY]
Ни одно из полей данной таблицы не может содержать пустые значения NULL,
поэтому при добавлении книги в базу данных необходимо предоставить полную
информацию о ней.
Таблица orders
Когда покупатели отбирают понравившиеся им книги для покупки, записи до-
бавляются в таблицу orders. Поля этой таблицы описаны ниже.
Таблица 4-9. Поля таблицы orders
Поле Тип Описание
ordersID Int Идентификатор записи таблицы o r d e r s (ключевое поле)
booksID Int Идентификатор книги, отобранной для покупки
ClientID Int Идентификатор покупателя, выбравшего данную книгу
AddDate Datetime Дата и время отбора книги
BookPrice money Стоимость отобранной книги в условных единицах
Поля b o o k s I D и C l i e n t I D представляют собой внешние ключи к таблицам
books и clients соответственно. Используя эти поля, мы можем определить, ка-
кую книгу отобрал тот или иной покупатель.
Глава 4. Связь приложений с базами данных через ADO 141

Поле 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

SELECT @Rights=UserID FROM clients WHERE UserID=@User AND Password=@Pass


UPDATE clients SET LastLogin=GETDATE() WHERE UserID=@User
Результат аутентификации записывается процедурой ClientLogin в выходной
параметр © R i g h t s . Попутно наша процедура обновляет поле LastLogin таблицы
clients, записывая в нее время и дату подключения, полученные от встроенной
функции GETDATE.
Сейчас мы не будем подробно описывать работу этой и других процедур — о
ней мы расскажем в разделах, посвященных о п и с а н и ю страниц ASP нашего ма-
газина. Отметим только, что процедуры Вы можете добавить в базу данных при
помощи приложения SQL Server Query Analyzer.
Базы данных в Интернете. Практическое руководство

После этого не забудьте выделить пользователям Интернета права на их


выполнение. Для этого запустите приложение SQL Server Enterprise Manager,
откройте базу данных B o o k s t o r e и папку с хранимыми процедурами Stored
Procedures. Выберите процедуру и щелчком правой клавиши мыши откройте
страницу Stored Procedure Properties, показанную на рис. 4-8.
: Sloied Pioceduie Pioperlies ChenlLoqin

Owner.
Create Oate

SEL£:T@fligWs LtserlD FROM clients WHEHE UseilD*@User О Pas


UPDATE clientsSEГ LaslLcgin- L > Ff WHERE UsertD-@'Js«

Рис. 4-8. Страница Stored Procedure Properties


Здесь Вы можете редактировать текст процедуры. Для сохранения измене-
ний щелкните кнопку ОК. Что же касается прав на выполнение процедуры, то
их нужно добавить кнопкой Permissions. Открыв панель Object Properties, от-
метьте на вкладке Permissions переключатели в столбце EXEC в строках тех
пользователей, которым Вы хотите предоставить доступ на выполнение про-
цедуры.

Создание источника данных


Теперь, когда мы создали базу данных с таблицами и хранимыми процедурами,
необходимо обеспечить доступ к ней со стороны сервера Web. Для этого нужно
определить источник данных.

Рис. 4-9. Страница Create New Data Source


Глава 4. Связь приложений с базами данных через ADO

Откройте на компьютере, играющем роль сервера Web, папку Control Panel


и дважды щелкните пиктограмму ODBC Data Sources. После того как на экра-
не появится панель ODBC Data Sources Administrator, откройте вкладку Sys-
tem DSN и нажмите кнопку Add. Вы увидите страницу мастера создания источ-
ников данных с названием Create New Data Source (рис. 4-9).
В списке, расположенном на этой странице, выберите драйвер SQL Server, a
затем щелкните кнопку Finish. На экране появится первая страница мастера
создания источника данных для SQL Server, показанная на рис. 4-10.
Cleato d New Dala Source to SQL Server

How do yew war* lo describe the daia $оысе?

Рис. 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

To change Ibe tielwork Ibray uset1 to


tick Clisnt Cartigijiaticr?

Рис. 4-11. Выбор способа аутентификации


144 Базы данных в Интернете. Практическое руководство

При использовании аутентификации Windows NT Вы можете оставить пере-


ключатели в том положении, как они показаны на рис. 4-11. Щелчок кнопки
Client Configuration позволит Вам выбрать сетевую библиотеку и указать пара-
метры подключения для выбранной библиотеки (рис. 4-12).
Add Network Librdiy Omfiqurnii.jn

!
I

f OII-

РИС. 4-12. Выбор сетевой библиотеки и настройка ее параметров


При создании источника данных для Интернет-магазина Вы можете оставить
эти параметры в исходном состоянии.
Чтобы продолжить процесс, щелкните кнопку Next в панели выбора спосо-
ба аутентификации, показанной на рис. 4-11. На экране появится третья панель
мастера, на которой нужно выбрать базу данных по умолчанию (рис. 4-13).

1У С hanoe the deiaull database 1

-:

ANSI quoted idenlifieps.


ЧУ Use ANSI nils, paddhtK and warn rn

Рис. 4-13. Выбор базы данных по умолчанию


Остальные элементы управления, расположенные в этой панели, можно не
менять.
Снова щелкните кнопку Next. В панели, показанной на рис. 4-14. укажите
нужный язык системных сообщений и пометьте переключатель Use regional
settings when outputting currency, numbers, dates and times.
Для завершения работы мастера щелкните в этой панели кнопку Finish. На
экране появится описание конфигурации созданного источника данных (рис. 4-15).
Теперь Вам нужно проверит], работоспособность источника, например, при
помощи кнопки Test Data Source. Щелкните ее, и если Вы все сделали правиль-
но, на экране появится сообщение об успешном завершении теста (рис. 4-16).
Глава 4. Связь приложений с базами данных через ADO :

0iange !he language ofSQL Servet s^iSm messaijss tir

E3
^ £ertofRS ifansWion to diaieeia

1
l.org oueqi 'пи imilliseconcil

Log ODBC driver ?telislics lo the bg f,le.

Рис. 4-14. Последняя панель мастера создания источника данных

A new ODBC dala scutce wil Ье степей wih the foflmwig


corfigwalam:

iaii5lat6 ChasacSej Data Yes


ogLwig RunningQuerpet Hc-
_OB Oliver S;atisticc MO
Jse irteyaied Seciey: Yes
J«e Region^ Sefthgs. Yes
3
repaed Statemenls Option: Prop temporal)» procedures on
disconnect
Jsa FetovB" Serva: No
JseANSI Quoted ideHiwra- Yes
Uie ANSI Null. Paddiig» and Warnings- Yes

Рис. 4-15. Описание конфигурации созданного источника данных

SQL Seiwei flDBC Data Source T«sl

Attempting
tiection established
Vending tjption settings
>isconrwctng from seivei

TESTSCOMaETED SUCTESS FULLY!

. !

Рис. 4-16. Сообщение об успешной проверке источника данных


Базы данных в Интернете. Практическое руководство

Теперь вес готово для того, чтобы приступить к настройке виртуальных ка-
талогов сервера 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:

Virtual Diedsui | Document Diruciwy SeeE*fci JHTrPHeaden | CusSon

• Anonymous Access and Authrailicatran Genital


Enabte агюпугпош access and adt Ihe
authan&cation methods'»this rssouoe. гл

Secure Corcrriunic

Use Key Manage! In create a Certificate Reguest TNs darls Ihi


wccess cl leceiwis a SSL Certihcate tor this reiouca

, -IPAddess and Donan Name Fiestndons

Й3| Втап! a Deny acsfssto Ihs resource using IP Addresses at Inteme

Рис. 4-17. Настройка доступа к виртуальному каталогу


Глава 4. Связь приложений с базами данных нерез ADO

Создайте виртуальный каталог с именем BookShop, воспользовавшись для


этого приложением Microsoft Management Console. Для ограничения доступа
отредактируйте свойства виртуального каталога BookShop, открыв в панели
BookShop Properties вкладку Directory Security (рис. 4-17).
Прежде всего, на этой вкладке Вы можете запретить доступ анонимных
пользователей Интернета к данному каталогу. Для этого щелкните кнопку Edit
в поле Anonymous Assess and Authentication Control. На экране появится па-
нель Authentication Methods (рис. 4-18).
Authentication Methods

ee' ore or moie Authentication Metfrads lof tNs resource

Г &lln* Anonymous АСЙВ*


NoUset Name/Password reijuied to access ths les

Account used !& Апог^чтюш Access.

fs Windows NT Cbarlenge^Rejponse
User Наше and Password required when;
* A(aw Апогцлпоиг ts disabled
"Access is restiioted using NTFS uccess СопЦЫ Lis

Рис. 4-18. Отмена доступа анонимных пользователей


Снимите в этой панели отметку у переключателя Allow Anonymous Access
и оставьте ее у переключателя Windows NT Challenge/Response.
Следующий таг — создание списка адресов IP для администраторов.
Щелкните кнопку Edit в поле IP Address and Domain Name Restrictions на
вкладке Directory Security (рис. 4-17). Вы увидите панель редактирования спис-
ка адресов IP, показанную на рис. 4-19.
and Domain Name Restrictions

IP Address Access Rejt'icfcns


BydelaJt, aflaxrpttaswfflbe.

(/Granted Ц 172,21.1.10

Рис. 4-19. Панель IP Address and Domain Name Restrictions


Отметьте в этой панели переключатель Denied Access. В результате к дан-
ному виртуальному каталогу будет запрещен доступ со всех адресов IP, за исклю-
чением перечисленных в списке. Список редактируется с помощью кнопок Add
(добавление нового адреса). Remove (удаление адреса) и Edit (редактирование
адреса).
Вы должны добавить в список IP-адреса рабочих станций сотрудников Ва-
шего магазина, работающих с административным приложением. При этом поза-
ботьтесь о том, чтобы у них были постоянные, а не динамические адреса IP.
Базы данных в Интернете. Практическое руководство

Дополнительный уровень безопасности административного приложения ре-


ализуется парольной системой ограничения доступа, встроенной в само прило-
жение.

Приложение покупателя
Приложение покупателя будут запускать посетители магазина, поэтому при его
реализации мы решили обойтись без клиентских сценариев, Это позволило нам
расширить номенклатуру браузеров, способных правильно показывать страни-
цы «публичной» части Интернет-магазина.
Прежде чем приступить к рассказу о том, как реализовать страницы ASP
приложения покупателя, рассмотрим логику его работы.
Когда посетитель попадает на эту страницу, в окне его браузера появляется
форма, показанная на рис. 4-20.
Э Подключение пользователя - Microsoft Inleinel

Вход для покупателей

Пароль

Пожалуйста, зарегистрируйтесь

Рис. 4-20. Первая страница приложения покупателя


Прежде всего посетитель должен зарегистрироваться, выбрав среди всего
прочего идентификатор (имя для входа) и пароль. Это позволит нам не только
получить информацию о покупателях, по и просто необходимо для создания в
этой базе индивидуальных электронных «корзин», куда покупатели отклады-
вают товар перед оплатой.
мдта - ILJ1 *.!

Вам НУЖНО

Рис. 4-21. Сообщение новым покупателям о необходимости зарегистрироваться


Глава 4. Связь приложений с базами данных через ADO -

Если посетитель уже зарегистрировался ранее, то, для того чтобы попасть в
магазин, ему нужно ввести в поле Имя свой идентификатор, а в поле Пароль —
свой пароль. Новые пользователи должны зарегистрироваться, щелкнув кноп-
ку Регистрация.
В тех случаях, когда указанный идентификатор пользователя отсутствует в
регистрационной базе данных или когда пароль указан неправильно, посетитель
получает сообщение о необходимости регистрации (рис. 4-21).
Щелкнув ссылку Вам нужно зарегистрироваться, посетитель попадает на
входную страницу, показанную на рис. 4-20.
Таким образом, мы не сообщаем посетителю, допустил ли он ошибку при
вводе идентификатора или пароля, а просто информируем его о необходимос-
ти регистрации.
Форма регистрации новых посетителей показана на рис. 4-22.
'3 Подключение пользователя - Miciosofl Internet Enploi... И '

Регистрация новых покупателей

Псиц отмеченные символом , нужно залопнятъ обзгэатепы-

1
[Иванов Иван Иванова

Идентификатор

Пароль

Подтверждение пароля

Надионапьньш язык | Русский

Адрес E-Mail i vari о v е m ei l_s tv. ru

{1111 11, Москва уп Пер

Извещения о нсльо товарах I Не посылать

Сбросить

в] Done

Рис. 4-22. Форма регистрации нового покупателя


Здесь посетителю предлагается ввести такие сведения, как полное его имя,
выбранный идентификатор и пароль, язык, на котором следует отображать стра-
ницы магазина, адрес электронной и обычной почты, а также ответить на вопрос,
хочет ли он получать рекламные извещения о появлении в магазине новых то-
варов.
Как мы уже говорили раньше, при описании полей таблицы clients, содер-
жащей сведения о посетителях, поля идентификатора пользователя и электрон-
ного адреса сделаны уникальными. Если при регистрации посетитель укажет
идентификатор или адрес электронной почты, введенный ранее другим посети-
телем и хранящийся в базе данных, он получит сообщение об ошибке с предло-
жением повторить регистрацию. Форма с этим сообщением показана на рис. 4-23.
Базы данных в Интернете. Практическое руководство

"3 hllp //saluin/BookShopClietH/iegmake.aJp - Microsoft I..

Ошибка при за полней ни формы


Польз оьатет с таким идентификатор ом и адресом E-Mail уже
зарегистрирован.
Выберите другой идентификатор

Повторить регистрацию

=у, L DM! Hianel

Рис. 4-23. Сообщение об ошибке при регистрации


В случае успешной регистрации посетители увидит на экране другое сообще-
ние — приглашение для входа (рис. 4-24).

Входите!

^.h Local ritianei

Рис. 4-24. Приглашение для входа


Щелкнув ссылку Входите!, посетитель вновь попадет на начальную страни-
цу, показанную на рис. 4-20. Теперь ему нужно ввести свой идентификатор и
пароль, а затем щелкнуть кнопку Вход.
Выполнив описанные выше действия, покупатель наконец попадает на глав-
ную страницу магазина, состоящую из трех фреймов (рис. 4-25). Левый пред-
ставляет собой меню команд, состоящее всего из двух строк — Выход и Опла-
тить покупки. Верхний фрейм содержит список книг, имеющихся в продаже, а
в нижнем фрейме отображается содержимое корзины покупателя. При первом
посещении магазина корзина пуста.
Заметим, что хотя фреймы и упрощают создание и сопровождение страниц
сервера Web, некоторые браузеры (особенно устаревшие) испытывают трудно-
сти при отображении фреймов. В реальном проекте Интернет-магазина лучше
отказаться от применения фреймов, но мы их используем для простоты изложе-
ния материала и сокращения объема листингов.
Как видно из рис. 4-25, в правом верхнем фрейме отображается информация
из таблицы books, описанной нами ранее. Эта информация вводится и редакти-
руется сотрудниками Вашего магазина при помощи административного прило-
жения, которое мы рассмотрим далее.
Справа от названия книги, под ее стоимостью находится ссылка, щелкнув
которую покупатель добавит книгу в свою корзину. Список отобранных книг
появится в правом нижнем фрейме (рис. 4-26).
Глава 4. Связь приложений с базами данных через ADO

г
•Ц 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 див работы с базами даншп

Скотт Хнлайер, Д'эннэл Ми~нк Програмплфовлнне Active 40 y e


Sei~vei Pages ЕГ^ложкть Е
Русская редакция jgcpjjgjjg
J
Аннотация:
ЭтакнигапосвящвнатеинопогииАЗР ..
d

Вы отобрали для покупки


;Js) LoctfintFar^

Рис. 4-25. Страница, которая отображается при первом посещении магазина

иишниянниидм ВБ
Асй^^Шр..-^^^.^!^^^-^^^, _ _ _ _ _ _ _____ _ d -*Go

Выход Сегодня в продаже


Оплатить локуаки
А.В Фролов. Г.В. Фролов. Создание приложений ( u;n.ii>ni 1
;SOye
:дакных для Интернет и ннграсетен прякшчегкое
Положить в
корзин у
{Русская редакция

Аннотация
Эта.ншгапредстаэгиет с обой практическое руководство по созданию прьшожений W&b с
базами данных для Интернета, а также для корпоративной интрасетк. Б ней рас смотрены
современные технологии, созданные Micros oft для раНсты с ба^аыидайнъи..

Скотт Хнллнер, Дэшпл Мшнк. Программирование Active 40 у.е.


Server Pages
Русская редакция

\ АН нотация.
Ш ТЪ В;

|Эта книга посвшцена тгянопогии ASP.. -;


....

Вы отобрали для покупки


А.Е. Фролов. Г.В. Фролов. Сотдание приложений с ба~лмп !
80у.е
данных для Интернета и н н г р л с е т е й практичегкое
Удалить «з '
руководство
[корзины
^Русская редакция

Скотт Хиляйер, Д-»ннэл КЬпнк. Прогрпм^пфованне Activ* ЙОу.е.


Server Pages Удалить из

•gj hlEp;//3aiurn/BookShcfCfe ^^е1а!р?ГО»ВШП5Т=Ь ' 1 ^ La-afmbanel

Рис. 4-26. Список книг, которые покупатель отобрал в свою корзину


152 Базы данных в Интернете. Практическое руководство

Ссылка Удалить из корзины позволит посетителю отказаться от покупки той


или иной книги.
Вы можете рассчитать стоимость книг с учетом доставки, а также позволить
посетителю указывать количество приобретаемых книг. Стоимость доставки
определяется автоматически, если Вы добавите в базу данных магазина табли-
цу с информацией о стоимости доставки для различных регионов, а при регис-
трации посетитель выберет из списка город и страну проживания. Не забудьте
также учесть способ доставки (поездом, самолетом, курьером и т. д.).
Кроме того, стоит создать страницу, на которой пользователь мог бы редак-
тировать некоторые или все поля регистрационной информации.
Ссылка Оплатить покупки позволит посетителю оплатить покупку или сде-
лать предварительный заказ. Мы рассмотрим соответствующие процедуры поз-
же в этой главе, а сейчас опишем исходные тексты страниц ASP приложения
покупателя.
Файл global.asa
В корне виртуального каталога BookShopClicnt, созданного для страниц клиен-
тского приложения, необходимо разместить файл global.asa (листинг 4-6).
Листинг 4-6 Вы найдете в файле ch4\BookShopClient\global.asa на прилагае-
мом к книге компакт-диске.
В начале этого файла мы поместили ссылку на библиотеку типов ADO, нуж-
ную для использования констант в серверных сценариях ASP:
<!- METADATA TYPE="typelib"
FILE="d:\program files\common files\system\ado\msado20.tlb" ->
Проверяя работу нашего приложения, отредактируйте путь, указанный в
параметре F I L E тега M E T A D A T A , таким образом, чтобы он указывал на файл
msado20.tlb Вашего сервера Web.
Страницы входа и начальной регистрации
Несколько страниц приложения покупателя предназначены для аутентифика-
ции посетителей и для регистрации новых посетителей. Они взаимосвязаны, и
поэтому мы будем рассматривать их имеете.
Вход зарегистрированных посетителей
Форма входа зарегистрированных покупателей, показанная на рис. 4-20, опре-
делена в файле default.asp (листинг 4-7).
Листинг 4-7 Вы найдете в файле ch4\BookShopClient\default.asp на прилагае-
мом к книге компакт-диске.
Вы должны настроить свой сервер Web таким образом, чтобы при просмот-
ре виртуальных каталогов пользователю отправлялся файл с именем default.asp.
Кроме того, следует запретить посетителям прямой просмотр содержимого вир-
туальных каталогов сервера Wei).
Изучая листинг 4-6, Вы можете заметить, что в нем находятся две формы.
Первая форма предназначена для входа зарегистрированных покупателей:
<form ACTION="enter.asp" METHOD="post" TARGET="_top">
<*12>Вход для покупателей</Г12>
<table BORDER="0" CELLPADDING="5" CELLSPACING="0">
Глава 4. Связь приложений с базами данных через ADO 153

<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 Базы данных в Интернете. Практическое руководство

На следующем этапе сценарий создает объект C o m m a n d , необходимый для


выполнения хранимой процедуры аутентификации посетителя ClientLogin:
var cmd = Server.CreateObject("ADODB.Command");
cmd.CommandText = "ClientLogin";
cmd.CommandType = adCmdStoredProc:
cmd.ActiveConnection = connect;
Хранимая процедура C l i e n t L o g i n имеет два входных параметра (идентифи-
катор и пароль посетителя) и один выходной (результат аутентификации).
Входные параметры определяются в объекте Command следующим образом;
cmd.Parameters.Append(cmd.CreatePiirameter(
"User", adVarChar, adParamlnput, 50, sUser));
cffld.Parameters.Append(cmd.CreateParameter(
"Pass", adVarChar, adParamlnput, 50, sPassword));
Имена параметров указаны как User и Pass. Это текстовые строки, поэтому
тип параметров мы указываем как a d V a r C h a r . Константа a d P a r a m l n p u t определяет,
что параметры User и Pass являются входными. Длина текстовых строк, переда-
ваемых через данные параметры, не превышает 50 байт. И наконец, значения
параметров берутся из переменных sUser и sPassword.
Выходной параметр определяется аналогичным образом, но с применением
константы a d P a r a m O u t p u t :
var ParamOut = cmd.CreateParameter(
"Rights", adVarChar, adParamOutput, 50, " ");
После формирования параметров они добавляются к команде метолом Append,
после чего команда выполняется при помощи метода Execute:
cmd.Parameters.Append(ParamOut);
cmd. ExecuteO;
Если в процессе создания соединения с источником данных, формирования
команды и ее параметров, а также при выполнении команды не возникло ника-
ких ошибок, соединение закрывается методом Close:
connect.Close();
Строго говоря, можно не закрывать соединение подобным образом, так как
после того как пользователь обратится к другой странице, оно будет автомати-
чески закрыто. Однако это может произойти не сразу (по у м о л ч а н и ю через
20 минут), поэтому для освобождения ресурсов сервера SQL Server мы закры-
ваем соединение явным образом.
Далее мы проверяем значение, полученное от хранимой процедуры Client-
Login через параметр P a r a m O u t .
Если аутентификация пользователя прошла успешно, в этом параметре бу-
дет находиться значение, отличное от n u l l . В этом случае мы сохраняем в пере-
менных сеанса результат аутентификации, признак успешной идентификации и
идентификатор пользователя, а затем передаем управление странице main.asp:
if(ParamOut.value != null)

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

Рис. 4-27. Сообщение об ошибке при соединении с источником данных


Другое сообщение появится в случае, если Вы забудете назначить права до-
ступа к хранимой процедуре:
[enter.asp]
[-2147217911][Microsoft][ODBC SQL Server Driver][SQL Server]EXECUTE permission
denied on object 'ClientLogin1, database 'Bookstore', owner T dbo'.
Кстати, если мы вовсе опустим обработку ошибок на странице enter.asp, то
ошибка все равно будет обработана, но уже системой интерпретации страниц
ASP. При этом в случае, например, отсутствия прав на выполнение хранимой про-
цедуры в окне браузера посетителя появится сообщение, показанное на рис. 4-28.
•Mtf)://tatiwi/BookStaipGliirit/anlBi-Mp Microsoft I .. РЦЙЦЗ

iress MS] Nip.//saiufn/SookShopCKent/enief.asp

-I
Microsoft! OLE DB Provider for ODBC Drivers error 1 80040tQ9 1

[Microsoft][ODBC SQL Server Drlver][SOL Server|EXECUTE


permission denied on object'ClientLogin', database
'Bookstore', owner 'dbo'.

/BookSfiopClientienler.asp, line 29

Рис. 4-28. Сообщение об ошибке, отображаемое интерпретатором ASP


Хотя оно содержит даже больше информации, чем нужно для отладки стра-
ницы (дополнительно отображается номер строки, в которой произошла ошиб-
ка), пользователю оно будет непонятно. Мы можем рекомендовать пользовате-
лю выполнять те или иные действия в зависимости от логики работы приложе-
ния или от того, где возникла ошибка, а также в ряде случаев полностью лик-
видировать ее последствия незаметно от пользователя.
Хранимая процедура ClientLogin
Теперь мы расскажем Вам о том. как устроена хранимая процедура C l i e n t L o g i n ,
которая вызывается из сценария страницы cnter.asp и выполняет аутентифика-
цию пользователей приложения посетителей магазина.
Глава 4. Связь прилджений с базами данных через ADO 157

Исходный текст хранимой процедуры ClientLogin приведен в листинге 4-10.


Листинг 4-10 Вы найдете в файле ch4\BookShopScripts\dbo.ClientLogin.PRC на
прилагаемом к книге компакт-диске.
Процедура C l i e n t L o g i n имеет два входных параметра @User и @Pass и один
выходной @Rights:
CREATE PROCEDURE ClientLogin §User varchar(50}.'«Pass varchar(50), ©flights
varchar(16) output AS

SELECT @Rights=UserID FROM clients WHERE UserID=@User AND Password=@Pass


UPDATE clients SET LastLogin=GETOATE() WHERE UserID=@LJser
С помощью оператора SELECT процедура выбирает идентификатор пользова-
теля из столбца UserlD таблицы clients так, чтобы идентификатор, записанный
в этом столбце, совпадал с содержимым параметра @User. Кроме того, проверя-
ется совпадение пароля пользователя (хранящегося н столбце Password) и содер-
жимого переменной @Pass.
Если в результате выполнения такого запроса в таблице clients найдена под-
ходящая запись, считается, что аутентификация прошла успешно. С помощью
оператора UPDATE процедура ClientLogin обновляет поле LastLogin найденного
пользователя, записывая в него дату и время подключения, полученные от фун-
кции GETDATE.
Определение фреймов главной страницы
После успешной аутентификации страница enter.asp загружает в окно браузера
посетителя страницу определения фреймов приложения покупателя с именем
main.asp. Исходный текст этой страницы, подготовленной при помощи Microsoft
FrontPage, Вы найдете в листинге 4-11.
Листинг 4-11 хранится в файле ch4\BookShopClient\main.asp на прилагаемом к
книге компакт-диске.
С помощью тегов <FRAMESET> и <FRAME> мы определили три фрейма с имена-
ми left, rtop и rbottom.
<frameset cols="150,*">
<frame name="left" scrolling="no" noresize target="rtop" src="toc.asp">
<frameset rows="57X,*">
<frame name="rtop" target="rbottom" src="booklist.asp">
<frame name="rbottom" src="order.asp?FIRST=1">
</frameset>
<noframes>
<body>
<р>Для посещения нашего сервера нужен браузер,
способный работать с фреймами.</р>
</body>
</noframes>
</frameset>
Фрейм left загружается страницей toe.asp, содержащей команды приложения
покупателя — Выход и Оплатить покупки. Фрейм rtop используется для отобра-
жения списка книг, имеющихся в продаже, и загружается страницей booklist.asp.
И наконец, фрейм rbottom используется для показа содержимого корзины с
158 Базы данных в Интернете. Практическое руководство

книгами, отобранными посетителем для покупки. Он загружается страницей


order.asp с параметром FIRST, равным единице (о назначении этого параметра Вы
узнаете позже при изучении исходного текста страницы order.asp).
Тег <NOFRAMES> используется для формирования строк HTML, отображаемых
при загрузке данного документа в окно браузера, не способного работать с фрей-
мами. Здесь мы просто сообщаем пользователю, что ему нужно обновить его
браузер.
Чтобы исключить возможность прямой загрузки страницы main.asp в обход
страницы аутентификации покупателей, мы включили в исходный текст этой и
других страниц приложения покупателя файлы headcr.asp и footer.asp — при
помощи оператора #include, доступного в страницах ASP:
<Х@ LANGUAGE = "JScript" %>
<!- ffinclude file="header,asp" ->
<html>
<head>
<title>KHtDKHbm магаэин</1:Ше>

</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

Страница просмотра списка книг


На странице просмотра списка книг, расположенной в файле booklist.asp (лис-
тинг 4-13) посетитель может отобрать понравившиеся ему книги. Эта страница
формируется из таблицы books, содержимое которой готовится сотрудниками
Интернет-магазина с помощью административного приложения.
Листинг 4-13 хранится в файле ch4\BookShopClient\booklist.asp на прилагае-
мом к книге компакт-диске.
Особенностью сценария, расположенного на этой странице, является работа
с набором записей Recordset, полученным в результате выполнения хранимой
процедуры.
В начале своей работы сценарий создает соединение с источником д а н н ы х и
открывает его. Эта операция выполняется точно таким же образом, как и в толь-
ко что рассмотренном сценарии на странице enter.asp:
connect = Server.CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");
Теперь нужно запустить на выполнение хранимую процедуру, возвращающую
набор записей из таблицы books. Вместо того чтобы создавать команду, опреде-
лять ее параметры и запускагь методом Execute, как это делалось раньше, мы
используем другой метод для запуска хранимой процедуры.
Набор записей создается янным образом при помощи метода CreateObject
объекта Server:
rs = Server.CreateObject("ADODB.Recordset")
Далее мы его открываем, указыная методу Open объекта Recordset имя выпол-
няемой хранимой процедуры ListBooks, соединение с источником данных c o n -
nect и другие параметры:
rs.Open("ListBooks", connect,
adOpenForwardOnly, adLockReadOnly, adCmdStoredProc);
Константа a d O p e n F o r w a r d O n l y , передаваемая методу Open в качестве третьего
параметра, определяет для открываемого набора записей тип курсора, предназ-
наченного для просмотра записей только в одном (прямом) направлении.
Константа a d L o c k R e a d O n l y сообщает ADO, что полученный набор записей
изменяться не будет, поэтому его не нужно блокировать.
И наконец, константа a d C m d S t o r e d P r o c , передаваемая методу Open через по-
следний параметр, указывает, что первый параметр метода задает имя хранимой
процедуры, подлежащей выполнению.
После получения набора записей мы проверяем свойство EOF. Если оно хра-
нит значение True, это означает что полученный набор записей пуст. В этом слу-
чае вместо списка книг в верхнем правом фрейме появляется сообщение «Нет1
записей»:
if(rs.EOF)
{%>
<TRXTD COLSPAN=4 ALIGN="CENTER">[HeT записей]</ТО></ТР>
<*}
else {...}
1 60 Базы данных в Интернете. Практическое руководство

Если же записи есть, мы запускаем цикл формирования отчета в виде табли-


цы, содержащей поля извлеченного набора записей.
В цикле мы последовательно просматриваем строки набора записей, переме-
щаясь по ним при помощи метода MoveNext, определенного в объекте Recordset.
Условием завершения цикла является равенство свойства rs, EOF значению Т rue:
<|12>Сегодня в продаже</Ь2>
<TA'BLE BORDER=1>
<%
while (Irs.EOF)
!
X>
<tr><td><.b><X=rs.Fields("Author")a:>. <X=rs.Fields("Title"))!></b>
<brxixx=rs.Fields( "Publisher" )xx/ix/td>
<tdxX=rs.Fields("Price")X> y.e.<br>
<a href="order.asp?ID=<K=rs.Fields("booksID")X>&FIRST=0"
taгget="гbottom">Гloлoжить в корзину</А>
</td><tr>
<tr><td со!зрап=2>Аннотация: <br>
<smallxx=rs.Fields( "Annotation" )XX/smallx/tdxtr>
<X
rs. Move Next О ;
!
rs. Close();
:
connect. Close();
}
catch (ex)

%>
</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

Когда обработка набора записей закончена, мы должны закрыть этот набор


методом Close объекта R e c o r d s e t :
rs.Closet);
Вслед за этим надо закрыть и соединение с источником данных, вызывая
метод Close объекта C o n n e c t i o n :
connect.Close();
Исходный текст хранимой процедуры ListBooks, запускаемой на выполнение
серверным сценарием страницы booklist.asp, представлен в листинге 4-14.
Листинг 4-14 Вы найдете в файле ch4\BookShopScripts\dbo.ListBooks.PRC на
прилагаемом к книге компакт-диске.
Эта процедура очень проста. С помощью оператора SELECT она выбирает не-
сколько полей из таблицы books:
CREATE PROCEDURE ListBooks AS
SELECT booksID,Author, Title, Publisher, Price, AddDate,Annotation
FROM books
В результате ее работы создается набор записей, из которого формируется
таблица со списком книг, имеющихся в продаже.
Добавление книги в корзину
Для того чтобы добавить книгу и свою корзину, покупатель щелкает ссылку
Положить в корзину, расположенную в строке с названием этой книги. Об этой
ссылке мы рассказывали в предыдущем разделе: она вызывает страницу order.asp
с параметрами ID и FIRST.
На самом деле страница order.asp выполняет двойную функцию. Когда мы
вызываем се с параметром FIRST, равным 1, она просто отображает содержимое
корзины покупателя. Если же значение этого параметра равно 0, перед таким
отображением происходит добавление выбранной книги в корзину.
В следующем разделе мы рассмотрим выполнение и той, и другой операции.
Страница просмотра содержимого корзины
Особенностью страницы просмотра содержимого корзины является то, что она
может выполнять одно или два обращения к базе данных в зависимости от зна-
чения параметра FIRST. Если этот параметр равен 0, сценарий вначале запуска-
ет хранимую процедуру AddToOrder, добавляющую выбранную книгу к корзине,
а затем процедуру L i s t O r d e r s , предназначенную для просмотра содержимого
корзины.
Рассмотрим исходный текст страницы order.asp, представленный в листин-
ге 4-15.
Листинг 4-15 Вы найдете в файле ch4\BookShopClient\order.asp на прилагае-
мом к книге компакт-диске.
Для обработки ошибок, возникающих при обращениях к базе данных, мы
предусмотрели единый блок catch. Поэтому блок t r y также один.
Сразу после того, как страница получает управление, сценарий получаст
идентификатор пользователя и сохраняет его в локальной переменной с именем
ClientlD.
var ClientID=Session( 1 'UserID");
162 Базы данных в Интернете. Практическое руководство

Этот идентификатор понадобится для отбора тех записей в таблице orders,


которые имеют отношение к данному покупателю.
Далее сценарий анализирует содержимое переменной сеанса с именем FIRST.
Если оно не равно 1, то считается, что страница вызвана для добавления новой
книги в корзину покупателя.
В этом случае мы извлекаем идентификатор ID добавляемой книги и сохра-
няем его в локальной переменной booksID, а затем устанавливаем соединение с
источником данных обычным способом.
Затем мы создаем, команду cmd как объект Command и готовим параметры для
запуска хранимой процедуры A d d T o O r d e r . Этой процедуре нужно передать два
входных параметра — идентификатор добавляемой книги booksID и идентифи-
катор покупателя ClientID, добавившего книгу. После этого команда запуска-
ется на выполнение методом Execute:
if(Request("FIRST")(1> != 1)

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

В любом случае нужно подготовить команду запуска хранимой процедуры


L i s t O r d e r s , возвращающую набор записей для книг, отобранных данным посе-
тителем магазина. Эта процедура принимает только один параметр — идентифи-
катор посетителя ClientlD.
Запуск процедуры выполняется с помощью метода Execute:
cmd = Server. CreateObjectC'ADQDB. Command");
cmd.CommandText = "ListOrders";
cmd. CommaridType = adCmdStoredProc;
cmd.ActiveConnection = connect;

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 Базы данных в Интернете. Практическое руководство

На странице order.asp вызываются две хранимые процедуры с именами AddTo-


Order и ListOrders. Расскажем о том, как они работают.
Исходный текст процедуры AddToOrder, предназначенной для добавления
новых книг в корзину покупателя, показан в листинге 4-16.
Листинг 4-16 Вы найдете в файле ch4\BookShopScripts\dbo.AddToOrder.PRC на
прилагаемом к книге компакт-диске.
В рамках этой процедуры последовательно выполняются обращения к двум
таблицам — к таблице client, содержащей сведения о посетителях, и к таблице
o r d e r s , в которой хранятся личные корзины покупателей.
Сначала процедура A d d T o O r d e r должна определить идентификатор записи в
таблице clients, соответствующий идентификатору покупателя, передаваемому
в процедуру через входной параметр @ClientID.
Это задачу решает первый оператор SELECT. Найденный идентификатор со-
храняется в переменной emClientID тина I N T и затем используется при добавле-
нии записей в таблицу o r d e r s :
CREATE PROCEDURE AddToOrder §booksID varchar(SO),
e>ClientID varchar(SO) AS

DECLARE ФпСНепШ INT


SELECT @nClientID=clients.ClientID FROM clients
WHERE UserID=@ClientID
Второй оператор SELECT определяет стоимость выбранной книги по ее иден-
тификатору и записывает в переменную §bookPrice:
DECLARE ©bookPrice MONEY
SELECT @bookPrice=Price FROM books WHERE booksID=@booksID
Оператор INSERT добавляет в таблицу o r d e r s новую запись, заполняя поля
идентификатора выбранной книги b o o k s I D , идентификатора покупателя
C l i e n t I D , поместившего книгу в корзину, и стоимости книги bookPrice:
INSERT orders (booksID, ClientID, bookPrice)
VALUES(@booksID, @nClientID,@booKPrice)
Заметим, что стоимость книги легко получить по ее идентификатору из таб-
лицы books, поэтому нет никакой необходимости хранить ее в таблице orders.
Мы создали такую избыточность, для того чтобы облегчить получение общей
суммы отобранных книг, которая потребуется на этапе оплаты товара.
Исходный текст хранимой процедуры ListOrders Вы найдете в листинге 4-17.
Листинг 4-17 хранится в Файле ch4\BookShopScripts\dbo.ListOrders.PRC на
прилагаемом к книге компакт-диске.
Она получает единственный входной параметр @ClientID — идентификатор
покупателя, содержимое корзины которого необходимо извлечь из таблицы
orders.
Эта операция выполняется в два приема.
Вначале процедура L i s t O r d e r s получает идентификатор записи покупателя,
обращаясь к содержимому переменной @ClientID. Этот идентификатор сохраня-
ется в переменной @nClientID:
CREATE PROCEDURE ListOrders ©ClientID varchar(SO) AS
DECLARE @nClientID INT
Глава 4. Связь приложений с базами данных через ADO 165

SELECT @nClientID=clients.ClientID FROM clients


WHERE UserID=§ClientID
Затем выполняется оператор SELECT, возвращающий информацию о книгах,
заинтересовавших данного посетителя. Она получается с помощью объединения
таблиц orders и books оператором JOIN:
SELECT books.booksID, books.Author, books.Title, books.Publisher, books.Price
FROM orders
JOIN books ON orders.booksID=books.booksID
WHERE orders.ClientID=@nClientID

Удаление книги из корзины


Если посетитель передумал покупать книгу, отобранную в корзину, он может
удалить ее оттуда, щелкнув ссылку Удалить из корзины, расположенную рядом
с книгой. При этом будет загружена страница delorder.asp (листинг 4-18).
Листинг 4-18 Вы найдете в файле ch4\BookShopClient\delorder.asp на прилага-
емом к книге компакт-диске.
Первое действие, выполняемое сценарием сразу после загрузки страницы, —
получение из переменной сеанса UserlD идентификатора посетителя и сохране-
ние этого идентификатора в переменной ClientID:
var ClientID=Session("UserID");
Далее сценарий запускает хранимую процедуру D e l O r d e r , передавая ей два
параметра:
connect = Server.CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");
var cmd = Server.CreateObject("ADODB.Command");
cmd.CommandText = "DelOrder";
cmd.CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;

cmd.Parameters.Append(cmd.CreateParameter("ClientID", adVarChar, adParamlnput,


50, ClientID));
cmd.Parameters.Append(cffld.CreateParameter("bookIO", adVarChar, adParamlnput,
50, Request("ID")(1)));
cmd. ExecuteO;
Первый параметр с именем ClientID хранит только что упомянутый иденти-
фикатор посетителя, удаляющего книгу из своей корзины. Второй параметр
называется bookID и передается в страницу delorder.asp из страницы order.asp. Он
содержит идентификатор удаляемой книги.
После удаления книги из корзины нужно обновить правый нижний фрейм,
отображающий содержимое корзины покупателя. Мы это делаем с помощью
метода Redirect объекта Response:
Response.Redirect("order.asp?FIRST=1");
Обратите внимание, что при загрузке страницы order.asp мы указываем па-
раметр FIRST, равный единице. Как Вы помните, это значение свидетельствует.
166 Базы данных в Интернете. Практическое руководство

что нужно показать текущее содержимое корзины, не выполняя над ней ника-
ких операций.
Исходный текст хранимой процедуры 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*"

Рис. 4-29. Вход в административное приложение


При самом первом запуске административного приложения база данных ад-
министраторов пуста, в нее автоматически добавляется введенный идентифика-
тор и пароль. Таким образом, кто первый подключился к системе, тот и стал
администратором.
После успешного ввода пароля па экране появляется главная страница адми-
нистративного приложения, показанная на рис. 4-30.
Глава 4. Связь приложений с базами данных через ADO 16

Igg—
*• - •• • *м я™g,™^^gg,^g^
а л j •=> -* _л * f j 'Lrtt eje^s»
AtfteN |t] rvrp /..'-^urn/Boc^hcp/Mr, ЭФ J *>Go

КнШЗ
Добро пожаловать,
ЛоЕ^ттат&гш уважаемый frolov!
Ви подключились ксистгмекак
Адпшсипратор

gJDcni j^ LCI ;al intranet

Рис. 4-30. Главная страница административного приложения


В левом фрейме находится меню административных команд, а в правом —
сообщение об успешном подключении и текущие права пользователя.
Первое, что должен сделать администратор, войдя в систему, — определить
список управляющего персонала магазина. Для этого он должен щелкнуть ко-
манду Управляющий персонал. После этого в правом фрейме появится список
персонала (рис. 4-31).
ижного маназина - Microsoft Intetnel Enploiei piovided by »--

AUrfr^ja W D // S a h.r n flDokShcprt«n«p J г>Г,п

Угщжщошй
Список управляющего персонала
lOflSE
11ия Imua КОМАНДЫ
тмдалючвння

finbv Админисгратор [04.12.591737:40

PBUW ЗмЕдующий ; 04.12.95 17-37:40 ";ы"°т


складом -'дашпт.

iSldMBff МеНеДНМр ^ 041299173740 Ш.ментг


продаж ил
11 ОЕЫЙ СОТ|)^ГГНИ^

4j Done ~-T— ~ %я, Local m'ranei

Рис. 4-31. Список администраторов


После первого запуска в этом списке будет только одна запись — о первом
подключившемся администраторе. Ссылка Новый сотрудник позволит Вам до-
бавить другие записи. Когда Вы щелкнете ее мытью, в правом фрейме появит-
ся форма регистрации нового сотрудника (рис. 4-32).
Здесь задаются идентификатор и пароль сотрудника, а также его права (или,
если хотите, функциональные обязанности).
Для добавления имени сотрудника в базу данных щелкните кнопку Создать.
Ссылка Изменить, расположенная напротив каждой записи в списке персо-
нала, позволяет отредактировать идентификатор, пароль и права сотрудника.
Соответствующая форма показана на рис. 4-33.

7 Зак. 3571
168 Базы данных в Интернете. Практическое руководство

]Ъ Администрирование книжииш мэназнна - Microsoft Internet Ex.. Я|*1Е1

l
Address |jJ] http://!alLWBookShcp< main.e5p

Новый администратор
Имя (admin

Пароль 123

Права [Всепрвва

Создать

Done

Рис. 4-32. Добавление записи о новом администраторе


•а Ад м ни нет рн ров а нив книжного м<)н^'>ннп • Micrusolt Interne! Explorer p.

Address№] Htp.J'^lLir,/Bo^S-,ir;'

Редактирование записи
Книги
Пзрпттатдлк
пользователя admin

Имя jadmin

Параш, |Тгз
п .
'
Сохранить изменения

fli Local ritian«

Рис. 4-33. Редактирование учетной записи сотрудника


После внесения изменений щелкните кнопку Сохранить изменения, и они
будут записаны в базу данных магазина.
Ссылки Удалить в списке сотрудников предназначены для удаления учетных
записей.
Если щелкнуть в левом фрейме ссылку Книги, на экране появится список
книг, имеющихся в продаже (рис. 4-34).
Первоначально этот список пуст. Чтобы добавить в него название книги,
щелкните ссылку Новая книга. На экране появится форма, предназначенная для
добавления новой книги (рис. 4-35).
Здесь необходимо указать автора к н и г и , ее название, издательство, стоимость
и текст аннотации. Для того чтобы добавить книгу в базу данных, следует щел-
кнуть кнопку Добавить.
В любой момент можно отредактировать описание книги, хранящееся в базе
данных магазина. Для этого достаточно в списке книг щелкнуть ссылку Изме-
нить. В правый фрейм будет загружена страница с формой для редактирования
сведений о книге (рис. 4-36).
Глава 4. Связь приложений с базами данных через ADO 169

ч* Администрирование книжного ианазннл - Microsoft Internet ffuplorer provided by www auidmedr. HFSTB'
look, ,Ци|р

Link* #]BookShcp 0] Book Shop Cfer*

:i] hllp. //sat WBookS hop /main, asp

Управляющий Список книг


К НЕГИ

iA.B, Фролов, Г.В. Фролов


Сотд«1Ште гцжло^кешя! ( башми данных для
1[нт»рнета н интрасетей: практическое
руководство
Русская редакция
Цена. 80у.е.
Дата обновления: 04 12.99 S.41'20
Аннотация:
Эта взшг» представляет со Бой практическое рукоюдстео по созданию
'прнпоясений Web с базами данных для Интернета, а также для корпоративной
iHBTpecem. Е ней рассмотрены совремшные типологии, созданные Microsoft дня
работы с базагда данных .

Скотт ХылайРр, Дэниэл ЬЬпш,


П1>ог[>»ммн[1оваю1« Active Server Pages
Изменить
Рус екая редакция
Цена1 40у.е.
Дата обновления: 04.12.9? 18.03^7
'Аннотация.
•Эта книга посвящена теоюпогин ASP

[Новая книга'

Рис. 4-34. Просмотр списка книг, имеющихся в продаже

Админнстрированне книжного манаэина - Microsoft Inlernel Enplorer provided by «HW.auramedia.ru W

. Addiets|g] Wt3A';a!um^ookSliop' J rnairia5p J^j r'Go

i_^_ 'Ь^лщщш
Новая к ннга
ЬГтпи
Автор Скотт Хилайер. Дэниэг Мизик
Название Программирование Active SetrvcE Расгеэ

Издатель Русская редакция _»]


Цена, у е. 5о
Аннотация Эта. гниг-а посвящена технологии 15?.. .

Добаеигь |

[Й -Done НЁ1-веайййап«
• ПЕЭ

Рис. 4-35. Добавление в список названия книги


о Базы данных в Интернете. Практическое руководство

3 Администрирование К1ШЖНОГО маназннэ - Microsoft Internet Explorer provided by rmw auiamedia.ru ИМ 13 1


Уе £dit View Favq*iles Taote Help

;*d^|ehBpv/ 1 * u .^o*sho:w-rMr* ! > j .^GO


i*
У пр авдяощи и
Редакти] ювание сведений о книге
Енигщ
Пскщатели Актер А В . Ф р о п о в . Г В Фролов

Названы* Создание приложений с базами данных для Интернета j*j ;


и интрасетей: практическое руководство

Издатель Русская редакция _^J

Ц|рна, у.е. 80

.Аннотация Эта книга представляет собой практическое _^


руководство по созданию приложений Veb с базами
паннык д^я Интернета, а также для корпоративной
иитрасети. Б ней рассмотрены современные
технологии, созданные n i c c o s o f c %ля работы с _т|

Сохренить изменения

1 >1^
^J Done 1

Рис. 4-36. Редактирование сведений о книге


Чтобы обновить содержимое базы данных после редактирования полей этой
формы, щелкните кнопку Сохранить изменения.
И наконец, чтобы удалить книгу из базы данных, достаточно щелкнуть ссыл-
ку Удалить напротив названия книги в списке просмотра, показанном на рис. 4-34.
Команда Покупатели, расположенная в левом фрейме страницы админист-
ративного приложения, открывает доступ для просмотра таблицы посетителей
client и редактирования некоторых ее полей. Если щелкнуть эту ссылку, на эк-
ране появится форма, позволяющая задать критерии поиска клиентов (рис. 4-37).
Чтобы сократить объемы листингов в книге, мы расскажем здесь лишь о трех
важнейших критериях — идентификаторе покупателя, по которому он «входит»
в магазин, адресе его почты и дате регистрации. Время регистрации задается с
помощью двух календарей, описанных в главе 2.
Если оставить поля идентификатора пользователя и электронного адреса
незаполненными, щелчок ссылки Поиск приведет к появлению в правом фрей-
ме списка всех покупателей, зарегистрированных в указанный период времени,
Если, кроме того, снять отметку с переключателя искать по дате регистрации:,
будет составлен список всех зарегистрированных покупателей.
Так как поиск записей посетителей выполняется с применением оператора
сравнения LIKE, в полях Идентификатор покупателя и Адрес E-Mail можно
использовать символы шаблонов, стандартные для языка Transact-SQL: K[]~_.
На рис. 4-38 показан полный список зарегистрированных покупателей, полу-
ченный с помощью только что описанной формы поиска.
Командой Изменить Вы можете отредактировать некоторые параметры ре-
гистрации выбранного пользователя (рис. 4-39).
Глава 4. Связь приложений с базами данных через ADO 171

'Э Администрирование «нижнего ман-азина - Microsoft Inlemel Euplorei provided by wvm.amamediiiu


routes Jooh

-I-***
s ig] http /AalurtVBookS hop/main asp

Просмотр списки зарегистрированных покупателей

Идентификатор похупатгля:

Адрес E-Mail:

R искать по дате регистрации

Начальная дата Конечная дата

ЯнЕ-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. Форма для поиска зарегистрированных покупателей

!> gda view Favorites Jacte Hdp D M


,^f -^ ' f T>I -Links gJBookStop e]Boc*ShcpO«rt ,
1
AjJ*ess|Sj hllp ^satuin/Boi* Shop/mail asp _^J jVQo

Управляь-.ший
]1окупатели
арегистрироБвпис! в любое греми
'днпификатор *4, ЕМай; %

Нжмпй>
Ш

•DB " ^"vJSU" Ajqiec E-Mail Команды

.Изменить
Апекса "ЧР n ' !
"
J ftolovTgglastiel ru Корзина
Фропш !2Q:23:43

Изменить
Q5 12 9S
John JohnSt от Корзина
120253
1 . . . •Удалить

ittnur JT '°M299 p tlto «@ s ome_mal.tu S^*'


12 02 13

РИД 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

Рис. 4-38. Полный список зарегистрированных покупателей


172 Базы данных в Интернете. Практическое руководство

Щ Администрирование книжного мэназина • Micmsoft Inleinel Enploi . В«яЛЭ|

швсонад Редактирование учетной запит


KHIJII
Покупатели
посетителя "sirtor"

Идентификатор sidor

Пароль

Язык 'Анппсюий"
Деньги "О"
Дата регистрации 03.12.99 И 45:12
Агфес IP при регистрации 172.21.1 10

Адрес Email sidar@somemail some_

Обновить

jb| Lotdrtranet

Рис, 4-39. Редактирование параметров регистрации покупателя


Для сокращения объемов листингов мы отображаем здесь не все параметры
регистрации. При создании реального проекта Вы, однако, можете расширить
набор редактируемых параметров, добавив в него все остальные поля таблицы
clients.
Команда Корзина позволяет сотруднику магазина просмотреть содержимое
корзины выбранного пользователя (рис. 4-40).
Э Администрирование книжного маназина - Microsoft Inlet net Enplorei piovided by wwH.durflinedia.iu B[i|f3

Содержимое коряшы покупателя frolov

А-В. Фролов. Г.В. Фролов. Оспданне щщложв'иий с бячами


данных для Интернета пинтрягетеп: практическое
[руководство
\Русская редакция

Скотт Хилапер, Дэшпл ЬЬпнк. Црогрлммнрованне Active


Server Pages
Русская редакция

Рис. 4-40. Просмотр содержимого корзины покупателя


Здесь мы не предусмотрели возможность удаления книг из корзины, так как
считаем, что это должен делать сам покупатель. Что же касается очистки кор-
зин покупателей от старых записей, то для выполнения этой работы стоит пре-
дусмотреть отдельную команду в административном приложении.
Вернемся к списку покупателей.
Глава 4. Связь приложений с базами данных через ADO 173

Ссылка Удалить позволит сотруднику магазина удалить регистрационную


запись выбранного покупателя. Однако такую операцию надо выполнять осто-
рожно. Например, если покупатель отобрал товар в корзину или уже делал по-
купки в Вашем магазине, его, возможно, не следует удалять.
Если щелкнуть ссылку Удалить, на экране появится временная модальная
диалоговая панель с предупреждением (рис. 4-41).

Внимание!

Учетная запись
Jolm Sniitl.
Зуд ет удалена!

Рис. 4-41. Диалоговая панель с предупреждением


Вы можете в этой панели отказаться от удаления, щелкнув ссылку Отменить,
или подтвердить свои намерения при помощи ссылки Удалить.
Если корзина удаляемого покупателя пуста, операция будет выполнена ус-
пешно. Это подтвердит сообщение, показанное на рис. 4-42.

Учетная запись
Joint Smith
успешно удалена
СК

Рис. 4-42. Сообщение об успешном удалении учетной записи покупателя


Если же покупатель отобрал книги в корзину, наше административное при-
ложение откажется удалять его учетную запись, о чем и сообщит администра-
тору (рис. 4-43).

Невозможно удалять активную учетную


запись:

Александр Фролов
ЭК

Рис. 4-43. Сообщение о невозможности удалении учетной записи активного покупателя

Файл global.asa
В корне виртуального каталога BookShop, созданного нами для страниц адми-
нистративного приложения, необходимо поместить файл global.asa. Вы можете
использовать здесь тот же самый файл, что и для приложения покупателя. Его
исходный текст мы уже приводили в листинге 4-6.
Страницы входа
Рассмотрим исходные тексты страниц, предназначенные для входа сотрудников
Вашего Интернет-магазина в административное приложение.
174 Базы данных в Интернете. Практическое руководство

Страница default.asp (листинг 4-20) должна загружаться в окно браузера по


умолчанию, когда сотрудник магазина пытается просмотреть содержимое вир-
туального каталога административного приложения. ^
Листинг 4-20 хранится в файле ch4\BookShop\default.asp на прилагаемом к
книге компакт-диске.
В форме, расположенной на той странице, необходимо ввести идентифика-
тор и пароль, а затем щелкнуть кнопку Вход:
<FQRM ACTION="enter.asp" METHOD="post" TARGET="_top">
<Н1>Добро пожаловать!</Н1>
<TABLE BOROER=0 CELLPADDING=5 CELLSPACINGS
<TRXTD>MMfl</TDXTD>
<INPUT S1ZE=10 TYPE="EDIT" NAME="USFT>
</TDX/TR>
<TRXTD>naponb<ADXTO><nobr>
<INPUT SIZE=10 TYPE="password" NAME="PWD">
<INPUTTYPE="submit" VALUE="Bxofl"></nobr>
</TDX/TR>
</TABLE>
</FORH><ADX/TR>
В отличие от страницы начального входа посетителей здесь нет кнопки ре-
гистрации. Как мы уже говорили, первый, кто подключился к административ-
ному приложению, автоматически регистрируется в нем с полными правами. Б
дальнейшем он сам будет пополнять список персонала.
После щелчка кнопки Вход управление передается странице Enter.asp, вы-
полняющей аутентификацию (листинг 4-21).
Листинг 4-21 Вы найдете в файле ch4\BookShop\Enter.asp на прилагаемом к
книге компакт-диске.
В целом она напоминает аналогичную страницу аутентификации приложе-
ния покупателя, однако есть и отличия.
Серверный сценарий получает идентификатор пользователя и пароль из по-
лей формы с именами USR и PWD. записывая их в переменные sUser и sPassword
соответственно:
var sUser=Request("USR")(1);
var sPassword=Request("PWD"}(1);
Вначале серверный сценарий устанавливает соединение с источником данных:
var connect;
connect = Server.CreateQbjectC'ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");
Далее для выполнения аутентификации вызывается хранимая процедура
M a n a g e r L o g i n , работающая с таблицей managers:
var cmd = Server.CreateObject("ADODB.Command");
cmd.CommandText = "ManagerLogin";
cmd.CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;
Глава 4. Связь приложений с базами данных через ADO 175

cmd . Parameters . Append(cmd , CreateParameter(


"User", adVarChar, adParamlnput, 50, sUser));

cmd. Parameters. Append(cmd.CreateParameter(


"Pass", adVarChar, adParamlnput, 50, sPassword));

var ParamQut = cmd. CreateParameter(


"Rights", adVarChar, adParamOutput, 16, " ");

cmd . Pa ramete rs . Append ( Pa ramOut );


cmd.ExecuteO;
После выполнения этой процедуры результат аутентификации сохраняется
в переменной ParamOut, а права сотрудника, пытающегося подключится к при-
ложению, — в переменной сеанса R i g h t s . Далее выполняется загрузка главной
страницы административного приложения main.asp:
if(ParamOut. value! =" ")
:
Session( "Rights" )=ParamOut. value;
Session("0k")="0k";
Session("UserID")=sUser;
Response. Redirect ("main, asp");
I
Если аутентификация закончилась с ошибкой, в окне приложения появляет-
ся сообщение Доступ запрещен, оформленное в виде ссылки на страницу аутен-
тификации default. asp:
else
i
Session("0k")="";
Session ( "Use rID")="";
Session (" flights" )="";
%>
<HTHL>
<BODY>
<CENTERXH1><a href=default.asp>flocTyn запрещен</а></Н1></СЕМТЕН>
</BOOY>
</HTML>

Главная страница
Главная страница административного приложения содержит определение двух
фреймов (листинг 4-22).
Листинг 4-22 Вы найдете в файле ch4\BookShop\main.asp на прилагаемом к
книге компакт-диске.
В левом фрейме toc.asp находятся команды в виде ссылок на другие страни-
цы, а в правом (с именем m a i n ) отображается результаты выполнения этих команд:
<l- Jfinclude file="header. asp" ->

(см. след, стр.)


1 76 Базы данных в Интернете. Практическое руководство

<frameset cols="201, *">


<frame name="contents" target="main" src="toc.asp">
<frame name="main" src="hello.asp">
<nof rames>
<body>
<р>Для просмотра этой страницы нужен браузер, способный работать'с фреймами.

</body>
</noframes>
</f rameset>

<!- Kinclude file="footer.asp" ->


Обратите внимание на включение в исходный текст страницы файлов hea-
der.asp и footer.asp. Эти файлы такие же, как и в приложении покупателя. Они
предназначены для проверки результата аутентификации и позволяют избежать
прямой загрузки страниц приложения в обход аутентификации.
Страница меню команд
Исходный текст страницы команд toe. asp представлен в листинге 4-23.
Листинг 4-23 Вы найдете в файле ch4\BookShop\toc.asp на прилагаемом к кни-
ге компакт-диске.
Для загрузки страниц, ASP, выполняющих команды, мы применили здесь
функцию клиентского сценария to, составленную на языке JavaScript:
<SCRIPT LANGUAGE=JavaScript>
function to(url)
{
parent. main. document. location. href=
itrl+"?"+Math . random( ) . toSt ring( ) ;
}
</SCRIPT>
Ее единственной задачей является отключение кэширования страниц брау-
зером, что достигается добавлением к адресу URL загружаемой страницы фик-
тивного параметра в виде случайного числа. Этот механизм отключения кэши-
рования мы рассматривали во второй главе нашей книги.
Для просмотра и редактирования списка управляющего персонала страница
toc.asp загружает страницу managers. asp. Редактирование списка книг, имеющих-
ся в продаже, выполняет страница books. asp, а работу со списком покупателей —
страница CustomerSearch.asp:
<а href="javascript:to( 'managers. asp' )">Управляющий персонал</а><Ьг>
<а href="javascript:to( 'books. asp' )">Книги</а><Ьг>
<а href="javascript:to(' CustomerSearch.asp' )">Покупатели</а>

Страница с сообщением о подключении


При первой загрузке главной страницы административного приложения в окно
правого фрейма загружается страница hello. asp, показанная в листинге 4-24.
Листинг 4-24 хранится в файле ch4\BookShop\hello.asp на прилагаемом к кни-
ге компакт-диске,.
Глава 4. Связь приложений с базами данных через ADO 177

В ней содержится приветственное сообщение, включающее идентификатор


сотрудника, работающего с приложением, и его права. Эта информация, опре-
деляемая при аутентификации, извлекается из переменных сеанса с именами
LJserlO и R i g h t s соответственно:
<%
var sRights="";
if(Session("Rights") == "Administrator")
sRights="AflMHHHCTpaTOp";
else if(Session("Rights") == "Sales_manager")
sRights="MeHeflxep по продажам";
else if(Session("Rights") == "sn_manager")
8Р1д^з="Заведующий складом";
else
sRights=Session("Rights");
%>
<п2>Добро пожаловать, уважаемый <X=Session("UserID")X>!</h2>
<р>Вы подключились к системе как <bxx=sRightsKX/b>.

Страницы управления персоналом


Страницы управления персоналом, расположенные в файле managers.asp, позво-
ляют администратору магазина управлять учетными записями сотрудников. Эта
страница используется совместно с тремя другими — ncwmgr.asp, delmanager.asp
и edtmanager.asp, которые отвечают соответственно за создание, удаление и ре-
дактирование учетных записей.
Просмотр списка сотрудников
Просмотр списка сотрудников выполняется на странице managers.asp. Исходный
текст этой страницы Вы найдете в листинге 4-25.
Листинг 4-25 хранится в файле ch4\BookShop\managers.asp на прилагаемом к
книге компакт-диске,
В начале своей работы сценарий, расположенный на странице managers.asp,
запускает хранимую процедуру ListManagers:
var connect;
var rs;
connect = Server.CreateOb]ect("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");

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>&nbsp; <X=sDate)!X/TD>
<TD>
<a href="edtmanager. asp?ID=<!(=sID*l>" target="main">ll3MeHHTb</A>&nbsp;
<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

лучают в качестве параметра идентификатор пользователя ID, извлеченный из


поля Name текущей строки обрабатываемого набора записей rs.
В последнюю строку таблицы сценарий вставляет ссылку Новый сотрудник,
отправляющую администратора на страницу newmgr.asp. Там он сможет создать
учетную запись для нового сотрудника:
<TRXTD COLSPAN=6 ALIGN="CENTER"><a href="newmgr.asp" 1агдет="та1п">[Новый
сотрудник] </а><ЛО><ДН>
Исходный текст хранимой процедуры ListManagers, с помощью которой сце-
нарий managcrs.asp получает список сотрудников, представлен в листинге 4-26.
Листинг 4-26 Вы найдете в файле ch4\BookShopScripts\dbo.ListManagers.PRC
на прилагаемом к книге компакт-диске.
Эта процедура получает содержимое полей Name, R i g h t s и LastLogin таблицы
m a n a g e r s при помощи оператора SELECT и не имеет никаких интересных особен-
ностей:
CREATE PROCEDURE ListManagers AS
SELECT Name, Rights, LastLogin
FROM managers
Создание новой записи
Для создания новой учетной записи сотрудника магазина вызывается страница
newmgr.asp, исходный текст которой приведен в листинге 4-27.
Листинг 4-27 Вы найдете в файле ch4\BookShop\newmgr.asp на прилагаемом к
книге компакт-диске.
На этой странице имеется форма, позволяющая ввести имя и пароль нового
сотрудника, а также выбрать его права из списка:
<fonn ACTION="createmgr.asp" METHOD="post">
<|11>Новый администратор</М>
<table BOROER="0" CELLPADDING="5" CELLSPACING="0">
<trxtd>HMH</td><td>
<input SIZE="16" TYPE="EDIT" NAME="USR">
</tdx/tr><tr>
<td>naporb </td> <td>
<input SIZE="16" TYPE="EDIT" NAME="PWD"> </td>
</trxtr> <td>npaaa</td> <td>
<select NAME="RG" size="1">
<option selected value="Administrator">Bce npasa</option>
<option value="Sales_manager">MeHefl)Kep no продажам</ор^оп>
<option value="sh_manager">3aвeдyющий cioiaflOM</option>
</selectx/td> </tr>
<trxtd>&nbsp; </tdxtd>
<input TYPE="submit" УА1иЕ="Создать"> </td>
</tr>
</table>
</form>
Вы можете указать различные права (они зависят от структуры фирмы), от-
редактировав набор тегов <OPTION> в списке <SELECTION>.
Щелчок кнопки Создать отправляет данные, подготовленные в полях фор-
мы, на страницу createmgr.asp (листинг 4-28). Расположенный там серверный
180 Базы данных в Интернете. Практическое руководство

сценарий добавляет параметры нового сотрудника в таблицу m a n a g e r s базы дан-


ных магазина.
Листинг 4-28 хранится в файле ch4\BookShop\createmgr.asp на прилагаемом к
книге компакт-диске.
Добавление новых сотрудников разрешено только администраторам, поэто-
му в начале своей работы сценарий страницы createmgr.asp проверяет права
пользователя, записанные в переменную сеанса с именем Rights при аутентифи-
кации. Если это администратор, сценарий продолжает свою работу, а если нет, —
выводит сообщение о том, что прав недостаточно:
if(Session("Rights")=="Administrator")
{
connect = Server.CreateObject("ADODB.Connection"};
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", ""};

var cmd = Server.CreateObjectC'ADODB.Command");


cmd.CommandText = "NewMgr";
cmd.CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;

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.Pa ramete rs.Appendf cmd.CreatePa ramete r(


"Rights", adVarChar, adParamlnput, 16, Request("RG"){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

Оиа просто вставляет значения, полученные через параметры @User, @Pass и


©Rights в соответствующие поля таблицы m a n a g e r s :
CREATE PROCEDURE NewMgr @User varchar(50), @Pass varchar(5D),
@Rights varcharC16) AS
INSERT managers (Name, Password, Rights, LastLogin)
VALUES(@User, @Pass, ^Rights, NULL)
Что же касается поля LastLogin, хранящего дату последнего подключения
сотрудника к системе, то в нее записывается значение NULL. Мы это делаем по-
тому, что только что зарегистрированный сотрудник еще ни разу не входил в
систему-
Удаление учетной записи сотрудника
Эта операция выполняется на странице delmanager.asp (листинг 4-30).
Листинг 4-30 Вы найдете в файле ch4\ BookShop\delmanager.asp на прилагае-
мом к книге компакт-диске.
После проверки прав текущего пользователя серверный сценарий, располо-
женный на этой странице, получает со страницы managers. asp идентификатор
удаляемой записи через параметр ID и запускает хранимую процедуру DelMgr:
var rs, connect;
if ( Session ( "Rights" )=="Administ rater")
:
try
(
connect = Server. CreateObject("ADODB. Connection");
connect. ConnectionTimeout = 15;
connect. CommandTimeout = 10;
connect. Open( "DSN=BookStore" , "dbo" , " " ) ;

var cmd = Server. CreateObject("ADODB. Command");


cmd.CommandText = "DelMgr";
cmd.CommandType = adCmdStoredProc;
cffld. ActiveConnection = connect;

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 Базы данных в Интернете. Практическое руководство

Процедуре DelMgr (листинг 4-31) этот идентификатор передается через пара-


метр с именем User.
Листинг 4-31 Вы найдете в файле ch4\BookShopScripts\dbo.De!Mgr.PRC на при-
лагаемом к книге компакт-диске.
В случае успешного завершения в правый фрейм главного окна администра-
тивного приложения вновь загружается страница managers.asp.
Хранимая процедура DelMgr удаляет из файла managers запись сотрудника,
идентификатор которого равен идентификатору, полученному процедурой через
параметр @User:
CREATE PROCEDURE DelMgr ©User varchar{50) AS
DELETE managers WHERE Name=@User

Редактирование записи сотрудника


Редактирование записи сотрудника выполняется на странице edtmanager.asp
(листинг 4-32).
Листинг 4-32 хранится в файле ch4\ BookShopXedtmanager.asp на прилагаемом
к книге компакт-диске.
Операция редактирования учетных записей сотрудников должна выполнять-
ся только администраторами, поэтому, прежде чем обращаться к базе данных, мы
проверяем содержимое переменной сеанса Rights.
Если с правами все в порядке, начинает выполняться хранимая процедура
GetMgr извлекающая текущие параметры учетной записи выбранного сотрудни-
ка. Она имеет один входной параметр User (идентификатор сотрудника) и два
выходных — Pwd (пароль) и R i g h t s (права сотрудника):
connect = Server.CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");

var cmd = Server.CreateQbject("ADODB.Command");


cmd.CommandText = "GetMgr";
cmd.CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;

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

Значения, извлеченные из таблицы m a n a g e r s , помещаются в форму редак-


тирования, в которой администратор может их изменить:
<FORM ACTION="updatemgr.asp" METHOD="POST">
<Н2>Редактирование записи пользователя <X=Usr.ValueXX/H2>
<TABLE BORDER=0 CELLPADDING=5 CELLSPACING=0>
<ТРХТО>ИМЯ</ТОХТО>
<INPUT SIZE=16 TYPE="HIDDEN" NAME="ID" VALUE="<X=Usr.ValueS!>">
<INPUT SIZE=16 TYPE="EDIT" NAME="USR" VALUE="<X=Usr.
</TDX/TR><TR><TD>naporb</TDXTD>
<INPl)T SIZE=16 TYPE="EDIT" NAME="PWD" VALUE="<X=Pwd.
</TDX/TR>
<trxtd>npaea </td>
<%
var sAdm="", sSMan="", sStH="";
if(Rights.Value =- "Administrator")
sAdm="selected";
else if(Rights.Value == "Sales_manager")
sSMan="selected";
else if(Rights.Value == "shjnanager")
sStH="selected";
*>
<td>
<select NAME="flG" size="1">
<option <K=sAdmK> value="Administrator">Bce npaea</option>
<option <X=sSMan^> уа1ие="8а1ез_тападег">Менеджер по npoAa*aM</option>
<option <X=sStHX> value="sh_manager">3aBeflyioutHM CKflaflOM</option>
</select> </tdx/tr>
<TRXTD>&nbsp; </TDxTD>
<INPUTTYPE="SUBMIT" VALUE="CoxpaHHTb изменения"></ТОХ/ТН>
</TABLEX/FORM>
После тцелчка кнопки Сохранить изменения управление передается странице
сценария updatemgr.asp (листинг 4-33), выполняющей обновление записи в фай-
ле managers.
Запуская страницу updatemgr.asp, наш сценарий передает ей параметры ID
(идентификатор записи сотрудника), USR (идентификатор сотрудника), PWD (па-
роль) и RG (права сотрудника), причем параметр ID передается через скрытое
поле формы.
Листинг 4-33 Вы найдете в файле ch4\ BookShopXupdatemgr.asp на прилагае-
мом к книге компакт-диске.
Задача сценария, расположенного на странице updatemgr.asp, заключается в
запуске хранимой процедуры SetMgr, выполняющей обновление файла managres.
Данные для обновления она получает через свои параметры @LJser, @NewUse г, @Pass
и ©Rights:
connect = Server.CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");

var cmd = Server.CreateObject("ADODB.Command");


(см. след, стр.)
184 Базы данных 8 Интернете. Практическое руководство

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);

cmd.Pa rameters.Append С Us r);


cmd.Pa rameters.Append(NewUs r);
cmd.Parameters.Append(Pwd);
cffld,Pa rameters.Append(Rights);
cmd. ExecuteO;
connect.Closef);
Исходный текст процедуры SetMgr находится в листинге 4-34.
Листинг 4-34 хранится в файле ch4\BookShopScripts\dbo.SetMgr.PRC на прила-
гаемом к книге компакт-диске.
Она обновляет поля записи файла m a n a g e r s , вводя значения, полученные
через параметры:
CREATE PROCEDURE SetMgr ©User varchar(SO), ©NewUser varchar(SO) ,
@Pass varchar(SO) , ©Rights varchar(16) AS UPDATE managers SET Name=@NewUser,
Rights=@Rights, Password=@Pass FROM managers WHERE Name=@User
Описанную схему редактирования записей таблицы m a n a g e r s Вы можете
применить для создания редакторов любых других таблиц, как отдельных, так
и связанных между собой.
Редактирование списка книг
Сотрудники Вашего магазина должны иметь возможность просматривать и ре-
дактировать список книг, имеющихся в продаже, а также описания этих книг.
Эти задачи решаются с помощью серверных сценариев books.asp, newbook.asp,
createbook.asp, delbook.asp, editbook.asp и updatebook.asp.
Просмотр списка книг
Для просмотра списка книг используется страница books.asp, исходный текст
которой приведен в листинге 4-35.
Листинг 4-35 Вы найдете в файле ch4\ BookShop\books.asp на прилагаемом к
книге компакт-диске.
Для получения списка всех книг сценарий вызывает хранимую процедуру
ListBooks:
Глава 4. Связь приложений с базами данных через ADO 1 85

var connect;
var rs;
connect = Server. CreateObject("ADOD8. Connection");
connect. ConnectionTiflieout = 15;
connect. CommandTimeout = 10;
connect . Openf "DSN=BookStore" , "dbo" , "" ) ;

rs = Server. CreateObject("ADODB. Recordset")


rs. Open("ListBooks" , connect,
adOpehForwardOnly, adLockReadOnly, adCmdStoredProc);
Если список пуст, вместо таблицы со списком книг отображается строка «Нет
записей». В противном случае сценарий формирует таблицу с описанием книг,
обрабатывая в цикле набор записей rs, полученный в результате работы проце-
дуры ListBooks:
if(rs.EOF)
I
%>
<TR><TD COLSPAN=4 ALIGN="CENTER">[HeT эаписей]</ТОХ/ТВ>
<X
:
else

<HTML>
<BODY>
<Ь2>Список книг</п2>
<TABLE BORDER=1>
<trxth>KHnra</thxth>KoMaHfla</thx/tr>
<%
var i=0, sID, sDate="";
while (Irs. EOF)

<trxtdxbx)£=rs.Fields( "Author" )K>


<br><X=rs.Fields( "Title" )X></bXbr><iX)E=rs.Fields( "Publisher" )XX/i>
<Ьг>Цена: <X=rs. Fields("Price")S> y.e.
<Ьг>Дата обновления: <X=rs. Fields("AddDate")!EX/td>
<td>
<a href="editbook.asp?ID=<X=rs.Fields("booksID")J>"
target="main">H3MeHHTb</A>&nbsp;
<a href="delbook.asp?ID=<X=rs.Fields("booksID")X>"
target ="та!п">Удалить</А>
</td><tr>
<tr><td со!зрап=2>Аннотация: <br><smallxx=rs. Fields("Annotation")!6>
</smallx/td><tr>
<%
rs.MoveNextC);
}
rs.CloseO;
}
connect. Closet);
186 Базы данных в Интернете. Практическое руководство

В каждую строку создаваемой таблицы вставляются ссылки на страницы,


позволяющие редактировать описание книги и удалять книгу. Первая задача
выполняется на странице edit bo ok. asp, а вторая — на странице delbook.asp.
В нижней строке таблицы размещается ссылка Новая книга:
<а href="newbook.asp" target="main">[HoBafl книга]</а>
Если ее щелкнуть, загружается страница newbook.asp. Эта страница предназ-
начена для добавления в базу данных описания новой книги.
Что же касается хранимой процедуры ListBooks (листинг 4-36), то ее задача —
выбор полей booksID, A u t h o r , Title, P u b l i s h e r , Price, A d d D a t e и A n n o t a t i o n из
таблицы books.
Листинг 4-36 Вы найдете в файле ch4\BookShopScripts\dbo.ListBooks.PRC на
прилагаемом к книге компакт-диске.
Эта задача решается с помощью простого оператора SELECT:
CREATE PROCEDURE ListBooks AS
SELECT booksID,Author, Title, Publisher, Price, AddDate,Annotation
FROM books
Добавление новой книги
Как мы только что сказали, после щелчка ссылки Новая книга управление пе-
редается странице newbook.asp (листинг 4-37).
Листинг 4-37 хранится в файле ch4\ BookShop\newbook.asp на прилагаемом к
книге компакт-диске.
Она содержит форму с полями описания книги:
<form ACTION="createbook.asp" METHOD="post">
<п2>Новая книга</п2>
<table BORDER="0" CELLPADDING="5" CELLSPACING="0">
<trxtd ALIGN="left" VALIGN*"tOp">ABTOp </td>
<td ALIGN="left" VALIGN="top">
<input type="text" name="Author" size="50"> </td>
</tr><trXtd ALIGN="left" VALIGN="top">Ha3BaHHe </td>
<td ALlGN="left" VALIGN="top">
<textarea rows="2" name="Title" cols="50"></textarea> </td>
</trXtr><td ALIGN="left" VALIGN="top">M3AaTerb</td>
<td ALIGN="left" VALIGN="top">
<select name="Publisher" size="1">
<option value="PyccKafl Редакция">Русская Редакция</ор^оп>
<option уа1ие='71иалог-МИ<РИ''>Диалс)г-МИФИ</ор^оп>
<option value="BHHOM">BHHOM</option>
<option value="Microsoft Press">Microsoft Press</option>
</selectx/td> </trxtr>
<td ALIGN="left" VALIGN="top">L|eHa, y.e.</td>
<td ALIGN="left" VALIGN="top">
<input type="text" name="Price" size="20"></td>
</trxtrxtd ALIGN="left" VALIGN="top">AHHOTaijHfl</td>
<td ALIGN="left" VALIGN="top">
<textarea rows="5" name="Annotation" cols="50">
</texta геах/tdx/t rxt r>
<td ALIGN="left" VALIGN="top">&nbsp; </td>
Глава 4. Связь приложений с базами данных через ADO 187

<td ALIGN="left" VALIGN="top">


<input TYPE="submit" УАШЕ="Добавить"> </td></tr>
</table>
</form>
После заполнения всех полей формы сотрудник магазина должен щелкнуть
кнопку Добавить, тогда данные из полей формы будут переданы сценарию
createbook.asp (листинг 4-38).
Листинг 4-38 хранится в файле ch4\ BookShop\createbook.asp на прилагаемом
к книге компакт-диске.
Задачей этого сценария является получение указанных выше данных через
параметры A u t h o r , Title, P u b l i s h e r , Price и A n n o t a t i o n с последующей записью
в таблицу books.
Запись сведений о новой книге выполняется хранимой процедурой NewBook
с применением уже знакомой Вам техники:
connect = Server.CreateObject("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");

var cmd = Server.CreateObjectC'ADODB.Command");


cmd.CommandText = "NewBook";
cmd.ComrnandType = adCmdStoredProc;
cmd.ActiveConnection = connect;

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 Базы данных в Интернете. Практическое руководство

CREATE PROCEDURE NewBook ^Author varchar(50), «Title varchar(200), ©Publisher


varchar(SO), @>Price money, ©Annotation varchar(2048) AS
INSERT books (Author, Title, Publisher, Price, Annotation) VALUES(@Author,
@>Title, ©Publisher, @Price, ^Annotation)
Такой размер поля возможен только в таблицах SQL Server версии 7.0. Что
же касается версии 6.5, то для нее эта величина ограничена значением 255 байт,
поэтому хранение больших объемов текста нужно выполнять по-другому (на-
пример, с использованием полей типа text).
Удаление книги
Если сотрудник магазина захочет удалить книгу из базы данных и щелкнет
ссылку Удалить в списке книг, управление получит страница delbook.asp (ли-
стинг 4-40).
Листинг 4-40 вы найдете в файле ch4\ BookShop\delbook.asp на прилагаемом
к книге компакт-диске.
Данная страница запускает хранимую процедуру DelBook, передавая ей в ка-
честве единственного параметра идентификатор удаляемой книги bookID:
connect = Server.CreateObJect("ADODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");
var cmd = Server.CreateObject("ADODB.Command");
cmd.CommandText = "DelBook";
cmd.ComroandType = adCmdStoredProc;
cmd.ActiveConnection = connect;

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

Редактирование описания книги


Процесс редактирования описания книги состоит из двух этапов. На первом мы
получаем сведения о книге из таблицы books и записываем их в поля формы для
редактирования, а на втором отредактированное описание книги помещаем об-
ратно в таблицу books.
Первый этап выполняется при помощи сценария editbook.asp (листинг 4-42).
Листинг 4-42 Вы найдете в файле ch4\ BookShop\editbook.asp на прилагаемом
к книге компакт-диске.
Глава 4. Связь приложений с базами данных через ADO 189

После проверки прав пользователя сценарий запускает хранимую процеду-


ру GetBook, определяя для нее один входной и несколько выходных параметров:
var rs, connect;
connect = Server.CreateObjectC'ADODB.Connection"};
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("OSN=BookSto re", "dbo", "");

var cmd = Server.CreateObjectC'ADODB.Command");


cmd.CommandText = "GetBook";
cmd.CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;

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 Базы данных в Интернете. Практическое руководство

<td ALIGH="left" VALIGN="top"xb>ABTOp</bX/td>


<td ALIGN-"left" VALIGN="top">
<input type="text" name="Author" size="50" valu9="<X=Author.ValueX>">
</tdx/trxtr>
<td ALIGN="left" VALIGN="top"xb>Ha3aaHMe</bx/td>
<td ALIGN="left" VALIGN="top">
<textarea rows="2" name="Title" cols="50"><X=Tltl9.ValiJeX></textarea> </td>
</trxtr>
<td ALIGN="left" VALIGN="top"><b>Издaтeль</b></td>
<td ALIGN="left" VALIGN="tOp">
<X
var s1="", s2="", s3="", s4="";
if(Publisher.Value == "Русская Редакция")
s1="selected";
else if(Publisher.Value == "Диалог-НИФИ")
s2="selected";
else if(Publisher.Value == "Бином")
s3="selected";
else if(Publisher.Value == "Microsoft Press")
s4="selected";
%>
<select name="Publisher" size="1">
<option <X=s1S!> value="PyccK3fl Редакция">Русская Редакция</ор11оп>
<option <%=$2%> уа1ие="Диалог-МИФИ">Диалог-МИФИ</ориоп>
<option <K=s35(> value="5nHOM">BMHOM</option>
<option <fc=s4X> value="Microsoft Press">Microsoft Press</option>
</selectx/td>
</trXtr>
<td ALIGN="left" VALIGN="top"xb>UeHa, y.e.</bx/td>
<td ALIGN="left" VALIGN="top">
<input type="text" name="Price" size="20" value="<X=Price.Value!£>">
</tdx/tr><tr>
<td ALIGN="left" VALIGN="top"><b>AHHOTauHfl</bx/td>
<td ALIGN="left" VALIGN="top">
<textarea rows="5" name="Annotation" cols="50"><X=Annotation.ValueX>
</textarea></tdx/tr>
<TR><TD>&nbsp; <ADXTO>
<INPUTTYPE="hidden" VALUE="<S!=bookID):>" name="bookID">
<INPUTTYPE="SUBMIT" VALUE="CoxpaHHTb иэмененин"х/ТОХ/ТР>
</TABLEX/FORM>
После щелчка кнопки Сохранить изменения данные из этой формы переда-
ются странице updatebook.asp, которую мы рассмотрим чуть позже.
Сведения о книге извлекаются из базы данных при помощи хранимой про-
цедуры GetBook (листинг 4-43).
Листинг 4-43 Вы найдете в файле ch4\BookShopScripts\dbo.GetBook.PRC на
прилагаемом к книге компакт-диске.
Эта процедура принимает в качестве входного параметра идентификатор
книги @bookID, возвращая информацию о ней через выходные параметры:
CREATE PROCEDURE GetBook @bookID varchar(SO), ©Author varchar(SO) output,
@>Titile varchar(200) output, @Publisher varchar(SO) output, ©Price money output,
Глава 4. Связь приложений с базами данных через ADO 191

@>AddDate date-time output, ©Annotation varchar(2048) output AS

SELECT @Author=Author, @Titile=Title, @Publisher=Publisher,


@Price=Price, @AddDate=AddDate, @Annotation=Annotation
FROM books
WHERE booksID=@bookID
Обновление отредактированного описания книги в таблице books выполня-
ется серверным сценарием, расположенным на странице updatebook.asp (лис-
тинг 4-44).
Листинг 4-44 Вы найдете в файле ch4\ BookShopVupdatebook.asp на прилагае-
мом к книге компакт-диске.
Эта страница вызывает хранимую процедуру SetBook, обновляющую поля
таблицы books:
connect = Server.CreateObjectC'AOODB.Connection");
connect.ConnectionTimeout = 15;
connect.CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");

var cmd = Server.CreateObjectC'AOODB. Command");


cmd.CommandText = "SetBook";
crad.CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;

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));

cmd.Pa ramete rs.AppendC bookID);


cmd.Parameters.AppendCAuthor);
cmd.Parameters.Append(Title);
cmd.Parameters.Append(Publisher);
cmd.Pa ramete rs.AppendC Price);
cmd.Parameters.AppendCAnnotation);
cmd. ExecuteQ;
connect.Closef);
192 Базы данных в Интернете. Практическое руководство

Исходный текст процедуры приведен в листинге 4-45.


Листинг 4-45 Вы найдете в файле ch4\BookShopScripts\dbo.SetBook.PRC на
прилагаемом к книге компакт-диске.
Процедура выполняет обновление полей таблицы books значениями, передан-
ными ей через параметры:
CREATE PROCEDURE SetBook ©bookID varchar(50), ©Author varchar(SO),
©Title varchar(200), ©Publisher varchar(50), @Price varchar(SO),
©Annotation varchar(2048) AS

UPDATE books SET Author=@Author, Title=@Title, Publisher=@Publisher,


Price=CONVERT(money, ©Price), Annotation=©Annotation
FROM books WHERE bOOksID=§boOkID

Работа с записями покупателей


В функции администратора Интернет-магазина входит просмотр списка зареги-
стрированных покупателей, просмотр содержимого их корзин и удаление учет-
ных записей неактивных покупателей — с помощью административного прило-
жения.
Форма поиска покупателей
Так как в популярном магазине покупателей регистрируется множество, едва ли
имеет смысл выводить их полный список для просмотра или редактирования
отдельных учетных записей. Вместо этого мы разработали поисковую систему,
которая позволит сотруднику магазина находить учетные записи пользователей
по их именам, адресам электронной почты или датам регистрации. Дополнитель-
но мы предусмотрели использование при поиске символов шаблонов, необходи-
мых для того, чтобы найти пользователей по неточным или неполным именам
или по адресам электронной почты.
Исходный текст страницы Cu.stomerSearch.asp, предназначенной для задания
параметров поиска, представлен в листинге 4-46.
Листинг 4-46 Вы найдете в файле ch4\ BookShop\CustomerSearch.asp на при-
лагаемом к книге компакт-диске.
Обратите внимание на то, что в этой странице мы используем одновременно
и клиентские, и серверные сценарии. Для выбора посетителей по дате регист-
рации мы включили в исходный текст страницы файл calendar.js, описанный во
второй главе. С помощью средств Dynamic HTML этот файл создает на страни-
це два календаря, предназначенных для указания начальной и конечной даты
регистрации.
Сразу после загрузки страницы, однако, в дело включается серверный сце-
нарий. Анализируя содержимое переменной сеанса SearchUsersFirstUse, сервер-
ный сценарий выбирает для инициализации календаря одну из двух функций
клиентского сценария: b u i l d m a p или b u i l d m a p E x :
<%
if(Session("SearchUsersFirstUse") == null)
{
к>
<body onload="buildmap()">
Глава 4. Связь приложений с базами данных через ADO 1 93

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;> )">

Функция b u i l d m a p отображает на странице календарные даты, ограничиваю-


щие период времени по умолчанию (длительностью в одну неделю).
Для того чтобы не заставлять пользователя вводить один и тот же диапазон
дат, серверный сценарий сохраняет его в переменных сеанса. Когда страница
CustomerSearch.asp загружается во второй раз, эти параметры извлекаются из
переменных сеанса и затем подставляются в качестве параметров функции кли-
ентского сценария b u i l d m a p E x .
Переменная сеанса dSearchUsersDateCheck хранит флаг поиска по диапазону
дат, а переменные dSearchUsersLogName и dSearcnUsersEMail — имя и адрес элек-
тронной почты посетителя. Переменные сеанса dSearchUsersFromFY, dSearch-
UsersFromFM и dSearchUsersFromFD содержат компоненты начальной даты регис-
трации посетителя, а переменные dSearchUsersToFY, dSearchUsersToFM и dSearch-
UsersToFD — компоненты конечной даты регистрации посетителя.
На странице CustomerScarch.asp находится также функция клиентского сце-
нария с именем до:
function go<)
!
var sLogName=escape(document. all.SearchForm. LogName. value);
var 3EMail=escape(document. all. SearchForm.CEMail. value);
var sUseDataCheckBox="";
if (document. all. UseDataCheckBox. checked)

sllseDataCheckBox="yes";
}
else
{
sUseDataCheckBox="no";

window. location. href ="GetSearchResults. asp?DATECHECK="+


sUseDataCheckBox*-
(см. след, стр.)
194 Базы данных в Интернете. Практическое руководство

"&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();

Ей передается управление, когда пользователь щелкает ссылку Поиск для по-


лучения списка регистрационных записей посетителей. Функция до извлекает
из полей формы данные, необходимые для поиска учетной записи, и передает их
странице, выполняющей запрос к базе данных.
Обратите внимание, как функция до выполняет кодирование шаблона име-
ни и адреса электронной почты, вызывая встроенную функцию JavaScript с
именем escape:
var sLogName=escape(document.all.SearchForm. LogName.value);
var sEMail=escape(document.all.SearchForm.CEMail.value);
Такая кодировка необходима, чтобы параметры с символами кириллицы и
другими «особыми» символами, такими, как символы пунктуации, правильно
передавались вызываемой странице ASP.
Для загрузки в правый фрейм страницы GetSearchResults.asp, выполняющей
поиск регистрационных записей в базе данных, функция до использует свойство
браузера window, location, href. Обратите внимание на фиктивный параметр FRCE,
предназначенный для отключения кэширования методом генерации случайно-
го адреса URL.
О работе функции клиентского сценария hidecalendar мы уже рассказыва-
ли во второй главе. Она предназначена для удаления календарей из страницы
при снятии отметки с переключателя искать по дате регистрации:.
Просмотр списка зарегистрированных покупателей
Рассмотрим исходный текст страницы GetSearchResults.asp, выполняющей по-
иск учетных записей посетителей магазина в базе данных. Вы найдете его в ли-
стинге 4-47.
Листинг 4-47 хранится в файле ch4\ BookShopXGetSearchResults.asp на прила-
гаемом к книге компакт-диске.
Так же как и только что рассмотренная страница CustornerSearch.asp, страни-
ца GetSearchResults.asp использует одновременно клиентские и серверные сце-
нарии. В частности, функция клиентского сценария trydeluser применяется при
удалении ненужной учетной записи неактивного посетителя:
<SCRIPT LANGUAGE=javascript>
<!-
function trydeluser(sUserID, sUserLogin)

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

dialogWidth:350px; status: 0");


if( rVal==false) return;

// Удаление записи
var sASP="";
sASP="trydeleteacc.asp?ID=" + sUserlD;
1
rVal=showModalDialog(sASP, vParm, "dialogHeignt: 160px;
dialogWidth:350px; status :0");
if(rVal==false) return;

// Удаление строки из таблицы


sNode="A"+sUserID;
var oDeletedRow=document. get Element Byld(sNode);
oDeletedRow. removeNode(true);

</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" , "") ;

var cmd = Server. CreateObject("ADODB. Command");


cmd.CommandText = "SearchUsers";
cmd.CommandType - 4;
cmd.ActiveConnection = connect;

var dFrom, dTo, dLogName, dName, dEMail, dDateCheck;

// После обновления записи


if(Request("MODE"}(1) == "restart")

dFrom=Session("dSearcriUsersFrom");
dTo=Session("dSearchUsersTo");
dLogName=Session("dSearchUsersLogName");
dEMail=Session("dSearchUsersEMail");
dDateCheck=Session("dSearcnUsersDateCheck");
.-

// После страницы поиска


else

dFrom=trim( Request ("FY")<1), 4)+trim(Request("FM")(1), 2)


(см. след, стр.)
196 Базы данных в Интернете. Практическое руководство

+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;
!

end.Parameters.Append(cmd.CreateParameter("From", 200, 1, 13,


dFrom));
cmd.Parameters.Append(cmd.CreateParameter("To", 200, 1, 13, dTo));
cmd.Parameters.Append(cmd.CreateParameter("Login", 200, 1, 20,
dLogName));
end.Parameters.Append(cmd.GreateParameter("Email", 200, 1, 80,
dEMail»;
cmd.Parameters.Append(cmd.GreateParameter("DateCheck", 200, 1, 20,
dDateCheck));

rs = cmd. ExecuteO;
В зависимости от того, попали ли мы на страницу GetScarchResults.asp по-
сле обновления учетной записи или со страницы поиска этих записей, выпол-
няется извлечение параметров поиска из переменных сеанса или запись их в эти
переменные соответственно. Все это делается для того, чтобы пользователь мог
повторно не вводить параметры поиска, если ему потребовалось их изменить.
Далее параметры поиска передаются хранимой процедуре SearchUsers, выпол-
няющей запрос к базе данных. Результаты этого запроса оображаются в виде
набора записей rs.
На следующем этапе серверный сценарий страницы GetSearchResults.asp
формирует документ HTML, записывая в него параметры поиска и таблицу с
найденными учетными записями. При этом в локальной переменной n C o u n t e r
Глава 4. Связь приложений с базами данных через ADO 1 97

подсчитывается общее количество найденных покупателей, а в переменной nSumm


выполняется подсчет общего количества денег, полученных от них:
<Ь2>Покупатели</Ь2>
<Kif(Session("dSearchUsersDateCheck") == "yes")
{%>
^Зарегистрировались в период от <b><X=dFromXX/b> до <b><X=dToX></b>
<%} else < %>
<р>3арегистрировались в любое вреня

<Ьг>Идентификатор: <b><X=(dLogName != "") ? dLogName : "[любой]"Хх/Ь>,


EMail: <bxx=(dEMail != "") ? dEMail : "[любой]"ХХ/ьх/р>
<TABLE BORDER=1>
<TR>
<ТН>Идентификатор</ТНХТН>Полное имя</ТНХТН>Деньги</ТН>
<ТН>Дата регистрации</ТНхтн>Адрес Е-На11<ЛН><ТН>Команды</ТН>
</TR>

var nSumm=0;
var nCounter=0;
iff rs. EOF)

<TR><TD COLSPAN=6 ALIGN="CENTER">[CnHCOK покупателей пуст]</ТО></ТР>

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>&nbsp;
<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;

(см. след, стр.)


198 Базы данных в Интернете. Практическое руководство

rs.CloseO;
connect.Close();
%>
<тп>
T
<TD COLSPAN="2"><b>Bcero покупателей: <X=nCounterK></b><AD>< D><X=nSumm/100?!>
</TD><TD COLSPAN="3">&nbsp;</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

Удаление записи покупателя


Как уже говорилось ранее, для удаления учетной записи покупателя мы вызы-
ваем функцию клиентского сценария с именем t r y d e l u s e r , передавая ей в каче-
стве параметров идентификатор пользователя и его имя.
Рассмотрим работу функции trydeluser (листинг 4-47). Ее основная задача —
отображать предупреждающее сообщение об удалении пользователя, а также
предотвращать удаление учетных записей пользователей, отобравших в свои
корзины какие-либо книги (то есть учетные записи активных покупателей).
Для отображения предупреждающего сообщения об удалении учетной записи
посетителя эта функция отображает на экране модельную диалоговую панель
trydeleteuserdlg.asp (листинг 4-49), вызывая ее при помощи функции клиент-
ского сценария showModalDialog. При этом функция trydeluser передает странице
trydeleteuserdlg.asp массив параметров v P a r m с идентификатором пользователя
и его именем, используемым для входа в магазин.
Листинг 4-49 Вы найдете в файле ch4\ BookShopVtrydeleteuserdlg.asp на при-
лагаемом к книге компакт-диске.
Клиентский сценарий, находящийся на странице trydeleteuserdlg.asp, извле-
кает переданные ему параметры, используя имя пользователя для формирова-
ния предупреждающего сообщения об удалении пользователя:
<ЬЗ>Внимание!</ЬЗ>
<р>Учетная запись<Ьг><Ь><зсг1р1 LANGUAGE="javascript">
<!-
document.write(window.dialogArguments[1]);
//->
</script></b><br>6yA6T удалена!
Если пользователь решит продолжить удаление и щелкнет ссылку Удалить,
управление получит функция клиентского сценария tryDeleteAcc, определенная
на странице trydeleteuserdlg.asp. При отказе от удаления с помощью ссылки
Отменить будет вызвана функция клиентского сценария cancelDeleteAcc:
<а onclick="tryDeleteAcc()">
<font color="red">yflaflHTb</fontx/a>

<а onclick="cancelDeleteAcc()">
<font color="red">OTMeHHTb</fontx/a>
Функция tryDeleteAcc завершает работу модельной диалоговой панели, воз-
вращая значение true:
function tryDeleteAccO

window.returnValue=true;
event.returnValue=false;
window. closeQ;

Функция cancelDeleteAcc сообщает об отмене удаления возвращением зна-


чения false:
8 Зак.3571
200 Базы данных в Интернете. Практическое руководство

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", " " ) ;

var cmd = Server.CreateObject("ADODB.Command");


var DelOk;
cmd.CommandText = "DeleteUser";
cmd.CommandType = 4;
cmd.ActiveConnection = connect;
Глава 4. Связь приложений с базами данных через ADO 201

cmd.Pa ramete rs.Append(cmd.С reatePa rameter(


"User", 3, 1, 4, Request("ID")(1)));

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>
<%

Исходный текст хранимой процедуры DeleteUser приводится в листинге 4-51.


Листинг 4-51 Вы найдете в файле ch4\BookShopScripts\dbo.DeleteUser.PRC на
прилагаемом к книге компакт-диске.
В этой процедуре мы создаем локальную переменную ©Status, присваивая ей
нулевое значение. Если данный посетитель имеет записи в таблице orders (то
есть если он отобрал книги для покупки), мы считаем его активным и устанав-
ливаем содержимое переменной ©Status, равное единице:
CREATE PROCEDURE DeleteUser «ID int, @>0k int =0 output AS

DECLARE ©Status int


SELECT @Status=0

IF EXISTS(SELECT * FROM orders WHERE ClientID=@ID)


SELECT @Status==1
Для пассивных посетителей выполняется удаление регистрационных запи-
сей из таблицы clients:
IF @Status=0 BEGIN
DELETE clients WHERE ClientID=@ID
END
Результат выполнения операции удаления в любом случае записывается в
выходной параметр @0k:
SELECT @Ok=@Status
Глава 4. Связь приложений с базами данных через ADO 203

Просмотр содержимого корзины покупателя


Сотрудник магазина может просмотреть содержимое корзин покупателей при
помощи страницы order.asp (листинг 4-52). Эта страница выглядит почти так же,
как и страница просмотра корзины в приложении покупателя, однако в ней не
предусмотрено изменение содержимого корзины.
Листинг 4-52 Вы найдете в файле ch4\BookShop\order.asp на прилагаемом к
книге компакт-диске.
Серверный сценарий, расположенный на этой странице, вызывает хранимую
процедуру ListOrders — ее мы уже описывали ранее в разделах, посвященных
приложению покупателя:
var ClientID=Request("ID"}{1);

connect = Server. CreateObjectf'ADODB. Connection");


connect. ConnectionTimeout - 15;
connect. CommandTimeout = 10;
connect . Open( "DSN=BookStore" , "dbo" , " " ) ;

cmd = Server. CreateObject("ADOD6. Command");


cmd. CommandText = "ListOrders";
cmd. CommandType = adCmdStoredProc;
cmd.ActiveConnection = connect;

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)

<trXtdXbx*=rs.Fields("Author")X>. <K=rs. Fields("Title")X></b>


<brxixa;=rs.Fields( "Publisher" )KX/ix/td>
<tdxs=rs.Fields("Price")K> y.e.</tdxtr>
<%
rs.MoveNextQ;
!
rs.CloseO;
connect. Close();
%>
</TABLE>
204 Базы данных в Интернете. Практическое руководство

Редактирование регистрационных данных покупателя


Эта операция выполняется в два приема. Вначале загружается страница Edic-
User.asp, исходный текст которой представлен в листинге 4-53. Она получает
сведения о регистрационной записи покупателя из базы данных магазина и ото-
бражает их в форме. Затем данные ил формы попадают на страницу Updatc-
User.asp, обновляющую запись в базе данных.
Листинг 4-53 Вы найдете в файле ch4\BookShop\EditUser.asp на прилагаемом
к книге компакт-диске.
В начале своей работы страница EditUser.asp проверяет права текущего
пользователя, разрешая редактирование регистрационных записей покупателей
только администраторам. Затем выполняется запуск хранимой процедуры Get-
User, извлекающей регистрационную запись из базы данных. В качестве вход-
ного параметра мы передаем этой процедуре идентификатор покупателя:
connect = Server.CreateObjectC'ADODB.Connection");
connect.ConnectionTimeout = 15;
connect,CommandTimeout = 10;
connect.Open("DSN=BookStore", "dbo", "");

var cmd = Server.CreateObjectC'ADODB.Command");


cmd.CommandText = "GetUser";
cmd.CommandType = 4;
cmd.ActiveConnection = connect;

var ID = cmd.CreateParameter(
"ID", adlnteger, adParamlnput, 4, Request("ID")(1));
cmd.Parameters.Append(ID);

var UserlD = cmd.CreateParameterf


"UserlD", adVarChar, adParamOutput, 50, " ");
cmd.Parameters.Append(UserlD);

var Pass = cmd.CreateParameterf


"Pass", adVarChar, adParamOutput, 50, " ");
cmd.Parameters.Append С Pass);

var Language = cmd.CreateParameter(


"Language", adVarChar, adParamOutput, 50, " ");
cmd.Pa ramete rs.Append(Language);

var Money= cmd.CreateParameterf


"Money", adCurrency, adParamOutput, 8, "0");
cmd.Pa ramete rs.Appendf Money);

var RegisterDate = cmd.CreateParameterf


"RegisterDate", adDBTimeStamp, adParamOutput, 8, 0);
cmd.Parameters.Append СRegisterDate);

var RegisterIP = cmd.CreateParameterf


"RegisterIP", adVarChar, adParamOutput, 15, " "); i
cmd.Parameters.Append(RegisterIP);
Глава 4. Связь приложений с базами данных через ADO 205

var Email = cmd.CreateParameter(


"Email", adVarChar, adParamOutput, 80, " ");
cmd.Pa ramete rs.Append< Email);

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>&nbsp; </TDXTD>
<INPUTTYPE="submit" VALUE="06HOBHTb">
</TDX/TRx/TABLE></FORMx/TDx/TRx/TABLE>
206 Базы данных в Интернете. Практическое руководство

После того как администратор магазина отредактирует учетную запись поку-


пателя, он должен щелкнуть кнопку Обновить. В результате загружается стра-
ница Update User.asp, которой будут переданы обновленные данные из полей
форм, а также идентификатор учетной записи пользователя в скрытом поле
формы.
Исходный текст хранимой процедуры GetUser представлен в листинге 4-54.
Листинг 4-54 Вы найдете в файле ch4\BookShopScripts\dbo. GetUser. PRC на
прилагаемом к книге компакт-диске.
Эта процедура просто выбирает поя я учетной записи пользователя, возвра-
щая их через выходные параметры.
CREATE PROCEDURE GetUser «ID int, §UserID varchar(SO) output, @Pass varchar(SO)
output, ©Language varchar(50) output, ©Money moneyoutput, @RegisterDate datetime
output, §RegisterIP varchar(15) output, @Email varchar(80) output AS

SELECT §UserID="", <g>Pass="", @Language="", @Money=0, @RegisterDate="",


@RegisterIP="",

SELECT @UserID=UserID, @Pass=Password, §Language=Language, @Money=Money,


@RegisterDate=RegisterDate, @RegisterIP=RegisterIP, @Email=Email
FROM clients WHERE ClientID=@ID
Исходный текст страницы Update User.asp, выполняющей обновление реги-
страционной записи пользователя, показан в листинге 4-55.
Листинг 4-55 Вы найдете в файле ch4\BookShop\UpdateUser.asp на прилагае-
мом к книге компакт-диске.
<Я@ LANGUAGE = "JScript" K>
<!- flinclude file="header.asp" ->

Response. Redirect("GetSearchResults.asp?MODE= restart");


I
%>
<!- einclude file="footer.asp" ->
В начале своей работы серверный сценарий, расположенный на этой страни-
це, проверяет права пользователя. Мы разрешаем изменять учетные записи по-
купателей только администраторам.
Далее сценарий вызывает хранимую процедуру SetUserData, передавая ей
через входные параметры идентификатор пользователя и обновленное содержи-
мое полей учетной записи:
var rs, connect;
connect = Server. CreateObject("ADODB. Connection");
connect. ConnectionTimeout = 15;
connect. Comma ndTimeout = 10;
connect . Open( "DSN=BookStore" , "dbo" , " " ) ;

var cmd = Server. CreateObject("ADQDB. Command");


cmd.CommandText = "SetUserData";
cmd.CommandType = 4;
cmd. ActiveConnection = connect;
Глава 4. Связь приложений с базами данных через ADO 207

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

Работа с ADO в приложениях C++


Объектная модель ADO создавалась для применения с различными языками и
системами программирования, совместимыми с СОМ. В предыдущих разделах
.этой главы мы показали основные приемы использования интерфейсов и мето-
дов ADO в серверных сценариях ASP, написанных на языке Microsoft JScript.
Вместе с тем модель ADO также доступна в серверных сценариях VB Script, в
программах Java, Microsoft Visual Basic и в приложениях, подготовленных при
помощи Microsoft Visual C++.
Такие языки программирования, как Microsoft JScript, VB Script и Microsoft
Visual Basic, в значительной степени «маскируют» тот факт, что работа с ADO
выполняется средствами СОМ. Это позволяет разработчику составлять доста-
точно сложные программы, обращающиеся к базам данных, не затрудняя себя
детальным изучением модели компонентных объектов СОМ. В случае исполь-
зования C++ требуются более глубокие знания, хотя и здесь есть возможности
для упрощения разработки приложений, интенсивно взаимодействующих с
ADO.
208 Базы данных в Интернете. Практическое руководство

Для чего Вам могут потребоваться приложения C++, обращающиеся к базам


данных посредством ADO?
Если речь идет о приложениях для Интернета, то это нужно, прежде всего,
для связи программных расширений сервера Web, таких, как программы CGI
или ISAPI, с базами данных. Кроме того, иногда надо расширить объектную
модель ASP, добавив собственные элементы ActiveX, обращающиеся к базам
данных.
Разработчик приложения C++ может работать с ADO тремя различными
способами:
4 непосредственно вызывая интерфейсы и методы ADO с помощью функций
программного интерфейса Win32, предназначенных для работы с СОМ;
ф применяя средства библиотеки MFC, созданных для OLE;
ф импортируя библиотеки типов ADO с помощью оператора ftimport.
Первый из этих способов предполагает детальное знакомство программиста
с методикой применения технологии СОМ. В частности, необходимо следить за
использованием указателей на интерфейсы, своевременно захватывая их и ос-
вобождая при помощи методов AddRef и Release, а для создания объектов ADO
приходится вызывать явным образом функцию CoCreatelnstance.
Второй способ, ориентированный на применение MFC OLE, упрощает работу
с ADO посредством классов-оболочек (wrapper class). Недостатки данного спо-
соба — невозможность использования перечислимых типов данных из библио-
теки типов ADO, а также необходимость поставлять вместе с программой биб-
лиотеку динамической загрузки MFC DLL.
Ц наконец, третий способ, основанный на включении библиотеки типов ADO
оператором ttimport, предполагает создание вспомогательных классов-оболочек,
а также автоматическую генерацию перечислимых типов и глобальных уникаль-
ных идентификаторов GUID объектов ADO. Этот способ, на наш взгляд, наи-
более удобен, так как делает использование ADO в программах C++ примерно
таким же простым, как и в сценариях JScript и VB Script.
Одно из важных преимуществ применения оператора tfimport заключается в
использовании так называемых интеллектуальных указателей (smart pointer)
класса _com_ptr_t, а также классов для работы с типами данных BSTR и VARIANT.
Интеллектуальные указатели позволяют не беспокоиться о реализации и вызо-
ве методов Querylnterface, AddRef и Release, упрощая работу с указателями на
интерфейсы СОМ.
Еще одна особенность, связанная с оператором ftimport — обработка ошибок
при помощи исключений, Как известно, применение исключений для обработ-
ки ошибок вместо проверки кодов возврата сокращают объем листингов исход-
ного текста приложений, упрощают разработку и отладку программ. Когда при
создании объектов ADO или при выполнении методов ADO происходят ошиб-
ки, возникает исключение класса _ c o m _ e r r o r . Этот вспомогательный класс уп-
рощает обработку ошибок, так как сам вызывает методы интерфейса l E r r o r l n f о,

Импортирование библиотеки типов ADO


Ранее мы уже пользовались технологией импортирования библиотеки типов
ADO, создавая приложения ASP. Вспомните, что для каждого такого приложе-
ния мы создавали файл с именем global.asa, располагая его в корне виртуально-
Глава 4. Связь приложений с базами данных через ADO 209

го каталога приложения. В области определения метаданных этого файла мы


делали ссылку на библиотеку типов ADO следующего вида:
<!- METADATA TYPE="typelib"
FILE="d:\program files\common files\system\ado\msado20.tlb" ->
Что же касается программ C++, то для импортирования библиотеки типов
необходимо воспользоваться оператором ffimport, расположив его в области
определений, исходного текста программы;
«import "d:\program files\common files\system\ado\Msado20.tlb"
Наиболее подходящее место для расположения этого файла — файл StdAfx.b,
создаваемый автоматически системой разработки Microsoft Visual C++ и содер-
жащий различные h-файлы. Разумеется, Вы можете включать оператор tfimport
к файлы срр, содержащие обращения к ADO.
Как работает оператор tfimport?
Когда компилятор встречает такой оператор, ссылающийся на ту или иную
библиотеку типов, он генерирует для каждой библиотеки два текстовых файла
с расширением имени tlh и tli. Например, при импортировании библиотеки ти-
пов ADO версии 2.0 создаются файлы с именами Msado20.tlh и Msado20.tli. Эти
файлы создаются в каталоге с исходными текстами проекта Вашего приложения.
Файл Msado20.tlh содержит определения объектов и перечислимых типов
ADO, а файл Msado20.tH — классы-оболочки для методов объектной модели
ADO. Вы можете просмотреть их содержимое при помощи любого текстового
редактора, например в окне редактирования Microsoft Visual C++.
Если Вам не нравится размещать в исходном тексте программы абсолютные
ссылки на файлы библиотек типов, можно воспользоваться другим вариантом
вызова оператора flimport:
ttimport <Msado20.tlt»
При этом, однако, необходимо, чтобы полный путь к каталогу, содержащему
библиотеку типов (в нашем случае это d:\program files\common files\system\ado),
был прописан в переменной среды LIB, PATH или INCLUDE. Есть и другая возмож-
ность — добавить этот путь в список каталогов Visual C++ на вкладке Directories
панели Options, вызвав ее па экран при помощи строки Options меню Tools
(рис. 4-44).

or Tabs | Debug СекграШу Build СйвЯшв

DAProgram FilesWicnwofl Visual Sludk)\VC98W!lMNCLLIOE


d \program filas'idevsludioVv conclude
dAprcgram fil ss\ dev?! jdicW c\atl\inc lii de
d. program rlles\dev studio Wc\mfc\mckjde
E AE ra s eU ndcM oduleW include %

Рис. 4-44. Добавление пути к каталогу с библиотекой типов ADO


210 Базы данных в Интернете. Практическое руководство

После импортирования библиотеки типов указанным выше способом может


возникнуть проблема с константой EOF, определенной как значение -1. Эта кон-
станта обычно используется при работе с потоками ввода/вывода, однако как Вы
скоро убедитесь, в ADO ей уготовано и иное применение. Чтобы избежать кон-
фликта имен, мы переименуем EOF из библиотеки типов ADO в adoEOF. как это
показано ниже:
«import "d:\program files\common files\system\ado\Msado20.tlb" \
rename ("EOF", "adoEOF")

Обращение к интерфейсам и методам ADO


Прежде чем привести полные исходные тексты приложения, написанного на
C++ и обращающегося к базе данных средствами ADO, рассмотрим основные
приемы обращения к интерфейсам и методам ADO, основанные на использова-
нии оператора #import.
Инициализация СОМ
Автономное приложение, работающее с объектами ADO, должно до начала своей
работы выполнить инициализацию системы СОМ вызовом функции C o l n i -
tialize. Перед завершением работы приложению необходимо освободить ресур-
сы, связанные с системой СОМ, при помощи функции CoUninitialize.
Конечно, эти функции допустимо вызывать явным образом, однако удобнее
определить глобальную структуру с конструктором и деструктором, выполня-
ющую указанные действия:
struct Comlnit
I
Comlnitf)
{
::CoInitialize(NULL);

"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

Однако простое создание указателя еще не влечет за собой образование


объекта. Для того чтобы создать объект, мы вызываем метод Createlnstance, пе-
редавая ему в качестве параметра у н и к а л ь н ы й глобальный идентификатор
GUID объекта ADODB: :Connection. Этот идентификатор извлекается из файла
Msado20.tli, определенным в Microsoft Visual C++, при помощи конструкции
u u i d o f , являющейся расширением C++.
Результат выполнения метода Createlnstance записывается в переменную hr
типа HRESULT. Для проверки успеха завершения операции Вы должны использо-
вать макрос SUCCEEDED, например:
if(!SUCCEEDED(hr))
return;
Здесь, если операция завершилась с ошибкой, выполняется выход из функ-
ции установки связи с источником данных.
Теперь, когда объект C o n n e c t i o n успешно создан, можно открывать канал
связи методом Open:
_bstr_t bsConnString(L"DSN=BookStore");
_bstr_t bsUserID(L"dbo");
_bstr_t bsUserPwd(L"");

cn->Qpen(bsConnString, bsUserlD, bsUserPwd,


ADODB::adConnectUnspecified);
Здесь мы передаем методу Open имя источника данных, имя и пароль пользо-
вателя, а также дополнительный параметр, определяющий режим открытия
(синхронный или асинхронный). Константа A D O D B : : adConnectUnspecified опре-
деляет синхронный режим, установленный по умолчанию. Асинхронный режим
(задается константой adAsyncConnect) в нашей книге не рассматривается.
В процессе открытия канала связи с источником данных могут возникать
ошибки, вызывающие исключения. Обработка этих исключений рассмотрена
ниже.
Обращаем Ваше внимание на класс _bstr_t. Он становится доступным в ре-
зультате выполнения импорта библиотеки типов и помогает работать с типом
данных BSTR. Тип BSTR используется в программировании элементов СОМ для
передачи информации в виде текстовых строк Unicode. Класс _ b s t r _ t облегча-
ет создание таких строк и выполнение над ними основных операций.
После завершения операций необходимо закрыть соединение с источником
данных, вызвав метод Close:
cn->Close();
Выполнение команды
Для того чтобы выполнить команду, пользуясь установленным соединением с
источником данных, необходимо вызвать метод Execute:
_bstr_t bsCommand(L"select * from managers");
ADODB::_RecordsetPtr rs = NULL;
rs = cn->Execute(bsCommand, &vtMissing, ADODB::adCmdText);
В качестве первого параметра мы передаем методу Execute строку команды.
Как Вы помните, это может быть оператор языка SQL, имя хранимой процеду-
212 Базы данных в Интернете. Практическое оуководство

ры или имя таблицы базы данных. В нашем случае выполняется оператор 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

Вызов хранимой процедуры


В этом разделе мы рассмотрим способ вызова хранимой процедуры с парамет-
рами. Как мы уже говорили, использование хранимых процедур позволяет от-
делить данные от программ, что упрощает их создание и отладку.
Чтобы передать хранимой процедуре параметры, Вы должны вначале создать
объект класса Command (команду), а затем при помощи метода CreateParameter
определить вес необходимые параметры.
Создание объекта Command выполняется так:
ADODB::_CommandPtr cmd = NULL;
cmd.CreateInstance(__uuidof(ADODB::Command));
Далее надо «привязать» команду к каналу связи с набором данных, устано-
вив значение свойства A c t i v e C o n n e c t i o n :
cmd->ActiveConnection = en;
Так как команда выполняет вызов хранимой процедуры, в свойство Command-
Type следует записать значение ADODB: :adCmdStoredProc:
cmd->CommandType = ADODB::adCmdStoredProc;
В свойство CommandText Вы должны записать имя хранимой процедуры (как
переменную класса _bstr_t):
_bstr_t bsComrnandText(L"ManagerLogin");
cmd->CofnmandText = bsCommandText;
Теперь займемся параметрами команды.
Вначале определим указатель на параметр как объект класса AOODB; : _ Р а г а -
meterPtr:
ADODB::_ParameterPtr param = N U L L ;
Теперь мы создаем сам параметр, указывая его имя, тип, значение и размер
данных:
_bstr_t bsParamName(L"User");
param = cmd->CreateParameter(
bsParamName, ADODB::adVarChar, ADODB::adParamlnput,
-1, vtMissing);
Здесь имя параметра задано как User. Константа ADODB; : a d V a r C h a r определя-
ет, что параметр является текстовой строкой. С помощью константы ADODB: : ad-
Paramlnput мы указываем, что данный параметр — входной. Четвертый параметр
метода C r e a t e P a r a m e t e r указывает м а к с и м а л ь н ы й размер данных как -1, что
означает отсутствие ограничений на этот размер. И наконец, последний пара-
метр, определяющий значение параметра, указан как vtMissing (то есть пропущен).
Действительное значение входного параметра с именем User мы задаем при
помощи метода A p p e n d :
_variant_t vName(szName);
param->Value = vName;
cmd->Parameters->Append(param);
Здесь мы вначале инициализируем переменную vName из обычной текстовой
строки szName, закрытой двоичным нулем, а затем присваиваем ее значение свой-
ству param->Value. Далее параметр добавляется в набор параметров методом
Append.
214 Базы данных в Интернете. Практическое руководство

Второй входной параметр добавляется аналогично:


_bstr_t bsParaniName1(L"Pass"};
param = cmd->CreateParameter(
bsParamNamel, ADODB::adVarChar, ADODB::adParamlnput,
-1, vtMissing);

_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));

cn->Qpen(bsConnString, bsUserlD, bsUserPwd,


ADODB::adConnectUnspecified);

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 Базы данных в Интернете. Практическое руководство

Аналогичные действия можно предпринять и для других свойств объекта


Error:
strDescription.Format("Description: %s",
(LPCTSTR)_bstr_t(Error->GetDescription()));

strSource. Format("Source:Xs",
(LPCTSTR)_bstr_t(Error->GetSource())>;

strHelpFile.Formatf"HelpFile: Xs",
(LPCTSTR)_bstr_t(Error->GetHelpFileC)));

strHelpContext.Format("HelpContext: Kid", Error->GetHelpContext());

strNativeError.Format("NativeError: Ш", Error->GetNativeError());

st rSOLState.Fo rmat("SQLState: Xs",


(LPCTSTR)_bstr_t( Error->GetSQLState())>;
Полный текст функции обработки ошибок A d o E r r H a n d l e r приведен в лис-
тинге 4-57.
Пример программы
В качестве примера приложения, написанного на C++ и обращающегося к ADO
с применением импортирования библиотеки типов ADO, рассмотрим простую
консольную программу просмотра записей таблицы m a n a g e r s из базы данных
Bookstore. Эту базу мы создали в начале главы.
При запуске программа запрашивает имя пользователя и пароль. Если ввес-
ти идентификатор пользователя, обладающего правами администратора, про-
грамма покажет полное содержимое таблицы m a n a g e r s :
Login name: frolov
Password: 123

Manager list:

1 | frolov | 123 | 11.12.99 12:24:50 | Administrator


2 | Petrov | 123 | NULL | shjnanager
3 | Sidoroff | 123 | NULL | Salesjnanager
5 |TestAdmins |1234 | NULL | Sales_manager
4 | admin | 123 | 05.12.99 09:50:10 | Salesjnanager
Если же идентификатор или пароль введены неправильно либо если данный
пользователь не обладает правами администратора, то на консоли появляется
лишь сообщение о запрете доступа:
i
Login name: frolov
Password: 111

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;

Функция login запрашивает с консоли идентификатор и пароль пользовате-


ля, ищет его в базе данных, извлекает и проверяет права. Если пользователь
218 Базы данных в Интернете. Практическое руководство

зарегистрирован и обладает правами администратора, функция login возвращает


значение t r u e , а если нет — false.
Для администратора функция _tmain вызывает функцию getManagers, извле-
кающую из базы данных и отображающую информацию о сотрудниках магазина.
Функция login
Рассмотрим исходный текст функции login.
В блоке try эта функция создает объект ADOOB: : Connection и открывает со-
единение;
ADODB: :_ConnectionPtr en - NULL;
HRESULT hr = SJ)K;

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"");

cn->Open(bsConnString, bsUserlD, bsUserPwd,


ADODB: :adConnectUnspecified);

Затем она запускает хранимую процедуру Manage rLogin, имеющую три пара-
метра. В качестве двух входных параметров процедуре передается идентифика-
тор пользователя и его пароль, а в качестве выходного возвращается строка с
обозначением прав пользователя:
ADQDB::_CommandPtr cmd = NULL;
cmd.CreateInstance(__uuidof(ADODB; : Command));

cmd->ActiveConnection = en;
cmd->CommandType = ADODB: :adCradStoredProc;

_bstr_t bsCommandText(L"Manage rLogin");


cmd->CommandText = bsCommandText;

ADODB: :_ParameterPtr param = NULL;

_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);

_bstr_t bsParamNa[ne2(L" Rights");

param = cmd->CreatePararneter(
bsParamName2, ADODB: :adVarChar, ADODB: : adParamOutput,
16, vtMissing);

param->Value = _variant_t("");
cmd->Parameters->Append<param);

cmd->Execute(&vtMissing, ivtMissing, ADODB; : adCmdStoredProc);


Идентификатор и пароль запрашиваются из стандартного потока ввода, свя-
занного с клавиатурой.
Определение возвращаемого значения выполняется путем простого сравне-
ния значения выходного параметра и текстовой строки A d m i n i s t r a t o r :
_variant_t ok = param->Value;
_va riant J: okAdmin(L"Administrator");

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

(см. след, стр.)


220 Базы данных в Интернете. Практическое руководство

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"");

cn->Open(bsConnString, bsUserlD, bsUserPwd,


ADODB::adConnectUnspecified);

_bstr_t bsCommand(L"select * from managers");


rs = cn->Execute(bsCommand, &vtMissing, ADODB::adCmdText);

:
В результате выполнения этого оператора создается набор записей класса
Recordset. Он обрабатывается в цикле с использованием приемов, описанных
нами ранее:
_variant_t vManagerlD;
_variant_t vName;
_variant_t vPassword;
_variant_t vLastLogin;
_variant_t vRights;

CString strTmp = "";

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;

strTmp.Format("Xs | JMOs | S(10s | *20s | !UOs",


v2strCvManagerID), v2str(vName),
v2str( vPassword), v2str(vLast Login), v2str(vRights});

cout « (LPCTSTR)strTmp « "\n";

hr = rs->MoveNext();
if<!SUCCEEDED(hr))
break;

Для преобразования полученных значений в текстовые строки и отображе-


ния их в консольном окне мы используем функцию v2str, исходный текст ко-
торой приведен в листинге 4-59.
Исходный текст функции обработки ошибок A d o E r r H a n d l e r приведен в лис-
тинге 4-58. Мы уже рассказывали раньше об использованных в нем приемах
извлечения кодов ошибок.
Листинг 4-58 Вы найдете в файле ch4\CPPADO\adoerrhandler.cpp на прилагае-
мом к книге компакт-диске.
Служебная функция v2str (листинг 4-59) нужна для преобразования значе-
ний тина VARIANT в текстовые строки.
Листинг 4-59 Вы найдете в файле ch4\CPPADO\vcrack.cpp на прилагаемом к
книге компакт-диске.
Эта функция получает входной параметр как ссылку на переменную класса
COleVariant, предусмотренного в MFC для работы с переменными типа VARIANT.
Результат преобразования возвращается как текстовая переменная класса
CString.
Функция v2str устроена очень просто.
Как известно, переменная VARIANT может содержать значения разных типов,
причем тип определяется содержимым поля vt. Функция v2str анализирует
содержимое этого поля, выбирая тот или иной способ преобразования исходного
значения в текстовую строку.
Обратите внимание на применение функций C O l e C u r r e n c y и COleOateTirae,
помогающих выполнять такое преобразование для денежных данных и для зна-
чений дач1:
CString v2str(const C01eVariant& var)

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

денисм ресурсов СОМ в конструкторе и деструкторе специального объекта ини-


циализации.
Например, для инициализации строки параметров подключения к источни-
ку данных мы вначале создаем глобальную переменную s t r A c c e s s C o n n e c t типа
C S t r i n g и инициализируем ее следующим образом;
CString strAccessConnect("DSN=BookStore;UID=dbo;PWD=;" );
Далее в конструкторе глобального объекта инициализации мы выделяем па-
мять для строки bstrAccessConnect и записываем в нее данные из строки strAc-
cessConnect:
BSTR bstrAccessConnect;
bstrAccessConnect = strAccessConnect. AHocSysStringf);
Перед завершением работы программы деструктор объекта инициализации
освобождает ресурсы, связанные со строкой bstrAccessConnect:
SysFreeString(bstrAccessConnect);
Установка соединения с источником данных
Для установки соединения с источником данных нам необходимо создать объект
Connection. Так как мы отказались от импортирования библиотеки типов ADO,
нам придется выполнять эту операцию с помощью функции CoCreatelnstance;
«include <initguid.h>
«include "adoid.h"
ffinclude "adoint. h"

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 Базы данных в Интернеге. Практическое руководство

Так как все параметры соединения устанавливаются методом put_Connection-


String, мы указали для первых трех параметров метода Open пустые значения
bst rEmpty. Строка bst rEmpty определена как пустая строка:
1
CString strEmptyC ");
BSTR bstrEmpty;
bstrEmpty = strEmpty.AllocSysStringO;
Последний параметр метода Open задает синхронный режим открытия кана-
ла связи с источником данных.
Выполнение команды
Прежде чем выполнить команду, наша программа должна создать объект Command,
вызвав для этого функцию CoCreatelnstance:
ADOCommand* cmd = NULL;
if(SUCCEEDED(hr))
hr = CoCreateInstance(CLSID_CADOCornmand, NULL.
CLSCTX_INPROC_SERVER, IID_!ADOCommandt (LPVOID*)&cmd);
В случае успеха ссылка на интерфейс команды записывается в переменную
cmd типа ADOCommand*.
Далее нужно установить связь между командой и каналом связи с источни-
ком данных, для которого эта команда будет выполняться. Эта операция выпол-
няется с применением метода p u t r e f _ A c t i v e C o n n e c t i o n :
if(SUCCEEDED(hr))
hr = cmd->putref_ActiveConnection(cn);
И наконец, текст команды записывается методом put_CommandText:
if(SUCCEEDED(hr})
hr = cmd->put_CommandText(bstrCommand};
В нашем случае будет исполняться выборка всех записей из таблицы mana-
gers. Переменная bstrCommand, содержащая эту команду, инициализируется сле-
дующим образом:
CString strCommandC"select * from managers"};
BSTR bstrCommand;
bstrCommand = strCommand.AllocSysStringO;
Теперь мы можем выполнять команду при помощи метода Execute:
ADORecordset* rs = NULL;
if(SUCCEEDED(hr))
hr = cmd->Execute(&vtEmpty, &vtEmpty2, adCmdText, &rs);
Параметр adCmdText указывает, что команда представляет собой строку про-
граммы SQL. В результате выполнения команды будет создан набор записей
R e c o r d s e t , причем указатель на интерфейс соответствующего объекта будет за-
писан в переменную rs типа A D O R e c o r d s e t * .
Работа с набором записей
Как обычно, извлечение отдельных записей из набора необходимо выполнять в
цикле.
. Для проверки условия завершения цикла программа должна вызывать метод
get_EOF, определенный в объекте Recordset:
Глава 4. Связь приложений с базами данных через ADO 225

VARIANT_BOOL bEOF = VARIANT,FALSE;


if(SUCCEEOEDChr))
hr = rs->get_EOF(&bEOF);
По достижении конца набора записей этот метод вернет значение, равное
константе VARIANT,FALSE.
Цикл обработки набора записей выглядит, например, так:
while(b£OF == VARIANT,FALSE)
'

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.
Базы данных в Интернете. Практическое руководство

Чтобы перейти к обработке следующей записи набора, мы вызываем метод


MoveNext:
hr = rs->MoveNext();
if(!SUCCEEDED(hr))
break;
Заметим, что отсутствие исключений заставляет нас выполнят!, проверку
кода завершения каждого вызываемого метода, что увеличивает объем листинга.
Пример программы
В этом разделе мы приведем несложный пример программы, обращающейся к
ADO без импортирования библиотеки типов. Эта программа, так же как и пре-
дыдущая, показывает в консольном окне содержимое таблицы сотрудников
m a n a g e r s . Для сокращения листинга мы не стали проверять идентификатор и
пароль пользователя, запускающего эту программу.
Исходный текст программы Вы найдете в листинге 4-60.
Листинг 4-60 хранится в файле ch4\CPPAD01\CPPADO1.cpp на прилагаемом к
книге компакт-диске.
Переменные strAccessConnect, strEmpty и strCommand класса CString предназ-
начены для инициализации строчных переменных класса BSTR с именами bstrAc-
cessConnect, bstrEmpty и bstrCommand соответственно:
CString strAccessConnect("DSN=BookStore;Uin=dbo;PWD=;" );
BSTR bstrAccessConnect;

CString strEmptyC" 1 );
BSTR bstrEmpty;

CString strCofflmand("select * from managers");


BSTR bstrCommand;
В строке bstrAccessConnect записана строка параметров, необходимых для
подключения к источнику данных. Переменная b s t r E m p t y представляет собой
пустую строку, а переменная b s t r C o m m a n d содержит строку SQL, с помощью ко-
торой мы получим все записи из таблицы m a n a g e r s .
Кроме того, нам потребуются две пустые переменные vtEmpty и vtEmpty2 клас-
са VARIANT:
VARIANT vtEmpty;
VARIANT vtEmpty2;
В области глобальных переменных нашей программы находится определение
переменной c o m _ i n i t класса C o m l n i t , предназначенной для выполнения иници-
ализации переда началом работы программы и для освобождения ресурсов пе-
ред ее завершением:
struct Comlnit

ComlnitO
:
: :CoInitialize(NUI_L);

bstrAccessConnect = strAccessConnect. AllocSysStringQ;


Глава 4. Связь приложений с базами данных через ADO 227

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))

cerr « _Т("Ошибка инициализации MFC") « endl;


nRetCode = 1;

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);
Базы данных в Интернете. Практическое руководство

Если данный объект был успешно создан, мы записываем в свойство Соппес-


tionString объекта Connection строку параметров соединения, вызывая для этого
метод p u t _ C o n n e c t i o n S t r i n g :
if(SUCCEEDED(hr))
hr = cn->put_ConnectionString(bstrAccessConnect);
Далее соединение с источником данных открывается методом Open. Так как
все параметры соединения уже записаны в свойство ConnectionString. мы ука-
зываем первые три параметра метода Open как пустые, передавая через них стро-
ку bstrEmpty:
if(SUCCEEDED(hr))
hr = cn->Open(bstrEmpty, bstrEmpty, bstrEmpty,
adConnectUnspecified);
На следующем этапе происходит создание объекта Command, для чего мы опять
применяем функцию CoCreatelnstance, но с другими параметрами. Указатель на
интерфейс созданного объекта команд записывается в переменную cmd:
ifCSUCCEEOED(hr))
hr = CoCreateInstance(CLSID_CADOCommand, NULL,
CLSCTX_INPROC_SERVER, HD_IADOCommand, (LPVOID*)&cmd);
Чтобы связать объект Command и соединение, мы записываем указатель на
интерфейс соединения в свойство A c t i v e C o n n e c t i o n объекта C o n n e c t i o n , вызы-
вая метод p u t r e f _ A c t i v e C o n n e c t i o n :
if(SUCCEEDED(hr))
hr = cmd->putref_ActiveConnection(cn);
Текст команды записывается методом put_CommandText в соответствующее
свойство объекта Command:
if(SUCCEEDEDChr))
hr = cmd->put_CommandText(bstrCommand);
Теперь можно выполнять команду, вызывая метод Execute. В качестве первых
двух параметров мы передаем этому методу пустые переменные класса VARIANT.
Третий параметр определяет, что необходимо выполнить строку SQL, заданную
в свойстве CommandText объекта Command. И наконец, через последний параметр
методу Execute передается адрес переменной, в которую будет записан указатель
на объект класса Recordset, содержащий набор записей, извлеченных из табли-
цы managers:
if(SUCCEEDED(hr})
hr = cmd->Execute(&vtEmpty, &vtEmpty2, adCmdText, &rs);
После выполнения команды в цикле при помощи рассмотренной ранее ме-
тодики извлекаются отдельные записи набора R e c o r d s e t , и результат выводит-
ся на консоль.
Перед запуском цикла мы определяем переменные класса C o l e V a r i a n t , в ко-
торые будут записаны значения, извлеченные из строк таблицы:
COleVariant vManagerlD;
COleVariant vName;
COleVariant vPassword;
COleVariant vLastLogin;
COleVariant vRights;
Глава 4. Связь приложений с базами данных через ADO 229

Кроме того, мы определяем рабочую переменную strTmp класса CString, а


также переменную bEOF, которая будет использована для обнаружения конца
набора записей:
CString strTmp = "";
VARIANT_BOOL bEOF = VARIANT_FALSE;
Далее мы извлекаем признак конца набора записей при помощи метода
get_EOF:
if(SUCCEEDED(hr))
hr = rs->get_EOF(&bEOF);
В переменной adoFields находится указатель на интерфейс объекта Fields,
с помощью которого мы извлечем записи из набора:
ADOFields* adoFields = NULL;
Следующие переменные предназначены для хранения указателей на интер-
фейсы объектов Filed, создаваемых для хранения содержимого отдельных по-
лей текущей записи набора Recordset:
ADOField* fldManagerlD = NULL;
ADOField* fldName = NULL;
ADOField*fldPassword = NULL;
ADOField*fldLastLogin = NULL;
ADOField*fldRights = NULL;
Сам цикл организован следующим образом:
whilefbEOF == VARIANT_FALSE)
{
hr = rs->get_Fields(&adoFields);
if(!SUCCEEDED(hr))
break;

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;

8trTmp.Format("Xs I K10s | X10s | X20s | 5(10s",


v2str(vManagerID), v2str(vName),
v2str(vPassword), v2str(vLast Login), v2str(vRights));

cout « (LPCTSTR)strTmp « "\n";

hr = rs->MoveNext();

if(!SUCCEEDED(hr)>
break;

hr = rs->get_EOF(&bEOF);
if(!SUCCEEDED(hr))
break;

В начале цикла мы получаем очередную строку набора записей, вызывая


метод get_Fields для нашего набора записей.
Затем мы по очереди извлекаем поля текущей записи методом get_Item, a
затем и значения полей при помощи метода get_Value.
Далее в цикле выполняется преобразование извлеченных значений в тексто-
вую строку и вывод полученного результата на консоль. При этом использует-
ся функция v2str.
Перед очередной итерацией цикла наша программа получает следующую
запись набора, вызывая метод MoveNext. Сразу после этого извлекается признак
достижения конца набора записей- который используется для проверки условия
завершения цикла.
После обработки всех извлеченных записей мы закрываем набор записей, а
также соединение с источником данных:
rs->Close();
cn->Close();
ГЛАВА 5

Связь приложений с базами данных


через OLE DB

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


объектного интерфейса ADO в серверных сценариях ASP и в автономных при-
ложениях Microsoft Windows, написанных на языке программирования C++. Как
Вы смогли убедиться, и в том, и в другом случае применяются достаточно эф-
фективные методы обращения к базам данных. Эти методы не вызывают особых
затруднений при реализации и отладке. Интерфейс автоматизации, определен-
ный в рамках объектов ADO, позволяет обращаться к этим объектам из прило-
жений, составленных практически на любом языке программирования, в том
числе из языков сценария (таких, как JScript и VB Script).
Мы также говорили, что объекты ADO представляют собой объектный ин-
терфейс уровня приложений, созданный на базе другого объектного интерфей-
са, а именно OLE DB. Этот интерфейс — открытый стандарт, разработанный
специально для предоставления доступа приложениям к базам данных, как ре-
ляционных, так и нереляционных (таких, как серверы почты, базы данных
VSAM и т. д.).
Создавая приложения OLE DB, Вы можете реализовать в нем как функции
провайдера данных, так и функции потребителя данных. Заметим, что при со-
здании приложений с базами данных в Интернете и в интрасетях в большинстве
случаев применяют функции потребителя данных, используя какой-либо гото-
вый провайдер (например, для интерфейса ODBC или для текстовых файлов).
Необходимость в создании собственного провайдера может возникнуть лишь
для обращений к нестандартной базе данных, поэтому в нашей книге мы не бу-
дем рассматривать этот случай.
Применение объектного интерфейса OLE DB в большинстве приложений,
созданных для Интернета, нам представляется необязательным, а в некоторых
случаях и нежелательным. Фактически все операции с реляционными базами
данных можно выполнять в рамках объектного интерфейса ADO. Именно эта
технология, простая в применении и отладке, рассматривается Microsoft как
наиболее современная и подходящая для создания приложений Интернета. Тем
не менее, для того чтобы у Вас сложилась более полная картина, мы рассмотрим
некоторые случаи реализации этого метода доступа в автономных приложени-
ях Windows, написанных с использованием Microsoft C++.

93ак. 3571
232 Базы данных в Интернете. Практическое руководство

В рамках одной главы невозможно рассказать об объектах OLE DB хоть


сколько-нибудь подробно, поэтому мы изложим только основы. Вы сможете
применить полученные знания па практике, создавая, например, расширения
сервера Web, обращающиеся к базам данных через OLE DB, в виде приложений
CGI или ISAPI.

Программная модель OLE DB


Так же как и в случае только что рассмотренной объектной модели ADO, базо-
выми элементами программной модели OLE DB является набор объектов. Эти
объекты применяются для установки соединения с базами данных и сеансов,
выполнения команд с параметрами, получения результата выполнения этих ко-
манд в виде переменных или наборов записей, обработки событий и ошибок.
Рассмотрим порядок обращения приложения к базе данных с применением
программной модели OLE DB.

Инициализация среды выполнения


Работа OLE DB основана на модели компонентных объектов СОМ, поэтому
сразу после начала своей работы приложение должно выполнить инициализа-
цию системы СОМ. Как правило, обычные приложения выполняют эту иници-
ализацию вызовом функции C o l n i t i a l i z e .
В результате становится возможным загрузка объектов провайдера OLE DB
и работа с этими объектами.

Инициализация источника данных


Прежде чем обращаться к данным, приложение OLE DB должно установить
соединение с источником данных. Это действие требуется и при использовании
метода доступа ADO, однако установка соединения с применением объектов
OLE DB выполняется по-другому.
Во-первых, для установки соединения приложение должно создать массив
структур свойств, содержащих информацию для выполнения аутентификации.
Как минимум требуется указать имя источника данных, имя пользователя и
пароль.
Во-вторых, приложение должно вызвать метод S e t P r o p e r t i e s интерфейса
IDBProperties, выполняющий инициализацию указанных выше свойств. Интер-
фейс IDBProperties становится доступным после инициализации OLE DB.
И наконец, в-третьих, приложению необходимо вызвать метод Initialize ин-
терфейса IDBInitialize, что обязательно для инициализации источника данных.
После завершения работы с соединением его надо закрыть, вызвав метод
U n i n i t i a l i z e интерфейса IDBInitialize.
Открытие сеанса
Сеанс играет роль, аналогичную соединению с источником данных в ADO. В
рамках сеанса приложение может выдавать команды, выполняя те или иные
операции с источником данных.
Для открытия сеанса приложение использует метод CreateSession интерфей-
са IDBCreateSession.
Глава 5- Связь приложений с базами данных через OLE DB 233

Подготовка команды и параметров


При создании сеанса методом CreateSession интерфейса IDBCreateSession про-
грамма получает указатель на интерфейс IDBCreateCommand, позволяющий созда-
вать команды.
Устанавливая методом SetProperties интерфейса ICommandProperties различ-
ные атрибуты команды, программа влияет на ее исполнение.
Далее необходимо задать текст команды. Эта операция выполняется методом
SetCommandText интерфейса ICommandText. Текст команды представляет собой
строку языка Transact-SQL, имя хранимой процедуры SQL Server или имя таб-
лицы.
При необходимости средствами метода Prepare интерфейса ICommandPrepare
программа может выполнить предварительную подготовку команды. Эта опера-
ция имеет смысл, если команда представляет собой строку языка Transact-SQL
(а не хранимую процедуру) и будет выполняться многократно.
Если команда имеет параметры (например, команда запуска хранимой про-
цедуры с параметрами), необходимо описать параметры команды, создав груп-
пу структур-описателей доступа, называемых Accessor.

Выполнение команды
Для выполнения команды программа должна вызвать метод Execute интерфей-
са ICommandText.
После этого необходимо освободить объект, использованный для выдачи
команды.
В результате выполнения команды может быть создан набор записей, состо-
ящий из строк. По своему назначению этот набор записей аналогичен набору
класса Recordset, создаваемый в приложениях ADO.

Обработка результатов выполнения команды


Результаты выполнения команды OLE DB представлены наборами строк, отфор-
матированными в виде таблицы. Для извлечения данных из набора Вы должны
использовать ряд интерфейсов.
Прежде всего, Вам потребуется интерфейс IColumnsInfo. С его помощью про-
грамма получит информацию о столбцах набора данных. Интерфейс IRowsetlnf о
обеспечивает программу информацией о самом наборе записей. С помощью
интерфейса lAccessor программа выполнит привязку данных полученной таб-
лицы к переменным, определенным н программе. И наконец, интерфейс IRowset
нужен для получения данных из строк набора записей.
Обычно при извлечении данных из набора записей приложение вначале вы-
зывает метод CreateAccessor интерфейса lAccessor, выполняя привязку данных
к переменным. Далее все записи набора извлекаются порциями в цикле с помо-
щью метода GetNextRows интерфейса IRowset. А затем программа вызывает ме-
тод GetData интерфейса IRowset для получения данных из строк набора и запи-
си этих данных в переменные, указанные в процессе привязки.

Обработка ошибок
Обработка ошибок, возникающих при применении методов OLE DB, намного
сложнее, чем обработка ошибок, связанных с использованием ADO.
234 Базы данных в Интернете. Практическое руководство

Ошибки могут возникать при создании многочисленных объектов OLE DB,


поэтому Ваше приложение должно проверять код завершения соответствующих
функций. Если метод какого-либо интерфейса завершился с ошибкой и вернул
соответствующее значение, необходимо его проанализировать. При этом Вам
потребуются интерфейсы I S u p p o r t E r r o r l n f o , l E r o r l n f o , l E r r o r L o o k u p , l E r r o r -
Records и I S Q L E r r o r l n f o . В таблице 5-1 кратко описаны перечисленные интер-
фейсы.
Таблица 5-1. Интерфейсы, связанные с обработкой ошибок
Интерфейс Описание
I S u p p o r t E r r o r l n f o Этот интерфейс автоматизации позволяет определить, какие
объекты, описывающие ошибочные ситуации, можно получить от
объекта, вызвавшего появление ошибки. В частности, он позволяет
получить список таких объектов, создаваемых для получения ин-
формации об ошибках OLE DB
lErorlnfo Данный интерфейс автоматизации позволяет получить сообщение
об ошибке, имя компонента, вызвавшего ошибку, глобальный уни-
кальный идентификатор объекта GUID, создавшего ошибку, а так-
же имя файла справочной системы и название раздела справочной
системы, описывающего ошибку
lErrorLookup Этот интерфейс обеспечивается провайдером OLE DB. Использу-
ется интерфейсами Z E r r o r R e c o r d s и l E r o r l n f o
lErrorRecords Интерфейс для доступа к объектам, описывающим возникшие
ошибки OLE DB
ISQLErrorlnfo Данный интерфейс нужен для получения значения SQLSTATE и
естественного сообщения об ошибке. Обеспечивается провайдера-
ми ODBC

Объекты 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

Здесь через первый параметр мы передаем функции C o C r e a t e l n s t a n c e кон-


станту CLSID_MSDASQL, содержащую идентификатор класса SQLOLEDB.
Третий параметр функции CoCreatelnstance, заданный с помощью макроко-
манды CLSCTX_INPROC_SERVER, определяет, что источник данных SQLOLEDB работает
как встраиваемый в процесс сервер (in-process server).
Четвертый и пятый параметры задают соответственно идентификатор интер-
фейса I D B I n i t i a l i z e и адрес переменной p I D B I n i t i a l i z e , в которую записыва-
ется указатель на интерфейс I D B I n i t i a l i z e .
Если объект создан успешно, значение SUCCESS(hr) будет равно t r u e . В этом
случае Ваша программа может продолжить работу с источником данных, выпол-
няя его инициализацию и создание сеанса.
После завершения работы с источником данных программа должна вызвать
метод U n i n i t i a l i z e объекта SQLOLEDB, воспользовавшись для этого интерфейсом
IDBInitialize:
pIDBInitialize->Uninitialize();
Далее программа освободит не нужный более указатель на интерфейс IDBIni-
tialize, вызвав метод Release:
pIDBInitialize->Release();

Подготовка параметров инициализации


Для инициализации источника данных нам нужно создать массив структур
DBPROP, элементы которого будут содержать параметры инициализации (свойства
объекта SQLOLEDB).
Структура DBPROP определена следующим образом:
typedef struct tagDBPROP
{
DBPROPID dwPropertylD;
DBPROPOPTIONS dwOptions;
DBPROPSTATUS dwStatus;
DBID colid;
VARIANT vValue;
} DBPROP;
Ее поля описаны в таблице 5-2.
Таблица 5-2. Поля структуры DBPROP
Поле Описание
dwPropertylD Идентификатор свойства, параметры которого описаны в данной
структуре
dwOptions Параметры свойства. В это поле Вы можете записать константы
DBPROPOPTIONS.REQUIRED и л и D B P R O P O P T I O N S . O P T I O N A L .
Первая из них означает, что для данного свойства нужно обяза-
тельно задать значение, а вторая указывает, что данное свойство
задавать не обязательно
dwStatus Результат записи или чтения свойства. Устанавливается провайде-
ром и может использоваться для получения информации о том,
успешно ли выполнилась эта операция
236 Базы данных в Интернете. Практическое руководство

Таблица 5-2. Поля структуры DBPROP (продолжение)


Поле Описание
colid Дополнительный идентификатор столбца, к которому относится
данное свойство. Если свойство относится ко всем столбцам, в это
поле необходимо записать значение D B _ N U L L I D
Walue Значение свойства (типа VARIANT).
Если прочитало свойство, значение которого не установлено и для
которого не предусмотрено значения по умолчанию, в это поле
записывается константа VT_EMPTY.
В том случае, когда Вы задали в этом поле константу VT_EHPTY при
установке свойства, для инициализации свойства используется
значение по умолчанию
Возможные значения, устанавливаемые провайдером источника данных в
поле dwStatus, перечислены в таблице 5-3.
Таблица 5-3. Значения поля dwStatus
Значение Описание
DBPROPSTATUS_OK Значение свойства было установлено без ошибок
DBPROPSTATUS_BADCOLUMN Было указано неправильное значение поля colid струк-
туры DBPROP
DBPROPSTATUS_BADOPTION Было указано неправильное значение поля dwOptions
структуры DBPROP
DBPROPSTATUS_BADVALUE Неправильное значение в поле v V a l u e
DBPROPSTATUS.CONFLICTING Значение свойства не может быть установлено, так как
это приведет к конфликту с параметрами других
свойств
DBPROPSTATUS.NOTALLSETTABLE Была сделана попытка установить свойство для всех
столбцов, однако для некоторых столбцов это свойство
установить невозможно
DBPROPSTATUS NOTSET Свойство не было установлено в указанное значение.
Причина этого в том, что для параметра d w O p t i o n s ука-
зано значение в виде константы
DBPROPOPTIONS.OPTIONAL, и при этом установка свойства
в заданное значение невозможна
DBPROPSTATUS NOTSETTABLE Была сделана неудачная попытка установить значение
свойства, доступного только для чтения. Это же значе-
ние свидетельствует, что сделана попытка изменить
значение свойства в группе свойств после выполнения
инициализации источника данных
DBPROPSTATUS NOTSUPPORTED Установка свойства не была выполнена из-за того, что
провайдер не поддерживает данное свойство или набор.
Это з н а ч е н и е возвращается и в том случае, если потре-
битель данных предпринял попытку прочитать или
изменить значение свойств, не входящих в группу
инициализации, а источник данных не проинициали-
зирован
Глава 5. Связь приложений с базами данных через OLE DB 237

Итак, для инициализации соединения с источником данных нам нужен мас-


сив структур DBPROP, элементы которого описывают свойства источника данных.
В простейшем случае требуется задать четыре свойства:
ф уровень приглашения, определяющий, нужно ли выводить на экран пригла-
шения для пользователя;
ф имя источника данных DSN;
# имя пользователя;
ф пароль пользователя для доступа к источнику данных.
Для хранения этих свойств мы создаем массив rglnitProperties из четырех
элементов:
DBPROP rgInitProperties[4];
Далее нужно выполнить инициализацию элементов массива rglnitProperties.
В поле d w P r o p e r t y l D первого элемента массива (уровень приглашения) мы
записываем константу DBPROP_INIT_PROMPT:
rgInitProperties[OJ.dwPropertylD = DBPROP_INIT_PROMPT;
Таким образом обозначается, что данный элемент массива будет содержать
параметры свойства, определяющего уровень приглашения.
Данное свойство имеет отношение ко всем столбцам, поэтому в поле colid мы
указываем значение DB_NULLID:
rgInitProperties[0].colid = DB_NLJLLID;
Свойство, определяющее уровень приглашения, является обязательным (как
и все остальные три свойства нашего массива). Поэтому в поле d w O p t i o n s ука-
зана константа DBPROPOPTIONS_REQUIRED:
rgInitProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
Установка значения свойства выполняется в три приема:
VariantInit(&rgInitProperties[0].vValue);
rgInltProperties[0].vValu9.vt = VT_I2;
rgInitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;
Вначале инициализируется поле v V a l u e , имеющее тип V A R I A N T , с помощью
функции V a r i a n t l n i t . В результате такой инициализации в ноле будет записа-
но значение VT_EMPTY.
Далее мы указываем в поле vt тип данных как VT_I2 (двухбайтовое целое со
знаком), а затем записываем в поле iVal константу DBPROMPT_NOPROMPT. Эта кон-
станта указывает, что в процессе инициализации пользователю не следует вы-
водить на экран никаких приглашений.
Все возможные значения данного свойства перечислены в таблице 5-4.
Второй элемент массива r g l n i t P r o p e r t i e s определяет имя источника данных.
Идентификатор соответствующего свойства задается в поле d w P r o p e r t y l D с по-
мощью константы DBPROP_INIT_DATASOURCE:
rglnitPropertiesfl].dwPropertylD = DBPROP_INIT_DATASOURCE;
В поля dwOptions и colid этого и следующих двух элементов массива rglnit-
P r o p e r t i e s мы записываем значения DBPROPOPTIONS_REQUIRED и DB_NULLID соот-
ветственно:
238 Базы данных в Интернете. Практическое руководство

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

typedef struct tagDBPROPSET

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 Базы данных в Интернете. Практическое руководство

лена одна структура OBPROPSET, описывающая массив DBPROP, поэтому значение


первого параметра равно 1.
Для наглядности-мы показали на рис. 5-1 взаимосвязь структур DBPROPSET и
DBPROP при установке свойств источника данных.

plDBPraperties->SetProperties(3, &rglnitPropSet);

DBPROPSET

rgProperties

cProperties
guidPropertySet
(Improperly ID

dwOptions

dwStatus

vValiie

Рис. 5-1 . Задание свойств источника данных


Здесь мы подготовили массив из трех структур DBPROPSET, поэтому первый
параметр метода SetProperties интерфейса IDBProperties имеет значение, рав-
ное 3.
После того как программа установила свойства методом SetProperties, ин-
терфейс IDBProperties станет нам не нужен. Поэтому мы освобождаем указатель
на интерфейс методом Release:
pIDBProperties->Release();

Инициализация объекта
Последний этап в инициализации объекта провайдера источника данных — вы-
зов метода Initialize интерфейса IDBInitialize:
if(FAILED((pIDBInitialize)->Initialize()))
,

Напомним, что указатель па этот интерфейс мы получили на этапе создания


объекта провайдера источника данных функцией CoCreatelnstance:
hr = CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPHOC_SEflVER,
IID.IDBInitialize, (void**)&pIDBInitialize);
В том случае, если значения свойств указаны правильно, инициализация
пройдет успешно. Теперь можно переходить к созданию сеанса и выдаче команд,
Для этого нам опять потребуется интерфейс IDBInitialize.
Глава 5. Связь приложений с базами данных через OLE DB 241

Объект 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

Третий параметр определяет указатель на структуру DBPARAMS, применяемую


для запуска команд с параметрами. В этой книге мы не будем рассматривать
такие команды. Если параметров нет, Вы можете задать здесь значение N U L L .
Через четвертый параметр методу Execute передается указатель на перемен-
ную, куда после выполнения команды записывается счетчик строк, на которые
повлияла команда. Например, при создании набора записей в результате выпол-
нения команды SELECT в эту переменную будет записано количество строк в
образованном наборе записей.
После выполнения команды мы должны освободить указатель на интерфейс
ICommandText, вызвав метод Release:
pICommandText->Release();

Интерфейс IRowset и набор записей


Как мы только что сказали, в результате выполнения команды методом Execute
мы получаем указатель на интерфейс IRowset. Этот интерфейс используется для
извлечения результатов выполнения команды, то есть для извлечения данных
из полей строк набора записей, созданного командой.
Помимо этого интерфейса, нам потребуются интерфейсы I C o l u m n s I n f о ,
I R o w s e t l n f o , и lAccessor.
Для извлечения данных из набора записей нам предстоит выполнить следу-
ющие процедуры:
4 получить описание столбов набора записей при помощи метода GetColumnlnfo
интерфейса I C o l u m n s I n f o ;
ф выполнить привязку д а н н ы х набора к переменным программы с помощью
метода CreateAccessor интерфейса lAccessor;
4 извлечь идентификаторы строк набора методом G e t N e x t R o w s интерфейса
IRowset;
ф извлечь данные из полей строк методом GetData интерфейса IRowset.
Рассмотрим поэтапное выполнение этих процедур.
Получение описания набора записей
На первом этапе обработки набора записей нам нужно получить описание стол-
бцов набора, вызвав метод 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 .
Для этого мы вначале получим указатель на интерфейс IColumnsInfo, восполь-
зовавшись для этого указателем на интерфейс IRowset, который стал доступным
после выполнения команды:
IColumnsInfo* plColumnsInfo;
pIRowset->QueryInterface(IID_IColumnsInfo, (void**)&pIColumnsInfo));
Указатель на интерфейс I C o l u m n s I n f o извлекается с помощью метода Q u e r y -
I n t e r f a c e . Через первый параметр мы передаем этому методу идентификатор
интерфейса I C o l u m n s I n f o в виде константы I I D _ I C o l u m n s I n f o , а через второй
параметр — указатель на переменную, в которую будет записан указатель на
интерфейс I C o l u m n s I n f o .
Далее мы вызовем метод 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 :
244 Базы данных в Интернете. Практическое руководство

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

Таблица 5-6. Поля структуры DBPROPSET (продолжение)


Поле Описание
bScale Количество цифр справа от десятичной точки для данных типа
DBTYPE_DECIMAL или DBTYPE_NUMERIC

columnid Идентификатор столбца


После получения информации о столбцах набора мы должны освободить
ненужный в дальнейшей работе указатель на интерфейс I C o l u m n s I n f о:
pIColumnsInfo->Release();
Сведения, полученные на этом этапе, потребуются нам для выполнения при-
вязки полей из строк набора записей к переменным, определенным в нашем
п р и л о ж е н и и . Особый интерес вызывает информация, полученная в полях
d w F l a g s (характеристики столбца) и wType (тип данных).
Поле d w F l a g s может содержать константы, объединенные логической опера-
цией «ИЛИ» (таблица 5-7).
Таблица 5-7. Константы для заполнения поля d w F L a g s
Константа Описание
DBCOLUMNFLAGS_CACHEDEFERRED Отсроченное чтение с кэшированием
DBCOLUMNFLAGS^ISBOOKMARK Столбец содержит закладку bookmark
DBCOLUMNFLAGS_ISCHAPTER Столбец содержит значение оглавления (chapter value)
D8COLUMNFLAGS_ISFIXEDLENGTH Все данные в столбце имеют одну и ту же длину
DBCOLUHNFLAGS_ISLONG Столбец содержит данные типа BLOB
DBCOLUMNFLAGSISNULLABLE Этот флажок устанавливается в том случае, если столбец
может содержать значения NULL или если провайдер ис-
точника данных не способен определить, может ли зна-
чение полей столбца быть равно N U L L
DBCOLUMNFLAGS.ISROWID Столбец содержит идентификатор сохраняемой строки
DBCOLUMNFLAGS_ISROWVER Столбец содержит отметку о времени (timestamp) или
другое значение, используемое для отслеживания версий
записей, которое нельзя обновлять при помощи опера-
ций прямой записи
DBCOLUMNFLAGS_MAYBENULL Ноля столбца могут содержать значения N U L L
DBCOLUHNFLAGS_MAYDEFER Отложенный столбец. Данные такого столбца не извле-
каются из источника данных до тех пор, пока потреби-
тель данных не попытается их извлечь
DBCOLUMNFLAGS WRITE Для данного столбца можно вызывать метод SetData
интерфейса IrowsetChange, предназначенного для изме-
нения записи. Если этот флажок не установлен, записи
данного столбца доступны только для чтения.
Этот флажок не используется совместно с
DBCOLUMNFLAGS_WRITEUNKNOWN
DBCOLUMNFLAGS WRITEUNKNOWN Данные в столбце обновляются как посредством метода
SetData интерфейса I r o w s e t C h a n g e , так и с применением
других механизмов.
Этот флажок не используется совместно с
DBCOLUMNFLAGS WRITE
246 Базы данных в Интернете. Практическое руководство

Что же касается типа данных, указанного в поле wType, то в таблице 5-8 мы


перечислили некоторые возможные значения для провайдера сервера Microsoft
SQL Server.
Таблица 5-8. Типы данных

Тип в 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

Таблица 5-8. Типы данных (продолжение)


Тип в Microsoft
Тип данных OLE DB SQL Server Описание
DBTYPE DBTIMESTAMP smalldatetime Структура типа DBTIMESTAMP:
datetime typedef struct tagDBTIMESTAMP

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

Таблица 5-9. Поля структуры DBBINDING (продолжение)


Поле Описание
eParamlO Используется для привязки данных параметров. Определяет направ-
ление передачи данных и может принимать следующие значения:
DBPARAMIO_INPUT (входной параметр), DBPARAMIO_OUTPUT (выходной
параметр), DBPARAMIO_NOTPARAH (привязка используется не для пара-
метров). Константы DBPARAMIQJENPUT и DBPARAHIO_OUTPUT могут объе-
диняться логической операцией ИЛИ, если параметр является одно-
временно и входным, и выходным
cbHaxLen Размер памяти, которую должен заказать потребитель для хранения
полученных данных
dwFlags Флаги характеристик столбца. Набор флажков определен в рамках
перечисления D B C O L U M N F L A G S
wType Тип данных, расположенных в столоне
tiPrecision Максимальная точность численных данных или длина строки, пред-
ставляющей данные других типов
bScale Количество цифр справа от десятичной точки для данных типа
DBTYPE.DECIMAL или DBTYPE_NUHERIC

Заполнение массива структур DBBINDING удобно выполнять в цикле.


Перед запуском цикла нам нужно определить переменную n C u r r e n t C o l , в
которой будет храниться номер текущего столбца, а также переменную cbRow для
хранения текущего смещения поля в буфере строки:
ULONG nCurrentCol;
ULONG cbRow = 0;
Чтобы обнулить неиспользуемые поля структуры DBBINDING, мы применяем
функцию memset:
memset(pDBBind, 0, sizeof(DBBINDING) * nColsCount);
Цикл выглядит следующим образом:
for(ULONG nCurrentCol = 0; nCurrentCol < nColsCount; nCurrentCol++)
!

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, со-
держащего информацию о столбцах набора записей, полученного в результате
выполнения команды.

Выполнение привязки данных


После подготовки массива с информацией о привязке мы должны выполнить
привязку, создав объект Assessor.
Для этого мы вначале получаем указатель на интерфейс lAccessor и сохра-
няем его в переменной pIAccessor:
lAccessor* pIAccessor;
pIRowset->QueryInterface(IID_IAccessor, (voict**)&pIAccessor);
Далее мы создаем массив структур DBBINDSTATUS, размер которого равен ко-
личеству столбцов в полученном наборе данных:
D8BINDSTATUS* pDBBindStatus = NULL;
pDBBindStatus = new DBBINDSTATUS[nColsCount];
Далее мы передаем количество столбцов в наборе данных, указатель на мас-
сив pDBBind с информацией о привязке данных, адрес переменной для храпения
идентификатора привязки h A c c e s s o r и указатель методу CreateAccessor интер-
фейса lAccessor на массив структур DBBINDSTATUS:
HACCESSORhAccessor;
pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, nColsCount,
pDBBind, 0, &hAccessor, pDBBindStatus);
Через первый параметр методу CreateAccessor передаются флажки свойств
объекта привязки, которые определяют назначение этого объекта, Возможные
значения флажков мы перечислены в таблице 5-10.
Третий параметр метода CreateAccessor задает количество байт в наборе па-
раметров и не используется при работе с наборами записей. Поэтому для него
указано нулевое значение.
Глава 5. Связь приложений с базами данных через OLE DB 251

Таблица 5-10. Флажки метода CreateAccessor


Флажок Описание
DBACCESSOR_INVALID Используется методом G e t B i n d i n g s для того, чтобы сооб-
щить программе о произошедшей ошибке
DBACCESSOR_PASSBYREF Объект ирииязки передает значения по ссылке
DBACCESSOR_ROWDATA Объект привязки описывает привязку столбцов набора
данных
DBACCESSOR_PARAMETERDATA Объект предназначен для привязки значений парамет-
ров команды
DBACCESSOR_OPTIMIZED Создается оптимизированный объект привязки, которая
касается использования внутренних буферов
Массив структур D B B I N O S T A T U S , указатель на который передается методу
C r e a t e A c c e s s o r через последний параметр, позволяет отследить результат п р и -
вязки для каждого столбца.
Структура DBBINDSTATUS определена как двойное слово, в которое записыва-
ются флаги результата привязки:
typedef DWORD DBBINDSTATUS;
Эти флажки перечислены в таблице 5-11.
Таблица 5-11. Флажки результата привязки
Флаг Описание
DBBINDSTATUS_OK Успешное выполнение привязки
DBBINDSTATUS_BADORDINAL Неправильно указан номер столбца
DBBINDSTATUS_UNSUPPORTEDCONVERSION Провайдер не может выполнить затребованное
преобразование данных
DBBINDSTATUS.BADBINDINFO Неправильное значение параметров в поле
dwPart структуры параметров привязки
DBBINDSTATUS_BADSTORAGEFLAGS Ошибка при установке значения в поле
dwFlags структуры параметров привязки
DBBINDSTATUS_NOINTERFACE Отсутствует или недоступен интерфейс, необ-
ходимый для привязки данных

Обработка набора записей


Теперь, когда привязка данных выполнена, мы можем приступить к извлечению
данных из набора, созданного в результате выполнения команды.
Эта операция выполняется в тройном вложенном цикле.
Внешний цикл вызывает метод GetNextRows интерфейса IRowset, как это по-
казано ниже:
HROW rghRows[30];
HROW* pRows = &rghRows[0];
ULONG cObtainedRows;
while(TRUE)
{
pIRowset->GetNextRows(0, 0, 30, &cObtainedRows, &pRows );
iffcObtainedRows == 0)
break;
(см. след, стр.)
252 Базы данных в Интернете. Практическое руководство

// Обработка строк

pIRowset->ReleaseRows(cObtainedRows, rghRows, NULL, NULL, NULL);


}
Первый параметр метода GetNextRows определяет идентификатор оглавления
набора записей и в нашем случае равен нулю. Второй параметр задает началь-
ное смещение при обработке набора записей. Мы обрабатываем набор с самого
начала и определяем для этого параметра нулевое значение. Через третий пара-
метр мы передаем количество строк, извлекаемых для обработки из набора за-
писей за один прием (мы задали произвольное значение, равное 30). Если ука-
зать для этого параметра отрицательное значение, выборка записей будет выпол-
няться в обратном направлении (от конца набора записей к его началу).
С помощью четвертого параметра мы предаем методу GetNextRows адрес пе-
ременной, в которую метод запишет количество строк, извлеченных из набора
записей. И наконец, пятый параметр задает адрес массива для записи иденти-
фикаторов извлеченных строк.
После обработки извлеченных строк мы освобождаем ресурсы, связанные с
извлеченными строками, с помощью метода Releaseflows.
Обработка извлеченных строк выполняется в цикле второго уровня вложен-
ности:
char* pRowValues;
pRowValues = new char[cbRow];
ULONG iRowCounter;

for (iRowCounter=0; iRowCounter < cObtainedRows; iRowCounter++)


.
pIRowset->GetData(rghRows[ iRowCounter], hAccessor, pflowVaLues);

// Обработка полей строки

Здесь мы получаем отдельные строки из блока строк, извлеченных только что


рассмотренным методом GetNextRows, используя для этого массив идентифика-
торов строк rghRows и метод GetData.
В качестве первого параметра методу GetData передается идентификатор из-
влекаемой строки. Второй параметр предназначен для передачи идентификато-
ра объекта привязки.
Данные строки записываются методом GetData в область памяти p R o w V a l u e s ,
передаваемой методу GetData через третий параметр. Размер этой области хра-
нится в переменной cbRow. Он был вычислен на этапе привязки данных.
Теперь мы зададим обработку значений отдельных полей текущей записи,
извлеченной из набора. Она выполняется в цикле третьего уровня вложенности:
ULONG nCurrentCol;
for (nCurrentCol = 0; nCurrentCol < nColsCount; nCurrentCol++)
\
if Тип данных, хранящихся в поле:
Глава 5. Связь приложений с базами данных через OLE DB 253

// pColInfo[nCurrentCol].wType

// Данные из текущего поля


// pRowValues[pDBBind[nCurrentCol].obValue]

В этом цикле мы можем ссылаться как на массив p C o l I n f o , содержащий пол-


ную информацию о столбцах (имя столбца, тип данных и т. д.), а также на мас-
сив значений p R o w V a l u e s .
По завершении обработки набора записей Ваша программа должна освобо-
дить объект привязки и указатель на интерфейс lAccessor:
pIAccessor->ReleaseAccessor(hAccessor, NULL);
p!Accessor->Release();
Первая операция выполняется с помощью метода ReleaseAccessor, а вторая —
методом Release.

Программа 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 Базы данных в Интернете. Практическое руководство

Помимо обычных для консольных приложений Windows файлов windows. h


и stdio.h, мы включили в исходный текст файлы oledb.h, oledberr.h, msdaguid.h
и msdasql.h:
#include <oledb. h>
flinclude <oledberr. h>
«include <msdaguid.h>
^include <msdasql.h>
Файлы oledb.h и oledberr.h предназначены для определений объектов и ин-
терфейсов OLE DB, а файлы msdaguid.h и msdasql.h относятся к провайдеру
ODBC, использованному нами для создания источника данных.
Для инициализации системы СОМ перед началом работы программы и для
освобождения ресурсов СОМ перед завершением программы мы определили в
области глобальных переменных уже знакомую Вам но предыдущей главе пере-
менную c o m _ i n i t класса Comlnit:
struct Comlnit
{
ComlnitQ
{
::CoInitialize(NULL);

"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;

В начале своей работы функция main выполняет инициализацию источника


данных, вызывая для этого функцию init, определенную в нашей программе.
Если инициализация выполнилась успешно, функция main запускает команду,
извлекающую из таблицы clients информацию о покупателях. В том случае,
если команда выполнена без ошибок, вызывается функция get_records, извле-
кающая или отображающая в консольном окне строки таблицы clients.
При возникновении каких-либо ошибок в работе программы функция m a i n
освобождает указатели на интерфейсы IMalloc, IDBInitialize и IRowset. Пред-
варительно она убеждается в том, что указатели не содержат нулевые значения.
Перед освобождением указателя па интерфейс IDBInitialize мы вызываем ме-
тод U n i n i t i a l i z e , освобождающий ресурсы, полученные программой при ини-
циализации источника данных.
Функция init
Перед тем как приступить к инициализации источника данных, функция init
получает память для системы СОМ, вызывая для этого функцию CoGetMalloc:
if(FAILED(CoGetMalloc(MEMCTX_TASK, SpIMalloc)))
return false;
В результате в глобальную переменную pIMalloc записывается указатель на
интерфейс системы управления памятью СОМ. Приложение может использо-
вать этот указатель для вызова методов интерфейса IMalloc, таких, как АПос,
Realloc, Free и т. д. Память, полученная таким образом, применяется в много-
поточной среде.
Наша программа заказывает памя гь неявно, обращаясь к интерфейсам OLE
DB, однако, прежде чем завершить работу, функция get_records вызывает ме-
тод Free интерфейса IMalloc для освобождения памяти, заказанной для описа-
ния столбцов набора записей.
Дальнейшие действия, выполняемые функцией i n i t , мы подробно описали
в начале этой главы.
Вначале функция создает объект I D B I n i t i a l i z e и получает указатель на со-
ответствующий интерфейс:
256 Базы данных в Интернете. Практическое руководство

CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,


IID_IDBInitlalize, (void*-)&pIOBInitialize);
if (pIDBInitialize == NULL)
return false;
Далее функция init готовит массив свойств r g l n i t P r o p e r t i e s и, пользуясь
указателем на интерфейс IDBInitialize, выполняет инициализацию источника
данных:
VariantlnitC&rglnitProperties[0].vValue);
rgInitProperties[0].vValue.vt = VT_I2;
rgInitProperties[0].Walue.iVal = OBPROMPT_NOPROMPT;
rg!nitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;

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 Базы данных в Интернете, Практическое руководство

и обращаемся к полям структуры DBTIMESTAMP для отображения даты регистра-


ции посетителя в форматированном виде.
Прежде чем завершить работу, функция get_records удаляет заказанные мас-
сивы и освобождает память, полученную для буферов:
delete [] pDBBind;
delete [] pRowValues;
delete [] pDBBindStatus;

pIMalloc->Free(pColStringsBuffer);
pIMalloc->Free(pColInfo);

Использование библиотеки шаблонов ATL


Как показано в примере из предыдущего раздела, прямое использование объект-
ного интерфейса OLE DB заставляет разработчика вникать во множество дета-
лей, имеющих отношение к технологии применения модели компонентного
объекта СОМ. Например, Вам придется явным образом получать указатели на
интерфейсы, а потом заботиться об их освобождении, проверять коды заверше-
ния методов и функций, создавать сложные взаимосвязанные структуры для
привязки данных и т. д.
Однако те из Вас, кто пользуется для создания приложений системой про-
граммирования Microsoft Visual C++ версии 6.0 или более новой, получают воз-
можность заметно сократить объем кода, не имеющего непосредственного отно-
шения к выполнению операций с базой данных, а. значит, сконцентрироваться
на решении своей прикладной задачи. Такую возможность предоставляет биб-
лиотека шаблонов ActiveX Template Library (ATL).
Она существенно упрощает как создание новых элементов управления
ActiveX, так и использование готовых элементов управления ActiveX, к которым
можно отнести объекты OLE DB. Шаблоны этой библиотеки обеспечивают про-
стой доступ к возможностям OLE DB, упрощают процесс привязки данных на-
боров записей и параметров процедур, а также допускают использование есте-
ственных типов данных, привычных для разработчиков программ C++.
Создавая приложения OLE DB с использованием библиотеки шаблонов ATL,
Вы должны применять для работы с источником данных специальный набор
классов. Его мы рассмотрим в этом разделе. Эти классы скрывают внутреннюю
сложность обращения к объектам OLE DB, предоставляя в распоряжение про-
граммистов относительно простой набор методов и свойств.
Для использования этих классов в исходные тексты Вашего приложения
необходимо включить оператором #include файл atldbcli.h:
«include <atldbcli.h>

Класс 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 Базы данных в Интернете. Практическое руководство

Commit (фиксация транзакции), Abort (отмена транзакции) и GetTransactionlnfo


(получение информации о транзакции).
Класс CCommand
Класс CCommand обеспечивает методы для выполнения команд. Он определен
следующим образом:
template <class TAccessor = CNoAccessor, class TRowset = CRowset, class TMultiple
= CNoMultipleResults>
class CCommand :
public CAccessorRowset<TAccessor, TRowset>,
public CConmandBase,
public TMultiple
Здесь класс TAccessor представляет собой класс объекта привязки Accessor,
с которым мы уже имели дело в этой главе, TRowset — класс набора записей,
создаваемого при выполнении команды, и Tmultiple, который используется с
командами, возвращающими одновременно несколько результатов.
Несмотря па устрашающий вид определения класса CCommand, пользоваться
им достаточно просто. Вот как мы создаем объект cmd этого класса:
CCommand <CAccessor<tabClients> > cmd;
Здесь мы ссылаемся на класс tabClients, определенный в нашем приложении
для выполнения привязки данных:
class tabClients
(
public:
TCHAR m_ClientID[6];
TCHAR m_UserID[50];
ГСНАН m_Password[50];
DBTIHESTAMP m_RegisterDate;
TCHAR m_Email[80];

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

В классе tabClients мы расположили определения полей, сделанные с при-


менением обычных типов данных C++. Кроме этого, в этом классе есть описа-
ние столбцов набора записей, сделанное при помощи макрокоманд BEGIN_CO-
LUMOAP, COLUMN_ENTRY и END_COLUMN_MAP.
Через единственный параметр макрокоманде BEGIN_COLUMN_MAP нужно указать
имя класса привязки. В нашем случае это tabClients.
Макрокоманда C O L U M N _ E N T R Y , предназначенная для выполнения привязки
данных, имеет два параметра — номер столбца и имя поля данных записи.
И наконец, макрокоманда END_COLUMN_MAP, закрывающая определение столб-
цов набора, не имеет параметров.
Для того чтобы запустить команду на выполнение, Вы должны воспользо-
ваться методом Open, определенным в классе CCommand:
TCHAR mySQL[] =
"SELECT ClientID, UserlD, Password, RegisterDate, Email FROM clients";

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;

Б случае возникновения ошибок программа завершает свою работу с кодом


возврата 1.
Далее программа выполняет команду:
TCHAR mySQLt] = "SELECT ClientID, UserlD, Password, RegisterDate, Email FROM
clients";

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);

cout « setw(30) « szBuf « endl;

Здесь мы просто выводим извлеченные значения в выходной поток cout.


выполняя форматирование манипуляторами ввода/вывода (установку ширины
колонки и выравнивание).
Что же касается форматирования поля даты регистрации, то здесь мы при-
менили уже знакомый Вам трюк, связанный с использованием структуры
DBTIMESTAMP. Создав переменную ts типа DBTIMESTAMP, мы записали в нее значе-
ние, полученное из поля cmd.m_RegisterDate. Далее мы выполняем преобразо-
вание формата, обращаясь к полям переменной ts.
Перед завершением своей работы программа закрывает объект команды, се-
анс и соединение с источником данных:
cmd.CloseC);
sSession.CloseO;
dsDSN.CloseO;
ГЛАВА 6

Связь приложений с базами данных


через ODBC

Последний метод доступа, о котором мы расскажем в нашей книге, это Microsoft


Open Database Connectivity (ODBC). Интерфейс ODBC представляет собой
набор предназначенных для доступа к базам данных функций программного
интерфейса. Этот набор предполагает использование структурного языка запро-
сов SQL.
Для того чтобы интерфейс ODBC стал доступен программам, необходимо
установить драйвер ODBC. Такой драйвер имеется в составе Microsoft SQL
Server, а также в составе многих других СУБД, рассчитанных на работу в среде
операционных систем Microsoft Windows, Macintosh и некоторых версий Unix.
При создании приложений с базами данных для Интернета интерфейс ODBC
пригодится Вам для связи расширений сервера Web (таких, как программы CGI
и приложения ISAPI) с базами данных. При этом вызов функций ODBC будет
выполняться как непосредственно из программ расширений, так и через дочер-
ние процессы, запускаемые расширениями для обращения к базам данных (это
иногда требуется для повышения устойчивости сервера Web к программным
ошибкам, допущенным при обращении к базе данных).
Заметим, что в отличие от ADO, интерфейс ODBC не является объектным.
Поэтому он недоступен из серверных сценариев JScript и VB Script, расположен-
ных в страницах ASP.
В нашей книге мы не будем описывать все возможности ODBC — речь пой-
дет только о самых необходимых. Мы, в частности, расскажем о том, как посред-
ством этого интерфейса программы могут выполнять предложения языка SQL
и запускать хранимые процедуры с входными и выходными параметрами. Имен-
но эти операции чаще всего нужны при создании реальных приложений.

Программный интерфейс ODBC


В этом разделе речь пойдет о назначении и параметрах основных функций про-
граммного интерфейса ODBC.
Помимо этих функций, обеспечивающих прямой доступ к ODBC, корпора-
ция Microsoft разработала ряд программных интерфейсов и классов, предназна-
ченных для обращения к ODBC. Это такие интерфейсы, как RDO и DAO, а так-
же классы MFC и ОАО.
266 Базы данных в Интернете. Практическое руководство

Интерфейсы RDO и ОАО предназначены главным образом для создания


приложений Visual Basic, а классы MFC и DAO — для создания приложений на
основе Microsoft Visual C++, к тому же имеющих интерактивный интерфейс
пользователя.
При разработке приложений Интернета для интерактивной части проекта
обычно применяется технология ASP, серверные сценарии и ADO, о чем мы
подробно рассказывали в первых главах нашей книги. Что же касается расши-
рений сервера Web, то они не имеют никакого непосредственного интерфейса
пользователя. Поэтому классы MFC и ОАО, ориентированные на автоматизи-
рованное создание диалоговых приложений средствами мастеров Microsoft
Visual C++, не окажут Вам заметной помощи. Именно поэтому мы расскажем
Вам только о непосредственном интерфейсе ODBC.

Структура приложения ODBC


Программа, обращающаяся к базам данных посредством интерфейса ODBC,
обычно выполняет следующие действия:
ф инициализирует среду выпол нения;
4 подключается к источнику данных;
f создает и выполняет команды;
ф обрабатывает результат выполнения команды;
ф освобождает ресурсы, полученные для работы с ODBC.
Если команда предназначена для выполнения хранимой процедуры, прило-
жение должно выполнить привязку входных и выходных параметров этой про-
цедуры (а также при необходимости и кода завершения процедуры) к локаль-
ным переменным, определенным внутри тела программы.
Обработка результата также связана с привязкой данных к локальным пере-
менным. Вы уже знакомы с этой процедурой из предыдущей главы, посвящен-
ной интерфейсу OLE DB. Хотя приложения ODBC выполняют эту операцию
по-другому, смысл ее не меняется — устанавливается соответствие между ло-
кальными переменными и полями набора записей, полученных при выполнении
команды SQL.
Нетрудно заметить, что ранее мы говорили о тех же самых действиях, что
применяются для работы с базами данных, но выполняемых посредством мето-
дов доступа ADO и OLE DB. Действительно, набор действий остается тем же
самым. Меняется только способ их реализации.
Рассмотрим назначение функций ODBC, вызываемых на различных этапах
работы приложения ODBC.

Инициализация
Для выполнения инициализации среды выполнения приложение 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

В качестве первого параметра hType функции SQLAllocHandle передается тип


идентификатора. Это может быть идентификатор среды SQL_HANDLE_ENV, иденти-
фикатор соединения с источником данных SQL_HANDLE_DBC, идентификатор стро-
ки SQL_HANDLE_STMT или дескриптора SQL_HANDLE_DESC.
Второй параметр i n p H a n d l e определяет, в каком контексте нужно получить
идентификатор, Здесь могут быть указаны константы SQL_NULL_HANDLE, SQL_HAND-
LE_ENV или SQL_HANDLE_DBC.
И наконец, третий параметр определяет адрес переменной, в которую запи-
сывается идентификатор, полученный функцией SQLAllocHandle.
При успешном завершении функция S Q L A l l o c H a n d l e возвращает значения
SQL_SUCCESS или SQL_SUCCESS_WITH_INFO. Первая из этих констант возвращается,
если операция завершилась успешно. Вторая константа также означает успеш-
ное завершение и, кроме того, сообщает дополнительную информацию.
В том случае, если функция SQLAllocHandle завершилась с ошибкой, возвра-
щается значение SQL_INVALID_HANDLE или SQL^ERROR.
Перечисленные выше константы возвращаются многими функциями про-
граммного интерфейса ODBC. Немного позже мы расскажем о том,-как програм-
мы обрабатывают ошибочные ситуации, извлекая коды ошибок и тексты сооб-
щений об ошибках.
Перед завершением своей работы приложение должно освободить идентифи-
каторы, полученные при вызове функции SQLAllocHandle. Для этого предназна-
чена функция SQLFreeHandle. Она имеет два параметра:
SQLRETURN SQLFreeHandle (SQLSMALLINT hType, SQLHANDLE hHandle);
Через первый параметр функции S Q L F r e e H a n d l e передается тип идентифика-
тора (такой же, как и функции S Q L A l l o c H a n d l e ) , а через второй — освобождае-
мый идентификатор.

Инициализация среды выполнения


Рассмотрим фрагмент кода приложения, выполняющий инициализацию среды
для выполнения запроса к базе данных.
Прежде всего программа должна получить идентификатор среды типа
SQL_HANDLE_ENV:
SQLHENV hEnv = SQL_NULL_HENV;
RETCODE гс;
гс = SQLAllOcHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
Он записывается в переменную hEnv.
На следующем этапе нам нужно установить атрибуты среды, в частности
указать номер версии ODBC, равный 3. В результате приложение сможет вос-
пользоваться особенностями этой версии драйвера. Так, драйвер ODBC будет
работать с новыми кодами даты и времени SQL_TYPE_DATE, SQL J"YPE_TIME, SQL_TY-
PE_TIMESTAMP, SQL_C_TYPE_DATE, SQL_C_TYPE_TIME, SQL_C_TYPE_TIMESTAMP, a также -
возвращать коды SQLSTATE при обработке ошибок.
Атрибуты среды устанавливаются с помощью функции S Q L S e t E n v A t t r , име-
ющей четыре параметра:
-С = SQLSetEnvAttrChEnv, SQLJVTTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3, SQL_IS,INTEGER);
268 Базы данных в Интернете. Практическое руководство

Через первый параметр этой функции передается идентификатор среды, для


которой выполняется настройка атрибутов.
Второй параметр задает атрибут, подлежащий редактированию. В частности,
значение SQL_ATTR_ODBC_VERSION используется при настройке версии драйвера
ODBC.
Третий параметр функции SQLSetEnvAttr задает значение атрибута (для чис-
ленных значений) или адрес строки атрибута (для атрибутов, заданны к в виде
текстовой строки).
Смысл четвертого параметра зависит от типа значения атрибута, передавае-
мого через третий параметр. Если значение числовое (как в нашем случае), чет-
вертый параметр определяет тип значения атрибута, а если строчное — длину
строки.
Инициализация среды для установки соединения
На втором этапе инициализации Вам надо получить идентификатор соединения
типа SQL_HANDLE_DBC, который создается в контексте только что созданного иден-
тификатора среды SQL_HANDLE_ENV:
SQLHDBC hDbC = SQL_NULL_HDBC;
re = SQLAllocHandle(SQL_HANDLEJ)BC, hEnv, &hDbc);
Обратите внимание, что мы передаем значение функции S Q L A l l o c H a n d l e че-
рез второй параметр идентификатор hEnv, имеющий тип SQL_HANDLE_ENV.
Теперь, после выполнения второго этапа инициализации, у нас уже есть два
идентификатора — hEnv (идентификатор среды выполнения) и hDbc (идентифи-
катор среды соединения).

Установка соединения
Для установки соединения с источником данных Вам придется воспользовать-
ся функцией 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

szUserName, (SWORD)strlen( (const cnar-)szUserName),


szPassword, (SWORD)strlen((const char*)szPassword));
В случае успешного соединения функция SQLConnect вернет значение SQL_SUC-
CESS или SQL_SUCCESS_WITH_INFO, а при ошибке - значение SQL_INVALID_HANDLE или
SQL_ERROR.

Подготовка и запуск команды


Программный интерфейс ODBC предоставляет несколько возможностей для
выполнения команд, здесь, однако, мы рассмотрим только один, связанный с
использованием функции SQLExecDirect.
Выполнение команды с применением этой функции состоит из двух этапов.
На первом нужно создать идентификатор команды типа SQL_HANDLE_STMT, а на
втором — выполнить команду, вызвав функцию SQLExecDirect.
Получение идентификатора команды
Получение идентификатора команды реализуется обычным способом с приме-
нением функции SQLAllocHandle:
SQLHSTMT hStmt = SQL_NULL_HSTMT;
гс = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, fchStmt);
Обратите внимание на то, что идентификатор команды создается в контек-
сте идентификатора соединения типа SQL_HANDLE_DBC, поэтому через второй па-
раметр мы перелаем функции S Q L A l l o c H a n d l e значение hDbc. Идентификатор
соединения hDbc был получен на предыдущем этапе инициализации.
Запуск команды
Для запуска команды на выполнение надо вызвать функцию SQLExecDirect, про-
тотип которой расположен ниже:
SQLRETURN SQLExecDirect(SQLHSTMT hStatement,
SQLCHAR* StatementText, SQLINTEGER StatementTextLength);
Через первый параметр этой функции передается идентификатор команды
типа SQL_HANDLE_STMT, через второй — строка команды, а через третий — длина
строки команды.
Если команда выполнена без ошибок, функция SQLExecDirect возвращает
значение SQL_SUCCESS или SQL_SUCCESS_WITH_INFO, а при ошибке- значение
SQL_INVALID_HANDLE или SQL_ERROR. Возможно также, что Вы получите коды воз-
врата SQL_NEED_DATA, SQL_STILL_EXECUTING И SQL_NO_DATA.
Значение SQL_NEEO_DATA возвращается при использовании так называемых
параметров времени исполнения (data-at-execution parameter). Такой тип пара-
метров предполагает использование для передачи данных функций SQLParamData
и SQLPutData. Мы в своих программах не будем применять параметры времени
исполнения.
Значение SQL_NO_DATA возвращается при выполнении команд, не вызывающих
создания выходных наборов записей. К ним относятся, например, команды уда-
ления или обновления строк.
А сейчас мы покажем пример вызова команды, выполняющей выборку полей
M a n a g e r l D , Name, Password, L a s t L o g i n и R i g h t s из таблицы managers:
270 Базы данных в Интернете. Практическое р'/ководство

гс = SQLExecDirect(hStmt, (unsigned char*)


"select ManagerlD, Name, Password, Lastlogin, Rights from managers", SOLENTS);
В качестве длины строки, передаваемой функции SQLExecOirect через послед-
ний параметр, мы указали константу SQL_NTS. Эта константа означает, что дли-
на строки определяется закрывающим ее двоичным нулем. Таким образом, нам
не надо определять длину строки команды явным образом при помощи такой
функции, как s t r l e n .

Обработка результата выполнения команды


Если в результате выполнения команды создан набор записей, программа может
его получить посредством соответствующих функций интерфейса ODBC. Од-
нако перед тем как это сделать, она должна выполнить привязку полей набора
записей к локальным переменным, определенным внутри программы.
Привязка полей к локальным переменным
Такая операция выполняется функцией SQLBindCol:
SQLRETURN SQLBindCol(SQLHSTMT hStatement,
SQLUSMALLINT NinnberOfColumn,
SQLSMALLINT VariableType,
SQLPOINTER VariableValuePtr,
SQLINTEGER BufferSize,
SQLINTEGER* cbSize);
Параметр hStatement задает идентификатор команды, для которой выполня-
ется привязка. Это как раз тот идентификатор, с использованием которого фун-
кция SQLExecDirect выполняла команду.
Через параметр NumberQfColumn необходимо передать номер столбца набора
данных, созданного в результате выполнения команды. Если в наборе нет стол-
бца bookmark, то нумерация выполняется, начиная с единицы, а если есть — то с
нуля.
Параметры V a r i a b l e T y p e и V a r i a b l e V a l u e P t r задают соответственно тип ло-
кальной переменной, для которой выполняется привязка, и адрес этой локаль-
ной переменной.
С помощью параметра B u f f e r S i z e необходимо указать размер области памя-
ти, отведенной для локальной переменной с адресом V a r i a b l e V a l u e P t r (с учетом
двоичного нуля, закрывающего текстовую строку). Это значение используется
драйвером ODBC в качестве ограничителя, для тою чтобы избежать запись дан-
ных за пределами буфера. Такая ситуация иногда возникает при извлечении из
базы данных содержимого полей переменной длины.
Значение параметра B u f f e r S i z e итерируется при записи данных в перемен-
ные фиксированной длины типа целых чисел.
Параметр cbSize бывает как входным, так и выходным. Он задает адрес пе-
ременной, в которую записывается размер данных, прочитанных функцией из-
влечения записей SQLFetch (или SQLFetchScroll). Функция S Q L B u l k O p e r a t i o n s
использует этот параметр как входной.
В качестве примера приведем фрагмент кода, выполняющего привязку одно-
го числового поля, одного текстового поля и одного поля, содержащего отмет-
ку о времени:
Глава 6. Связь приложений с базами данных через ODBC 271

Локальные переменные, которые будут привязаны к полям, определены так;


SQLINTEGER nManagerlD;
SQLCHAfl szName[MAXNAME + 1];
SQLJTMESTAMP_STRUCT tsLastLogin;
Мы выполним привязку к полям таблицы managers с именами ManagerlD, Name
м LastLogin.
Вот фрагмент кода для первого из них:
SQLINTEGER cbManagerlD;
гс = SQLBindCoKhStmt, 1, SQL_C_SLONG, &nManagerID,
sizeof(nManagerlD), &cbManagerID);
Здес]. выполняется привязка первого столбца, поэтому мы передаем функции
SQLBindCol через второй параметр значение 1. Константа SQL_C_SLONG задает тип
данных, соответствующий стандартному типу С « l o n g int>>. Константы для
других типов Вы найдете в таблице 6-1.
Хотя мы и указали в пятом параметре размер области памяти, выделенной
для переменной nManagerlD, он будет проигнорирован, так как тип SOL_C_SLONG
занимает область памяти фиксированного размера.
Привязка переменной szName, в которую будет записана текстовая строка,
выполняется аналогично:
SQLINTEGER CbName;
гс = SQLBindCoKhStmt, 2, SQL_C,CHAR, szName, MAXNAME, ScbName);
Здесь мы указали тин данных SQL_C_CHAR, соответствующий типу данных С
« u n s i g n e d char*». Константа MAXNAME ограничивает длину буфера, не позволяя
драйверу ODBC выйти за его границы в процессе записи данных из поля Name.
Для привязки поля L a s t L o g i n (содержащего отметку о дате и времени по-
следнего подключения), мы указали тип данных SOL_C_TYPE_TIMESTAMP:
SQLINTEGER cbLastLogin;
гс = SQLBindCoKhStmt, 3, SQL_C_TYPE_TIMESTAMP, StsLastLogin,
sizeof(tsLastLogin}, &cbLastLogin);
Значение, заданное нами в пятом параметре, будет проигнорировано. Мы
указали его только для общности.
В таблице 6-1 мы перечислили идентификаторы типов, передаваемых функ-
ции SQLBindCol, для разных типов данных. Там же Вы найдете и типы данных
ODBC, которые потребуются при вызове хранимых процедур SQL Server.
Таблица 6-1. Идентификаторы типов данных С и ODBC
Тип данных С Идентификатор Тип данныхОРВС
char SQL_C_STINYINT .SQLSCHAR
unsigned char SQL_C_BIT SQLCHAR
SQL_C_UTINYINT
unsigned char* SQL_C_CHAR SQLCHAR*
SQL_C_BINARY
SQL_C_VARBOOKMARK
Short int SQL_C_SSHORT SGLSMALLINT
unsigned short int SQL_C_USHORT SQLUSMALLINT
long int SQL_C_SLONG SQLINTEGER
272 Базы данных в Интернете. Практическое руководство

Таблица 6-1. Идентификаторы типов данных С и ODBC (продолжение)


Тип данных С Идентификатор Тип данных ODBC

unsigned long int SQLJMJLONG SQLUINTEGER


SQL_C_BOOKMARK BOOKMARK
float SQL^C.FLOAT SQLREAL

double SQL^C.DOUBLE SQLDOUBLE


SOLFLOAT

_int64 SQL_C_SBIGIMT SQLBIGINT

unsigned _int64 SQL_C_UBIGINT SQLUBIGINT


DATE.STRUCT SQLJLTYPEJJATE SQL_DATE_STRUCT
TIME_STRUCT SQL_C_TYPE_TIME SQL_TIME_STRUCT
TIMESTAMP.STRUCT SQL_C_TYPE_TIMESTAMP SQL.TIMESTAMP^STRUCT
SQL_NUMERIC_STRUCT SQL_C_NUMERIC SQL_NUHERIC_STRUCT
SQLGUID SQL_C_GUID SQLGUID

В таблице есть ссылки на типы данных DATE_STRUCT, TIME_STRUCT, TIMES-


TAMP_STRUCT, SQL_NUMERIC_STRUCT и SQLGUID. Эти структуры предназначены для
представления даты, времени, отметки о времени, числовых значений и глобаль-
ных уникальных идентификаторов GUID. Для удобства ниже мы приводим их
определения:
struct tagDATE STRUCT

SQLSMALLINT year; // год


SQLUSMALLINT month; // месяц
SQLUSMALLINTday; // день
} DATE_STRUCT;

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

SQLCHAR precision; // точность


Глава 6. Связь приложений с базами данных через ODBC 273

SQLSCHAR scale; // масштаб


SQLCHAR sign; // знак
SQLCHAR val[SQL_MAX_NUMERIC_LEN]; // значение
} SQL_NUMERIC_STRUCT;

struct tagSQLGUID
<
DWORD Datal;
WORD Data2;
WORD Data3;
BYTE Data4[8];
} SQLGUID;

Цикл обработки записей


После того как Ваше приложение выполнило привязку данных, оно может при-
ступать к извлечению записей из набора, созданного в результате выполнения
команды.
Эта операция выполняется в простом цикле, показанном ниже:
while((rc = SQLFetch(hStmt)) != SQL_NO_DATA)
!
// nManagerlD, szName, szPass, szRights
// tsLast Login. day, tsLast Login. month, tsLast Login. year,
// tsLast Login. hour, tsLast Login. minute, tsLast Login. second

Здесь для извлечения очередной записи набора мы использовали функцию


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

Обработка ошибок
Как мы уже говорили, в случае возникновения ошибок функции ODBC возвра-
щают значения, такие, как SQL_INVALID_HANDLE или SQL_ERROR. Однако в большин-
стве случаев такой информации недостаточно.
Для того чтобы получить развернутое описание ошибки, необходимо запро-
сить у драйвера ODBC диагностические записи. Эта операция выполняется с
применением функции SQLGetDiagRec. Отдельные поля диагностических запи-
сей извлекаются при помощи функции SQLGetDiagField.

Извлечение диагностических записей


Логика обработки ошибок, рекомендуемая в руководство Microsoft, предполага-
ет, что функции получения диагностических записей вызываются всякий раз,
когда функции ODBC возвращают значения, отличные от SQL_SUCCESS. Однако
274 Базы данных в Интернете. Практическое руководство

в некоторых случаях Вы можете проигнорировать дополнительные информаци-


онные сообщения, возникающие при коде возврата SQL_SUCCESS_WITH_INFO, не
рассматривая данную ситуацию как ошибочную,
Так как с одной ошибочной ситуацией иногда связано несколько диагности-
ческих записей, функция SQLGetDiagRec должна извлекать эти записи в цикле.
Рассмотрим прототип функции SQLGetDiagRec:
SQLRETURN SQLGetDiagRec(
SQLSMALLINT hType,
SQLHANDLE hHandle,
SQLSMALLINT nRecord,
SQLCHAR* SQLState,
SQLINTEGER* pNativeErrorPtr,
SQLCHAR* pMessageText,
SQLSMALLINT obBuffer,
SQLSMALLINT* pcbText);
Через параметр hType Вы передаете тип идентификатора, для которого нуж-
но получить диагностику. Здесь Вы можете указать хорошо знакомые значения
SQL_HANDLE_ENV, SQL_HANDLE_DBC, SQL_HANDLE_STMT и SOL_HANDLE_DESC. Например,
если ошибка возникла при выполнении команды, первый параметр должен со-
держать тип идентификатора команды SQL_HANDLE_STMT, если при создании со-
единения с источником данных — тип SQL_HANDLE_OBC, и т. д.
Параметр h H a n d l e используется для передачи самого идентификатора.
Через параметр n R e c o r d Вы должны передать номер извлекаемой записи.
Обычно значение этого параметра последовательно, начиная с единицы, увели-
чивается в цикле.
Параметр SQLState должен содержать указатель на буфер, в который записы-
вается код состояния SQLSTATE. Первые два символа в этом буфере указывают
класс состояния, а следующие три — подкласс. Подробную информацию о ко-
дах состояния Вы найдете в документации на Microsoft SQL Server.
Через параметр p N a t i v e E r r o r P t r необходимо передать указатель на буфер, в
котором будет храниться естественный код ошибки, зависящий от источника
данных.
Текст сообщения об ошибке записывается в буфер, адрес которого передает-
ся функции SQLGetDiagRec через параметр pMessageText. Размер этого буфера
должен быть указан с помощью параметра c b B u f f e r .
И наконец, последний параметр pcbText используется для передачи указате-
ля на переменную, в которую будет записана длина упомянутого выше сообще-
ния об ошибке.
Вот как, например, выглядеть цикл извлечения диагностических записей:
RETCODE re = SQL_SUCCESS;
SDWORD sdwNativeError = OL;
SWORD pcbErrorMessage = 0;
UCHAR szSQLState[256] = "";
UCHAR szErrorMessage[256] = "";
SQLSMALLINT nRecordNumber

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 = SQLGetDiagFieldfnHandleType, sqlhHandle, nRecordNumber,


SQL_DIAG_SS_LINE, inline, SQL_IS_INTEGER, NULL);

re = SQLGetDiagField(nHandleType, sqlhHandle, nRecordNumber,


SQL_DIAG_SS_MSGSTATE, SsdwMessageState, SQL_IS_INTEGER, NULL);

re = SQLGetDiagField(nHandleType,.sqlhHandle, nRecordNumber,
SQLJ)IAG_SS_SEVERITY, &sdwSeverity, SQL_IS_INTEGER, NULL);

re = SQLGetDiagField(nHandleType, sqlhHandle, nRecordNumber,


SQL_DIAG_SS_PROCNAME, &schProcName,
sizeof(schProcName), &cbProcName);

re = SOLGetDiagField(nHandleType, sqlhHandle, nRecordNumber,


SQL_DIAG_SS_SRVNAME, SschServerName, sizeof(schServerName),
icbServerName);

Здесь используются константы, перечисленные в таблице 6-2.


Таблица 6-2. Идентификаторы типов данных С и ODBC
Константа Описание
SQL_DIAG_ROW_NUMBER Номер строки в наборе записей или номер параметра в на-
боре параметров, с которым связана данная запись
SQL_DIAG_SS_PROCNAME Имя хранимой процедуры, вызвавшей ошибку
SQL_DIAG_SS_LINE Номер строки в хранимой процедуре, вызвавшей появление
ошибки
SQL_DIAG_SS_MSGSTATE Значение состояния, полученное от оператора R A I S E R R O R
SQL_DIAG_SS_SEVERITY Степень тяжести ошибки
SQL_DIAG_SS_SRVNAME Имя сервера, на котором произошла данная ошибка

Программа 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

Полные исходные тексты программы ODBCAPP находятся в листинге 6-1.


Листинг 6-1 Вы найдете в файле ch6\odbcapp\odbcapp.cpp на прилагаемом к
книге компакт-диске.

Глобальные определения и константы


Для обращения к функциям и константам программного интерфейса ODBC мы
включили в исходные тексты нашего приложения четыре файла:
^include <sql.h>
^include <sqlext.h>
((include <sqltypes.h>
ffinclude <odbcss.h>
Для формирования строки сообщения об ошибке используется шаблон stirng
из стандартной библиотеки шаблонов STL. Поэтому в исходном тексте мы сде-
лали такие определения:
^include <string>
using namespace std;
В области глобальных переменных задали следующие переменные:
SQLHENV hEnv = SQL_NULL_HENV;
SQLHDBC hDbc = SQL_NULL_HDBC;
SQLHSTMT hStmt = SQL_NULL_HSTMT;

В переменной hEnv хранится идентификатор среды исполнения, в перемен-


ной hDbc — идентификатор соединения с источником данных, а в переменной
hStmt - идентификатор команды.

Функция 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);

if(re != SQL^SUCCESS && re != SQL_SUCCESS_WITH_INFO)


return 1;

re = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);

(см. след, стр.)


278 Базы данных в Интернете. Практическое руководство

if(гс != SQL^SUCCESS && гс != SQL_SUCCESS_WITH_INFO>


return 1;

гс = SQLAllQcHandle(SQL_HANDLE_DBC, hEnv, &hDbc);

if(re != SQL_SUCCESS && re != SQL_SUCCESS_WITH_INFO)


return 1;
Обратите внимание, что пока мы не установили соединение с источником
данных, обработка ошибок заключается в простом завершении программы с
кодом 1. Дело в том, что на данном этапе средства расширенной диагностики
ошибок еще тте доступны.
Далее программа устанавливает соединение с источником данных:
гс = SQLConnect(hDbc,
szDSN, (SWGRD)strlen((const char*)szDSN),
szUserName, (SWORD)strlen((const char«)szUserName),
szPassword, (SWQRD)strlen((const char*)szPassword));

if(re != SQL_SUCCESS && re ! = SQL_SUCCESSJHTH_INFO)


{
GetErrorMsg(SQL_.HANDLE_DBC, hDbc, sErrMsg);
printf("SQLConnect error\nKs", sErrHsg.c_str(»;
return 1;

Если функция SQLConnect вернула код ошибки, мы вызываем функцию GetEr-
r o r M s g , определенную в нашей программе. О ней мы поговорим позже, а сейчас
только скажем, что эта функция извлекает заголовки диагностических записей
и формирует из них текстовую строку s E r r M s g , которая затем отображается на
консольном экране.
В том случае, если функции m a i n удалось открыть соединение с источником
данных, она создает идентификатор команды и запускает команду на выполнение:
гс = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);

if(re != SQL_SUCCESS && re != SQL_SUCCESS_WITH_INFO)

GetErrorMsgConn(SQL_HANDLE_DBC, hDbc, sErrMsg);


printf("SQLAllocHandle Error\n!is", sErrHsg.c_str());
return 1;

re = SQLExecDirect(hStmt, (unsigned char-)


"select ManagerlD, Name, Password, LastLogin, Rights from managers", SQL_NTS);

if(re != SQL_SUCCESS && re != SQL SUCCESS_WITH_INFO)


;
GetErrorMsgConn(SQL_HANDLE_STMT, hStmt, sErrMsg);
printf("SQLExecOirect Еггог\пХз", sErrMsg.c_str());
return 1;
Глава 6. Связь приложений с базами данных через ODBC 279

В качестве команды мы применили здесь простой оператор SELECT, извлека-


ющий несколько полей из таблицы m a n a g e r s .
Для обработки ошибочных ситуаций здесь вызывается функция G e t E r r o r -
MsgConn. Она извлекает не только заголовки диагностических записей, по и поля
записей состояния, формируя из этой информации строку сообщения об ошиб-
ке. Записи состояния становятся доступными только после установления соеди-
нения с источником данных, поэтому мы и подготовили две разные функции
обработки ошибок.
Следующий этап — привязка локальных переменных к полям набора записей,
извлеченных в результате выполнения команды:
SQLINTEGER nManagerlD;
SQLINTEGER cbManagerlD;
SQLCHAR szNane[MAXNAME + 1];
SQLINTEGER cbName;
SQLCHAR szPass[MAXNAME + 1];
SQLINTEGER cbPass;
SQL_TIMESTAMP_STRUCT tsLastLogin;
SQLINTEGER cbLastLogin;
SQLCHAR szflightstMAXNAME + 1];
SQLINTEGER cbRights;
!
re = SQLBindCoKhStmt, 1, SQL_C_SLONG, &nManagerID,
sizeof(nManagerlD), &cbManagerlD);
re = SQLBindCoKhStmt, 2, SQL_C_CHAR, szName, MAXNAME, &cbName);
re = SQLBindCoKhStmt, 3, SQL_C_CHAR. szPass, MAXNAME, &cbPass);
re = SQLBindCoKhStmt, 4, SQL_C_TYPE_TIMESTAMP, StsLastLogin,
sizeof(tsLastLogin), &cbLastLogin);
re = SQLBindCoKhStmt, 5, SQL_C_CHAR, szRights, MAXNAME, AcbRights);
Здесь мы последовательно привязываем пять локальных переменных nMana-
g e r l D , szName, szPass, t s L a s t L o g i n , s z R i g h t s к столбцам набора записей с имена-
ми ManagerlD, Name, Password, LastLogin, R i g h t s соответственно. В этой главе мы
уже рассказывали о том, как выполнить привязку данных с использованием
функции S Q L B i n d C o l .
После выполнения привязки функция main запускает цикл извлечения запи-
сей из набора, полученного в результате выполнения команды:
while((rc = SQLFetch(hStmt)) != SQL_NO_DATA)
I
if(cbLastLogin < 0)

printf("Hd | X10s | X t O s | X15S | (not logged yet)\n",


nManagerlD, szName, szPass, szRights);

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 Базы данных в Интернете. Практическое руководство

Записи извлекаются средствами функции SQLFetch. В теле никла мы выво-


дим на консоль содержимое извлеченных полей.
Так как поле L a s t L o g i n (дата последнего подключения сотрудника магазина)
может содержать значения NULL, мы обрабатываем этот случай отдельно. При-
знаком извлечения пустой записи N U L L является отрицательное значение пере-
менной c b L a s t L o g i n , содержащей размер извлеченных данных.
Освобождение ресурсов, полученных программой для обращения к базе дан-
ных, нужно выполнять следующим образом.
Вначале программа освобождает идентификатор команды, вызывая для это-
го функцию SQLFreeHandle:
SQLFreeHandle(SQL_HANDLE_STMT, hStint);
Далее выполняется отключение от источника данных при помощи функции
SQLDisconnect:
SQLDisconnect(hDbc);
И наконец, мы освобождаем идентификаторы соединения и среды исполнения:
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);

Функция GetEn orMsgConn


Эта функция выполняет извлечение заголовка диагностической записи, а так-
же полей записей состояния, созданных для данной диагностической записи.
Алгоритм извлечения записей мы уже рассматривали.
Опустив для краткости определения локальных переменных, приведем ис-
ходный текст цикла извлечения записей и формирования сообщения об ошибке:
while(rc != SQL_NO_DATA_FOUND)
I
гс = SQLGetDiagRec(nHandleType, sqlhHandle,
nRecordNumber, szSQLState, &sdwNativeError,
szErrorMessage, 255, &pcbErrorMessage);

if(re != SQL_NO_DATA_FOUND)
{
re = SQLGetDiagField(nHandleType, sqlhHandle, nRecordNumber,
SQL_DIAG_ROW_NUMBEfl, &nRowNumber, SQL_IS_INTEGER, NULL);

re = SQLGetDiagField(nHandleType, sqlhHandle, nRecordNumber,


SQL_DIAG_SS_LINE, SnLine, SQL_IS_INTEGER, NULL);

re = SQLGetDiagFieldCnHandleType, sqlhHandle, nRecordNumber,


SQL_DIAG_SS_MSGSTATE, SsdwMessageState, SQL_IS_INTEGER,
NULL);

re = SQLGetDiagField(nHandleType, sqlhHandle, nRecordNumber,


SQL_DIAG_SS_SEVERITY, SsdwSeverity, SQL_IS_INTEGER, NULL);

re = SQLGetDiagFieldfnHandleType, sqlhHandle, nRecordNumber,


SQL_DIAG_SS_PROCNAME, SschProcName, sizeof(schProcName),
ScbProcName);
Глава 6. Связь приложений с базами данных через ODBC 281

гс = SQLGetDiagField(nHandleType, sqlhHandle, nRecordNumber,


SQL_DIAG_SS_SRVNAME, SschServerName, sizeof(schServerName),
&cbServerName);

sErrMsg = "SQL State: ";


sErrMsg += (LPSTR)szSQLState;

wsprintf(szBuf, "5Ed", sdwNativeError);


sErrMsg += "\nNative Error: ";
sErrMsg += (LPSTR)szBuf;

sErrMsg += "\nError Message: ";


sErrMsg += (LPSTR)szErrorMessage;

wsprintf(szBuf, "%d", nRowNumber);


sErrMsg += "\nODBC Row Number: ";
sErrMsg += (LPSTR)szBuf;

wsprintf(szBuf, "Xd", sdwMessageState);


sErrMsg += "\nMessage State: ";
sErrMsg += (LPSTR)szBuf;

wsprintf(szBuf, "Xd", sdwSeverity);


sErrMsg += "\nSeverity: ";
sErrMsg += (LPSTR)szBuf;

sErrMsg += "\nProc Name': ";


sErrMsg += (LPSTR)schProcName;

sErrMsg += "\nServer Name: ";


sErrMsg += (LPSTR)schServerName;

nRecordNumber++;

Заголовок диагностической записи извлекается функцией SQLGetDiagRec.


Далее при помощи функции SQLGetDiagField извлекаются по очереди значения
записей состояния. Среди них есть как численные значения, так и текстовые
строки. Для формирования общей строки сообщения об ошибке мы преобразу-
ем численные значения в строки и добавляем к строке s E r r M s g , ссылка на кото-
рую передается функции GetErrorMsgConn.
Вот какая строка формируется в том случае, если мы допустили синтаксичес-
кую ошибку в операторе SELECT, указав, например, его как SELECTT:
SQLExecDirect Error
SQL State: 42000
Native Error: 156
Error Message: [Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect synta
x near the keyword 'from'.
ODBC Row Number: 1
Message State: 1
Severity: 15
(см. след, стр.)
282 Базы данных в Интернете. Практическое руководство

Ргос 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);

wsprintf(szBuf, "Sid", sdwNativeError);


if(re != SQL_NO_DATA_FOUND)

sErrMsg = "SOL State: ";


sErrMsg += (LPSTR)szSQLState;

sErrMsg += "\nNative Error: ";


sErrMsg += (LPSTR)szBuf;

sErrMsg += "\nError Message: ";


sErrMsg += (LPSTR)szErrorMessage;

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

Запуск хранимых процедур


Как уже говорилось ранее, применение хранимых процедур позволяет упростить
разработку и сопровождение проектов с базами данных за счет отделения про-
грамм от данных.
Практически при создании приложений для Интернета или интрасетей Вам
потребуется вызвать хранимые процедуры из серверных сценариев JScript или
VB Script, встроенных в страницы ASP, либо из расширений сервера Web, таких,
как программы CGI или приложения 1SAPI. В последнем случае мы рекомен-
дуем обращаться к бале данных Microsoft SQL Server с применением интерфейса
ODBC, а не ADO или OLE DB.
Именно поэтому, рассказывая о работе с методами доступа ADO и OLE DB
в приложениях, написанных на C++, мы опустили материал о вызове хранимых
процедур как второстепенный. Теперь же необходимо к нему вернуться, так как
уже в следующей главе мы займемся созданием расширений сервера Web, обра-
щающихся к базам данных через интерфейс ODBC.

Привязка параметров
Запуск процедур выполняется примерно таким же образом, что и запуск команд
при помощи функции 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 Базы данных в Интернете. Практическое руководство

Параметр nValueType определяет идентификатор типа данных С локальной


переменной, привязываемой к параметру. Здесь можно указывать такие констан-
ты, как SQL_C_CHAR или SQL_C_SSHORT. Полный список идентификаторов приве-
ден в таблице 6-1.
Тип параметра хранимой процедуры указывается через параметр п Р а г а -
m e t e r T y p e функции S Q L B i n d P a r a m e t e r . Здесь указываются такие типы данных
SQL, как SQL_CHAR или SQLSMALLINT (см. таблицу 6-1).
Параметр nColumnSize определяет максимальное количество символов, цифр
или точность данных, передаваемых через параметр.
С помощью параметра nDecimalDigits программа задаст количество десятич-
ных цифр маркера параметра.
Через параметр p P a r a m e t e r V a l u e программа передает функции S Q L B i n d -
Parameter указатель на буфер локальной переменной, которая должна быть при-
вязана к параметру хранимой процедуры. Длина этого буфера определяется
параметром c b B u f f e r S i z e .
И наконец, через параметр pcbParamSize Вы передаете указатель на перемен-
ную, в которую предварительно была записана длина параметра.
В следующем фрагменте кода мы выполняем привязку двух входных пара-
метров и одного выходного параметра:
SQLCHAR szAdminName[51];
SQLINTEGER cbAdminName = SQL_NTS;

гс = 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

начены для передачи имени пользователя и пароля, причем соответствующие


столбцы в базе данных могут содержать до 50 символов. Выходной параметр
предназначен для передачи строк, размером не более 16 символов.
Последние три параметра функции S Q L B i n d P a r a m e t e r описывают буферы,
привязываемые к соответствующим параметрам. Они имеют одинаковый размер
(51 символ с учетом двоичного нуля, закрывающего строку).

Запуск процедуры
Как мы уже говорили, для запуска на выполнение хранимых процедур можно
воспользоваться уже знакомой Вам функцией 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 означает, что при выполнении процедуры не был
создан набор записей. В зависимости от того, какие действия выполняет храни-
мая процедура, данную ситуацию можно считать нормальной или ошибочной.
Например, если процедура обновляет или удаляет записи в базе данных или в
зависимости от тех или иных условий возвращает выходные параметры, не из-
меняя базу данных, набор записей может и не создаваться.

Извлечение значений выходных параметров процедуры


Перед извлечением значений выходных параметров хранимой процедуры (а
также значения кода возврата процедуры) необходимо выполнить обработку
наборов записей, созданных при выполнении команды.
Если эти результаты не нужны, их можно проигнорировать, вызвав ее в цикле
функцию SQLMoreResults:
while((rc = SQLMoreResults(hStmt)) != SQL_NO_DATA)
•:
if(re != SQL.SUCCESS && re != SQL_SUCCESS_WITH_INFO)
break;
Базы данных в Интернете. Практическое руководство

После завершения цикла, когда функция S Q L M o r e R e s u l t s вернет значение


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

Программа ODBCPARAM
Применение описанных выше методик вызова хранимых процедур с параметра-
ми демонстрируется на примере консольной программы ODBCPARAM.
Программа ODBCPARAM запрашивает у пользователя идентификатор и
пароль, выполняет запрос к таблице m a n a g e r s базы данных Bookstore и затем
отображает права пользователя:
Login name: frolov
Password: 123

Your rights: Administrator


Если пароль или идентификатор указаны неправильно, вместо прав на кон-
соли появляется строка « n o t h i n g » :
Login name: frolov
Password: 111

Your rights: nothing


Здесь мы не будем описывать глобальные определения и переменные про-
граммы ODBCPARAM, так как они аналогичны определениям, примененным в
программе ODBCAPP (см. ранее в этой главе). Вместо этого мы сразу расска-
жем о функции main, выполняющей все основные действия.
Сразу после запуска функция main получает все необходимые идентифика-
торы и открывает соединение с источником данных. Ниже показаны фрагмен-
ты кода, выполняющие эти операции:
гс = SQLAllocHandle(SQL_HANDLE_ENV, N U L L , &hEnv);

гс = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION.
(SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);

re = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);

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

IF NOT EXISTSCSELECT * FROM managers)


Глава 6. Связь приложений с базами данных через ODBC 287

INSERT managers (Name, Password, Rights, LastLogin) VALUES(@>User, ©Pass,


'Administrator', GETDATEQ)

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;

printf("\nLogin name: ");


gets((char*)szAdminName);

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 = SQLBindParameterChStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR,


SQL_CHAR, 50, 0,
szAdminPass, 50, ScbAdminPass);
В обоих случаях мы отмечаем входные параметры константой SQL_PARAM_INPUT.
288 Базы данных в Интернете. Практическое руководство

Выходной параметр привязывается с применением константы S Q L _ P A -


RAM_OUTPUT:
SQLCHAR szAdminRights[51];
SQLINTEGER cbAdminRightS = SQLJITS;

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);

while((rc = SQLMoreResults(nStmt)) != SQL_NO_DATA)

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

Расширения CGI и ISAPI сервера Web

В предыдущих главах нашей книги мы познакомили Вас с различными метода-


ми доступа к базам данных, пригодных для использования в приложениях Ин-
тернета и в интрасетях. Большинство таких приложений, выполняющих доста-
точно сложные операции, можно построить с использованием технологии ASP.
Однако в некоторых случаях Вам придется создавать собственные расшире-
ния сервера Web в виде программ CGI и приложений 1SAPI, Как правило, та-
кие расширения, работающие на том же сервере, что и сервис Web, необходимы
для обращения к нестандартным интерфейсам, например к интерфейсам платеж-
ных систем компаний, занимающихся пропессингом кредитных карточек через
Интернет.
Если пропессинговая компания предоставляет в Ваше распоряжение интер-
фейсный модуль в виде объекта компонентной модели Microsoft COM, к нему
можно обращаться непосредственно из серверного сценария, расположенного на
страницах ASP. Однако чаще всего модуль поставляется в виде библиотеки ди-
намической загрузки DLL, экспортирующей набор функций. Такие функции
легко вызываются из программ, составленных на языке С, но они недоступны
программам серверного сценария JScript или VB Script, встроенного в страни-
цы ASP.
Необходимо отметить, что, если Вы по каким-либо причинам не можете или
не желаете применять технологию ASP, Вы вполне обойдетесь и без нее. К Ва-
шим услугам приложения с базами данных для Интернета или интрасетей, раз-
работанные с использованием одних только расширений сервера Web. Однако
этот путь представляется нам намного более трудным, так как он предполагает
программирование на уровне вызова функций программного интерфейса Win32.
К тому же программы расширений намного сложнее в отладке но сравнению с
серверными сценариями ASP.
В этой главе мы расскажем Вам о том, как создать собственные расширения
сервера Web, созданного на базе Microsoft Internet Information Server версии 4.0,
в виде программ CGI и приложений ISAPI.
Так как наша книга посвящена базам данных, то мы рассмотрим вопросы
интеграции приложений CGI и ISAPI с сервером базы данных Microsoft SQL
Server.
290 Базы данных в Интерноте. Практическое руководство

Программы CGI и базы данных


Если Вы хотя бы чуть-чуть занимались разработкой приложений для Интерне-
та, Вы наверняка слышали о программах CGT. Тем не менее мы позволим себе
немного рассказать о них.
Что кроется за аббревиатурой СОТ?
CGI — это стандартный шлюзовой интерфейс (Common Gateway Interface)
для запуска внешних программ под управлением сервера Web. Соответственно
приложениями CGI называются программы, которые, пользуясь этим интерфей-
сом, получают через протокол HTTP информацию от удаленного пользователя,
обрабатывают ее и возвращают результат обработки обратно в виде ссылки на
уже существующий документ HTML или другой объект (например, графичес-
кое изображение) или в виде документа HTML, созданного динамически.
Передача информации от удаленного пользователя приложению CGJ обыч-
но выполняется следующим образом.
В документе HTML, который создастся для ввода информации, предназна-
ченной для обработки, размещается форма ввода. О формах ввода мы уже рас-
сказывали в нашей книге. Они обычно содержат необходимые элементы управ-
ления, такие, как поля редактирования текстовой информации, переключатели,
списки и т, д. Каждому элементу управления присваивается произвольное имя.
Кроме того, в этой формах обычно предусматривается кнопка, которую следует
щелкнуть после заполнения всех полей,
Когда пользователь заполняет форму и щелкает указанную кнопку, данные
передаются приложению CGI, путь к которому задается в заголовке формы. Это
приложение получает через протокол HTTP данные из полей формы в виде «имя
поля/значение ».
После обработки полученных данных приложение CGI создает документ
HTML и записывает его в стандартное устройство вывода stdout. Этот документ
затем автоматически передается удаленному пользователю.
Так как приложение CGI представляет собой не что иное, как программу, Вы
должны оттранслировать ее для той операционной системы, под управлением
которой работает Ваш сервер Web. В нашем случае необходимо создать консоль-
ное приложение Win32 (не путайте его с консольной программой MS-DOS — это
разные вещи).
Заметим, что программы C.GI, создаваемые для операционной системы Unix,
часто составляют с применением интерпретируемого языка Perl. Однако паша
книга ориентирована па применение технологий Microsoft, поэтому мы не бу-
дем касаться этой темы. Заметим только, что для платформы Microsoft Win-
dows NT также существуют реализации интерпретатора языка Perl.

Немного о формах HTML


Чаще всего программы CGI (и приложения ISAPI, которые мы рассмотрим поз-
же) применяются для обработки данных, введенных посетителями сервера Web
при помощи форм. Хотя Вы уже имеете некоторый опыт в создании форм, мы
рассмотрим некоторые вопросы, касающиеся взаимодействия форм и расшире-
н и й сервера Web.
Глава 7. Расширения CGI и ISAPI сервера Web 291

Как Вы знаете, форма содержит элементы управления, посредством которых


пользователь вводит текстовые или цифровые значения, выбирает строки из
списков. В форме могут располагаться переключатели, обычные или графичес-
кие кнопки.
Для того чтобы сделать форму в документе HTML, следует воспользоваться
тегом <FORM>. Этот тег применяется в паре с тегом </FORM>, завершающим опи-
сание формы. Между тегами <FORM> и </FORM> находятся описания элементов
управления в виде таких тегов, как <INPUT>, <TEXTAREA> и <SELECT> с соответству-
ющими параметрами.
Бот пример определения простейшей формы:
<FORH METHOD=GET ACTION="http://www.myserver.ru/cgi/form.exe">
<TABLE>
<TR>
<TD><INPUT TYPE=text NAME="text1" VALUE="Sample of text1"x/TD>
</TR>
<TR>
<TD><INPUT TYPE=text NAME="text2 M VALUE="Sample of text2"X/TD>
</TR>
<TR>
<INPUT TYPE=submit VALUE="Send">
</TR>
</TABLE>
</FORM>
Здесь элементы управления размещаются в таблице, состоящей из одного'
столбца и трех строк. В верхних двух строках мы расположили поля ввода и
редактирования текста, а в последней строке — кнопку Send.
Назначение параметров тега <FORM> описаны в таблице 7-1.
Таблица 7-1. Парамегры тега <FORM>
Параметр Описание
ACTION Адрес URL для выполнения дейстштй над результатами, введенными
в элементах формы
METHOD Способ передачи данных из формы на сервер Web
ENCTYPE Тип MIME передаваемых данных
Параметр ACTION определяет, какое действие будет выполнено над формой,
после того как пользователь ее заполнит и передаст серверу Web. В примере,
приведенном выше, в качестве значения для параметра ACTION мы указали путь
к программе CGI, которая будет выполнять обработку данных.
Посредством параметра METHOD Вы можете выбрать один из двух методов
передачи данных из формы серверу Web.
Если значение этого параметра равно GET (как в нашем примере), программа
CGI, указанная в параметре ACTION, получит данные из формы через перемен-
ную среды с именем QUERY_STRING. В том случае, когда значение параметра METHOD
равно POST, программа CGI получит данные из формы через стандартный поток
ввода. Позже мы рассмотрим различия этих методов более подробно.
И наконец, третий параметр ENCTYPE, используется очень редко и только для
метода POST. Он позволяет указать тип передаваемых данных и по умолчанию
имеет значение a p p l i c a t i o n / x - w w w - f o r m - u r l e n c o d e d .
292 Базы данных в Интерноте. Практическое руководство

Заметим, что для отправки формы на сервер Web можно использовать гра-
фическую кнопку типа IMAGE. Изображение такой кнопки задается парамет-
ром SRC.
Когда пользователь щелкает графическую кнопку, программа CGI получает
от нее координаты точки, в которой находился курсор мыши в момент щелчка.
Таким образом, возможно создание кнопки в виде сегментированного графичес-
кого изображения. Программа OGI при этом сумеет определить, в какой облас-
ти изображения был сделан щелчок мышью при отправке заполненной формы
на обработку.

Передача данных программе CGI


Когда пользователь заполняет форму и щелкает кнопку типа SUBMIT либо гра-
фическую кнопку (которая выполняет аналогичную функцию), данные из полей
формы вместе с именами этих полей передаются браузером серверу Web. Тот, в
свою очередь, анализирует эти данные и запускает соответствующую програм-
му CGI, путь к файлу которой указан в теге <FORM>.
Перед запуском программы CGI сервер Web выбирает в зависимости от зна-
чения параметра METHOD тега <FORM> один из двух способов передачи полученных
данных для обработки — метод GET или POST.

Метод 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

Строка, передаваемая в переменной среды QUERY_STRING, закодирована с ис-


пользованием так называемой кодировки URL. В этом случае все символы про-
белов заменяют символами «+>>. Кроме того, для представления кодов управля-
ющих и некоторых других символов используется последовательность символов
вида «Яхх», где «хх» — это шестнадцатеричный код символа в виде двух симво-
лов ASCII. В нашей книге мы приведем исходные тексты функций, предназна-
ченных для перекодирования информации, полученной из форм.
Метод POST
При использовании метода POST программа CGI получает данные из формы че-
рез стандартный поток ввода stdin. Если программа CGI составлена на языке
программирования С, то для получения данных она может воспользоваться та-
кими функциями, как f read или scant.
Что же касается количества байт данных, которые нужно считать из стандар-
тного потока ввода, то эта информация передается программе CGI через пере-
менную среды с именем CONTENT_LENGTH.
Ниже мы приводим фрагмент кода для определения размера информации для
ввода через стандартный поток stdin:
int Size;
Size = atoi(getenvrCONTENT_LENGTH"));
Входные данные можно затем получить, например, следующим образом:
char szfiuf[8196];
freadfszBuf, Size, 1, stdin);
Разумеется, буфер для чтения данных допустимо заказывать и динамически,
для чего следует воспользоваться такой функцией, как malloc.
Если в теге <FORM> не указан параметр ENCTYPE (тип MIME передаваемых
данных) или этот параметр имеет значение application/x-www-form-urlencoded,
данные, полученные через стандартный поток внода, закодиронаны в URL. Пе-
ред использованием Вы должны их перекодировать соответствующим образом.
Выбор между GET и POST
Метод GET обычно применяют для обработки небольших форм, так как браузе-
ры и операционные системы накладывают ограничения на размер данных, пе-
редаваемых через переменную среды QUERY_STRING.
В этом отношении метод POST предпочтительнее, так как не ограничивает
размер передаваемых данных.
Передача ответа из программы CGI
Вне зависимости от метода передачи данных (GET или POST) результат своей
работы программа CGI направляет в стандартный поток вывода stdouc. Если
программа составлена на языке программирования С, для записи результат ра-
боты она может воспользоваться, например, функцией p r i n t f или fwrite.
Чаще всего программы CGI применяют для создания динамических докумен-
тов HTML на основе данных, полученных из формы. В этом случае первой стро-
кой, которую необходимо вывести в стандартный поток вывода stdout, будет
следующая строка заголовка HTTP:
Content-type: text/html
294 Базы данных в Интернете. Практическое руководство

Сразу за ней необходимо вывести еще одну пустую строку, которая послужит
разделителем между заголовком 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, а второй нужен для создания пустой раздели-
тельной строки.

Переменные среды для программы CGI


Прежде чем перейти к примерам программ CGI, мы расскажем о переменных
среды, которые формируются для этик программ перед запуском. Через эти пе-
ременные помимо данных из полей форм передастся и другая очень важная
информация, причем ее не всегда можно игнорировать.
Рассмотрим по отдельности назначение переменных среды. Заметим, что
набор переменных, создаваемых при запуске программы CGI, зависит от конк-
ретной реализации сервера Web.
4 AUTH TYPE
Технология Web допускает защиту страниц HTML, когда доступ к отдельным
страницам предоставляется только для о т д е л ь н ы х пользователей при
предъявлении пароля. При этом используется система аутентификации, или
проверки подлинности идентификатора пользователя.
Переменная среды AUTH_TYPE содержит тип идентификации, который приме-
няется сервером. Например, для сервера Web типа Microsoft Information
Server при включении аутентификации в этой переменной будет храниться
строка «NTLM».
t GATEWAY_INTERFACE
В этой переменной находится версия интерфейса CGI, с которой работает
данный сервер.
+ НТТР_АССЕРТ
В этой переменной перечислены типы данных MIME, которые могут быть
приняты браузером от сервера Web. Например, сервер Microsoft'Internet
Information Server может передать браузеру растровые графические изобра-
жения в формате gif, jpeg, pjpeg, x-xbitmap. Подробно эти типы данных опи-
саны в спецификации протокола MIME, рассказ о которой выходит за рам-
ки нашей книги.
| HTTP_REFER
В переменную HTTP_REFER записывается адрес URL документа HTML, кото-
рый инициировал работу программы CGI.
Глава 7. Расширения CGI и ISAPI сервера Web 295

• НТТР_АССЕРТ_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 Базы данных в Интернете. Практическое руководство

Администратор создает таблицу соответствия физических каталогов и вир-


туальных, определяя права доступа к виртуальным каталогам с помощью про-
граммы настройки параметров сервера Web.
+ PATHJTRANSLATED
Физический путь к программе CGI.
+ QUERY_STRING
Строка параметров, указанная в форме после адреса URL программы CGI
после разделительного символа «?».
+ REMOTE.ADDR
Адрес IP узла, на котором работает браузер пользователя.
• REMOTE_HOST
Доменное имя узла, на котором работает браузер пользователя. Если эта
информация недоступна (например, для узла не определен доменный адрес),
вместо доменного имени укалывается адрес IP, как в переменной REHOTE_ADDR.
• REMOTE_USER
Имя пользователя, которое используется браузером для аутентификации.
Применяется только в том случае, если сервер Web способен работать с аутен-
тификацией и программа CGI отмечена как защищенная.
ф REQUEST_METHOD
Метод доступа, который используется для передачи данных от браузера сер-
веру Web. В своих примерах мы используем методы доступа GET и POST, хотя
протокол HTTP допускает применение и других методов доступа, например
PUT и HEAD.
ф SCRIPT_NAME
В эту переменную записывается путь к виртуальному каталогу и имя про-
граммы CGI. Анализируя эту переменную, программа CGI может определить
путь к своему загрузочному файлу.
+ SERVER_NAME
Доменное имя сервера Web или адрес IP сервера Web, если доменное имя
недоступно или не определено.
• SERVER_PROTOCOL
Имя и версия протокола, который применяется для выполнения запроса к
программе CGI.
+ SERVER_PORT
Номер порта, па котором навигатор посылает запросы серверу Web.
+ SERVER_SOFTWARE
Название и версия программного обеспечения сервера Web. Версия следует
после названия и отделяется от названия символом «/>>.
• REMOTE_IDENT
Имя, с которым пользователь подключился к серверу Web. Используется
только в том случае, если сервер Web способен подключать пользователей по
именам.
Глава 7. Расширения CGI и ISAPI сервера Web 297

Примеры программ CGI


В этом разделе мы покажем примеры несложных программ CGI, демонстриру-
ющих динамическое создание документов HTML, обработку данных, введенных
при помощи форм. Кроме того, здесь Вы найдете примеры программ CGI, вы-
полняющих обращение к базам данных.
Программа CGIHELLO
Программа CGIHELLO представляет собой простейшую программу CGI, кото-
рая запускается при помощи кнопки в форме, возвращая навигатору созданный
динамически документ HTML.
Эта программа хороша для проверки возможности запуска программ CGI на
Вашем сервере Web или на сервере Вашего поставщика услуг Интернета. Так как
она очень проста, то существует немного причин, по которым она не работает.
Это неправильная настройка прав доступа к виртуальному каталогу, содержаще-
му загрузочный модуль программы CGI, а также ошибочная ссылка на этот ка-
талог в параметре ACTION тега <FORM>.
Еще одна возможная причина — неправильное указание типа проекта в сре-
де Visual C++. Напоминаем, что программа CGI, предназначенная для работы
в среде Windows NT, должна обязательно создаваться как 32-разрядное консоль-
ное приложение, а не как консольная программа MS-DOS.
Наша первая программа CGI запускается из формы, расположенной в доку-
менте HTML с именем cgihello.html. Исходный текст этого документа представ-
лен в листинге 7-1.
Листинг 7-1 Вы найдете в файле ch7\cgihello\cgihello.html на прилагаемом к
книге компакт-диске.
В этом документе определена форма, содержащая единственную кнопку, со-
зданную с применением тега <INPUT> и имеющую тип SUBMIT:
<form HETHOO="GET" ACTION="http://saturn/cgi-bin/cgihello.exe">
<pxinput TYPE="submit" VALUE="Send"> </p>
</form>
В параметре ACTION тега <FORM> мы указали путь к программе CGI, причем этот
путь является виртуальным. Для передачи данных используется метод GET.
В результате работы программы CGIHELLO динамически создается доку-
мент HTML, страница которого показан на рис. 7-1.

Ffe £* View Favorites Tools HeSp

Результат работы
программы CGI
Эта страница создана динамически! результате работы
программы ССЧ

one ;g Local intranet

Рис. 7-1. Документ HTML, создаваемый динамически программой CGGIHELLO


298 Базы данных в Интернете. Практическое руководство

Рассмотрим исходный текст программы CGIHELLO (листинг 7-2).


Листинг 7-2 Вы найдете в файле ch7\cgihello\cgihello.c на прилагаемом к книге
компакт-диске.
Эта консольная программа не отличается особой сложностью и состоит из
ряда вызовов функции p r i n t f , выполняемых функцией main:.
void main(int argc, char *argv[])
{
printf("Content-type: text/html\n\n");
printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">");
printf ("<HTMLXHEADXTITLE>"
"Программы CGI</TJ.TLEX/HEAD><BODY>"};

printf("<H1>PeayflbTaT работы программы CGK/H1>");


printf("<P>3Ta страница создана динамически в результате"
" работы программы CGI");
printf("</BODY></HTML>");
;
В первый раз функция printf выводит заголовок HTTP и пустую строку-раз-
делитель. Далее программа CGIHELLO записывает построчно в стандартный
поток вывода текст документа HTML.
Программа CONTROLS
Более сложная программа CGI с названием CONTROLS выполняет обработку
данных, полученных из формы, показанной на рис. 7-2.
Э Органы управления в Форма rosoft Internet Explorer pinvid.

Текстовое пояе TEXT sample ot textl

Т i кетовое попе TEXT AREA Sample of t e x t

И ер включатели CHECKBOX p Первый


Г" Второй
F Третий
ПерекпючатышНАОЮ (• Пгрвый
t~ Второй
' Третий
Список |FirstOp1ion

Send Resei

Рис. 7-2. Поле с элементами различных типов


Глава 7. Расширения CGI и ISAPI сервера Web 299

Исходный текст этой формы представлен в листинге 7-3.


Листинг 7-3 Вы найдете в файле ch7\controls\controls.html на прилагаемом к
книге компакт-диске.
Программа CONTROLS отображает в динамически формируемом докумен-
те HTML метод, использованный для передачи (POST или GET), а также размер
и тип данных, поступающих от формы. Принятые данные отображаются как в
исходном виде, так и после перекодировки. Кроме того, в созданном динамичес-
ки документе HTML Вы сможете увидеть список значений всех полей, опреде-
ленных в форме (рис. 7-3).
3 Программы CG) - Micioiolt Internet Fnplorei pmvidEd bji

Beak Shop

REQUESTJJ4ETHOD - POST
CONTENT Jl-ENGTH - 127

Принятые данные
hid-HiddenUteitl =3 unple+cfH«xtl &pw*-Sunple4Ti|4p is swotd&t ext2=S amp] e+

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


hid- Hide! eo&textl -Sample of textl&pwd= Sample of passwo.d&tExt2=Saraple of
teit& с hkl = oi*& chk3= on&iad- onl fi s el- Fil st Options** JS&y* 2 1

Список значений полей


hid- Hidden
tert1=S ample oftextt
ptt-c№ ample ofpasswoid
tert2=S ample of tart
chkl-on
chk3-on
rad- onl
s el-Fir «Option
x-5E
Г21

Рис. 7-3. Документ HTML, сформированный динамически программой CONTROLS


Из рисунка видно, что браузер прислал серверу Web 127 байт информации.
Так как при этом использовался метод POST, данные были направлены в стандар-
тный поток ввода. Они представлены в кодировке URL, так как содержимое
переменой среды CONTENT.JYPE равно application/x-www-form-urlencoded.
Обратите внимание на текстовое поле с именем textl. Все пробелы в соот-
ветствующей строке в кодировке URL заменены символом «+». Символы «&>> и
<•, » приходят в виде «Х26» и «К2С». Функция перекодирования возвращает стро-
ку в исходный вид — «Sample of textl».
Форма, показанная на рис. 7-2, имеет две к н о п к и , предназначенные для пе-
редачи данных серверу Web. Это обычная кнопка и кнопка в графическом виде.
Мы щелкнули графическую кнопку, поэтому от формы пришла информация о
координатах курсора мыши в виде переменных с именами х и у.
300 Базы данных в Интернете. Практическое руководство

Исходный текст программы CONTROLS приведен в листинге 7-4.


Листинг 7-4 Вы найдете в файле ch7\controls\controls.c на прилагаемом к кни-
ге компакт-диске.
После запуска функция main программы CONTROLS выводит в стандартный
ноток вывода заголовок HTTP и начальные строки динамически формируемо-
го документа HTML. Для вывода мы использовали функцию p r i n t f :
printf("Content-type: text/html\n\n");

printfCXIDOCTYPE HTML PUBLIC"


" V-//W3C//OTD HTML 3.2//EN\">");

printf("<HTHLXHEAD><TTTLE>nporpaMMbi CGI"
"</TITLE></HEADXBODY BGCOLOR=#FFFFFF>");

szMethod = getenv("REQUEST_METHOD");
Далее функция m a i n определяет использованный метод передачи данных,
анализируя содержимое переменной среды REQUEST_METHOD:
if(!strcmp(szMethod, "POST"))
I

else if(!strcmp(szMethod, "GET"))

!
Это необходимо, так как при разных методах передачи нужно использовать
различные методы получения входных данных. Значение переменной среды
программа получает при помощи функции 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>"};

printf("REQUEST_METHOD = %s", getenv{"R£QUEST_METHOD"));


printf("<BR>CONTENT_LENGTH = xid", ISize);
printf("<Bfl>CONTENT_TYPE = Sis". getenv("CONTENT_TYPEM));

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;;)

szPtr = strchr(szParam, ' & ' ) ;

if(szPtr != NULL)

*szPtr = ' \ 0 ' ;


DecodeSt r(szParam);
printf("Xs<BR>", szParam);

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;

for(src=0, dst=0; szStringtsrc]; src++, dst++)


<
ch = szString[src];
ch = (ch == '+') ? ' ' : ch;
szString[dst] = ch;

if(ch == T)

szString[dst] = DecodeHex(&szString[src + 1]);


src += 2;
}

szString[dst] = '\0';

Эта функция сканирует входную строку, заменяя символы <<+» на пробелы.


Если в перекодируемой строке встречается комбинация символов вида «Ххх», мы
Глава 7. Расширения CGI и ISAPI сервера Web 303

заменяем ее однобайтовым кодом соответствующего символа с помощью функ-


ции DecodeHex.
Функция DecodeHex комбинирует значение кода символа из старшего и млад-
шего разряда преобразуемой комбинации символов:
char DecodeHex(char *str)
{
char ch;

if(str[0] >= ' A ' )


ch = <(str[0] 4 Oxdf) - ' A ' ) + 10;
else
ch = str[0] - ' 0 ' ;

ch «= 4;

if(str[1] >= ' A ' )


ch += <(str[1] & Oxdf) - ' A ' ) + 10;
else
ch += str[1] - ' 0 ' ;

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">
<р>Издательство &quot;Русская Редакция&дио1:;</а><Ьг>
<а HREF="http://saturn/cgi-bin/aref. exe?page2">Microsoft</axbr>
<а HREF="http://saturn/cgi-bin/aref.ехе?радеЗ">Наш сервер</а><Ьг>
Программа CGI принимает параметр и в зависимости от его значения отобра-
жает один из документов HTML. Например, при выборе первой строки в окне
304 Базы данных в Интернете, Практическое руководство

навигатора отображается главная страница сервера Web издательства «Русская


Редакция».
Исходный текст программы AREF приведен в листинге 7-6.
Листинг 7-6 Вы найдете в файле ch7\aref\aref.c на прилагаемом к книге ком-
пакт-диске.
Программа получает значение переменной среды QUERY_STRING, пользу-
ясь для этого функцией getenv:
char * szQueryString;
szQueryString = getenv("QUERY_STRING");
Далее она сравнивает значение параметра со строками «pagel», «page2» и
«радеЗ»:
if(!strcmp(szQueryString, "pagel"))
printf("Location: http://www.rusedit.ru/\n\n");

else if(lstrcmp(szQueryString, "page2"})


printf("Location: http://www.microsoft.com/\n\n");

else if(Istrcmp(szQueryString, "радеЗ"»


printf("Location: http://www.frolov.pp.ru/\n\n"};

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, и в зависимости от этого либо возвратит ссыл-
ку на тот или иной существующий документ, либо сформирует новый документ
динамически.

Доступ к базе данных из CGI


Если необходимо, чтобы программа CGI обращалась к базе данных, то для это-
го можно использовать один из методов доступа, описанный в нашей книге, —
ADO, OLE DB или ODBC. Когда Вы извлечете параметры из элементов фор-
мы, запустившей программу CGI, передайте их методам или функциям для до-
ступа к базе данных.
Глава 7. Расширения CGI и ISAPI сервера Web

В этом разделе мы рассмотрим исходные тексты программы CGI с названи-


ем CGICPPADO. Эту программу мы создали на основе консольной программы
CPPADO — ее исходные тексты описаны ранее, в четвертой главе книги.
Так как Вы уже познакомились со многими приемами построения програм-
мы CGICPPADO, обращаем Ваше внимание только на те моменты, которые
имеют отношение к особенностям программы, работающей в качестве приложе-
ния CGL
Для работы с программой CGICPPADO мы подготовили документ HTML с
формой, показанной на рис. 7-4.

,
г
Просмотр списка сотрудников

jfrolov

rfjj Computer

Рис. 7-4. Форма для ввода идентификатора и пароля


Здесь нужно ввести идентификатор сотрудника магазина, имеющего админи-
стративные права, а также его пароль. После щелчка кнопки Submit введенная
информация передается программе CGICPPADO.
Если идентификатор и пароль указаны правильно и действительно принад-
лежат сотруднику с административными привилегиями, программа CGICP-
PADO динамически формирует документ HTML, в котором отображается содер-
жимое таблицы managers базы данных Bookstore (рис. 7-5).

„J
Список сотрудников магазина

1NULL jSahs_maiMg*r
TestAdimns 1234 MULL iSales_manag«.
ictmin "~ ~ \123~ |05 129909:50:10 jSal

Рис. 7-5. Список сотрудников, извлеченный из базы данных программой CGICPPADO


306 Базы данных в Интернете. Практическое руководство

Если же пароль введен неправильно или если пользователь не обладает ад-


министративными привилегиями, в окне браузера появится лишь сообщение об
отказе в доступе (рис. 7-6).
рчаннкив - Mitiosofl Inl I [

Рис. 7-6. Пользователь не зарегистрирован как администратор, поэтому отображается сообщение


об отказе в доступе
Исходные тексты документа HTML с формой приведены в л и с т и н г е 7-7.
Листинг 7-7 вы найдете в файле ch7\CGICppado\managers.html на прилагае-
мом к книге компакт-диске.
Здесь Вам следует, прежде нсего, обратить внимание на параметры тега
<FORM>:
<form method="POST" action="http://saturn/cgi-bin/cppado.exe">
Параметр METHOD задает метод передачи данных из формы как POST, а пара-
метр ACTION определяет путь к загрузочному файлу программы CGICPPADO.
Поля ввода идентификатора и пароля пользователя определены в нашей
форме следующим образом:
<tr>
<td и1(ИЬ="134">Идентификатор:<:/1<1> <td width="230">
<input type="text" name="UserID" size="20"></td>
</tr>
<tr>
<td width="134">napcmb:</td><td width="230">
<input type="password" name="Pwd" size="20"X/td>
</tr>
Поле для ввода идентификатора пользователя называется UserlD, а поле па-
роля — Pwd. Эти имена нам потребуются в программе CGICPPADO для извле-
чения текста из соответствующих элементов формы.
Теперь займемся программой CGICPPADO.
Исходные тексты главного модуля этой программы приведены в листинге 7-8.
Листинг 7-8 Вы найдете в файле ch7\CGICppado\CPPADO.cpp на прилагаемом
к книге компакт-диске.
Когда пользователь щелкает кнопку Submit в форме, показанной на рис. 7-4,
программа CGICPPADO запускается на сервере Web. При этом управление пе-
редается функции _ t m a i n , выполняющей все действия по обращению к базе дан-
ных и по динамическому формированию результата этих обращений в виде до-
кументов HTML.
Глава 7. Расширения CGI и ISAPI сервера Web 307

В области глобальных переменных мы определили два массива символов,


предназначенных для хранения идентификатора и пароля, введенного посети-
телем в форме:
char szUserID[80];
char szUserPassword[80];
Перед началом работы программа CGICPPADO выводит в стандартный вы-
ходной поток заголовок документа HTML:
cout « "Content-type: text/html\n\n";
COut « "<!DOCTYPE HTML PUBLIC V-//W3C//DTD HTML 3.2//EN\">";
cout « "<HTMLxHEADxTITLE>flpocMOTp списка сотрудников"
"</TITLEX/HEADXBODY BGCOLOR=tt FFFFFFX' ;
Здесь мы пользуемся потоком класса cout, что вполне допустимо, так как он
связан с потоком stdout.
На следующем этапе наша программа проверяет метод, использованный для
передачи данных из формы:
char » szMethod;
szMethod = getenv("REQUEST_METHOD");
if(!strcmp(szMethod, "POST"))
'

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;

Этот ц и к л Вам должен быть знаком по программе CONTROLS, описанной


ранее в этом разделе, В частности, мы рассказывали о функции DecodeStr. Здесь,
однако, мы дополнительно вызываем функцию G e t P a r a m , извлекающую иденти-
фикатор пользователя и пароль и сохраняющую эти данные в глобальных пере-
менных szllserlD и szUserPassword.
Исходный текст функции G e t P a r a m показан ниже:
void GetParam(char *szString)
{
char szBuf[1024];
char * szPtr;

strcpy(szBuf , szString);
szPtr = strchr(szBuf , ' = ' ) ;
if(szPtr != NULL)
{
-szPtr = ' \ 0 ' ;
szPtr++;

if(!strncmp(szBuf, "UserlD11, 6))


strcpy((char*)szUserID, szPtr);

else if(! strncmp(szBuf , "Pwd", 3))


strcpy((char*)szUserPassword, szPtr);

Получая через параметр szString указатель на пару вида «Имя_параметра=3-


начение», эта функция последовательно сравнивает имя со строками « U s e r l D » и
«Pwd». При совпадении извлеченное значение содержимого соответствующего
поля формы сохраняется в глобальных переменных s z U s e r l D и s z U s e r P a s s w o r d .
После обработки данных, поступивших от формы, программа CGICPPADO
проверяет идентификатор и пароль пользователя, вызывая функцию l o g i n :
if(login((char*)szUserID, (char*) szUserPassword))
getManagersQ;
Глава 7. Расширения CGI и ISAPI сервера Web 309

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)
{

Функцию g e t M a n a g e r s мы также взяли из приложения CPPADO, изменив


только формат вывода информации, извлеченной из базы данных.
Перед началом цикла обработки набора записей эта функция формирует за-
головок таблицы в документе HTML, записывая в него'названия столбцов:
cout « "<п2>Список сотрудников магазина<Ь2>";
cout « "<table border=1>";
cout « "<th>MfleHTM0HKaTop</th><th>MMfl</th>"
"<^>Пароль<ЛЬх^>Последнее подключение<ЛИХ1:п>Права<Лп>";
Значения, извлеченные из полей текущей записи набора, форматируются для
записи в ячейки таблицы, создаваемой в документе HTML:
while(rs->adoEOF == VARIANT_FALSE)
{
vManagerlD = rs->Fields->GetItem(_variant_t("ManagerID"))->Value;
vName = rs->Fields->GetItem(_variant_t("Name"})->Value;
vPassword = rs->Fields->GetItem(_variant_t("Password"))->Value;
vLastLogin = rs->Fields->GetItem(_yariant_t("LastLogin"))->Value;
vRights = rs->Fields->Get!tem(_variant_t("Rights"))->Value;

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 Базы данных в Интернете. Практическое руководство

cout « (LPCTSTR)strTmp « "\n";


cout « "</tr>";

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;

В эту функцию мы также внесли незначительные изменения, касающиеся


формата вывода сообщений. Теперь эти сообщения будут отображаться не в
консольном окне, а в документе ILTML, поэтому для форматирования нам при-
шлось использовать соответствующие теги HTML.

Создание приложений ISAPI


В этом разделе речь пойдет о приложениях ISAPI, дополняющих возможности
сервера Microsoft Information Server. Вес эти приложения можно разделить на
две группы: расширения ISAPI и фильтры ISAPI.
Первые по своему назначению напоминают только что изученные Вами про-
граммы CGI. Однако в отличие от последних эти расширения выполнены в виде
библиотек динамической загрузки DLL, что имеет ряд преимуществ.
Так же как и программы CGI, расширения ISAPI получают данные от брау-
зера (например, из формы, заполненной посетителем сервера Web), обрабаты-
вают их и посылают ответ браузеру в виде динамически сформированного до-
кумента HTML. Однако вместо чтения содержимого переменных среды и стан-
дартного потока ввода расширение ISAPI получает данные при помощи специ-
ально предназначенных для этого функций. Аналогично вместо записи выход-
ных данных в стандартный поток вывода расширение ISAPI вызывает специаль-
ные функции.
Фильтры ISAPI также реализуются в виде библиотек DLL, однако они име-
ют другое назначение. Фильтры ISAPI способны контролировать весь поток
данных, проходящий через сервер, на уровне протокола HTTP. Поэтому их мож-
но применять для решения таких задач, как шифрование или перекодирование
данных, компрессия информации. Они пригодны для создания собственных
процедур подключения пользователей к системе и аутентификации (проверки
идентификации пользователей), а также для сбора статистической информации
использования ресурсов сервера.
Глава 7. Расширения CGI и ISAPI сервера Web 311

Принципы работы и структура расширения ISAPI


Как только что было сказано, расширение ISAPI создается в виде библиотеки
динамической загрузки DLL. Обращение к такой библиотеке выполняется в
документах HTML аналогично обращению к программам CGI — из форм или
ссылок, созданных при помощи тегов <FORM> и <А>.
Когда пользователь обращается к расширению ISAPI, соответствующая биб-
лиотека DLL загружается в адресное пространство сервера Microsoft Information
Server и становится его составной частью. Так как расширение ISAPI работает
в рамках процесса сервера Microsoft Information Server, а не в рамках отдельно-
го процесса (как это происходит при запуске программы CGI), оно может поль-
зоваться всеми ресурсами, доступными серверу. Это благоприятно сказывается
на производительности.
Производительность сохраняется на достаточно высоком уровне и в тех слу-
чаях, когда расширение сервера используется активно сразу многими пользова-
телями.
Если, скажем, 20 пользователей запустят одновременно одну и ту же програм-
му CGI, то на сервере будет создано 20 процессов — по одному для каждого
пользователя. Так как создание процесса отнимает достаточно много системных
ресурсов, это приведет к потере производительности.
Если же 20 пользователей одновременно обратятся к одному и тому же рас-
ширению ISAPI, в память серверного процесса будет загружена одна копия биб-
лиотеки DLL, содержащая расширение. Она будет работать в многопоточном
режиме. Очевидно, при этом полностью исключаются накладные расходы сис-
темных ресурсов на запуск процессов.
Сравнивая программы CGI и расширения ISAPI, нужно заметить, что, не-
смотря на существенное превосходство в быстродействии расширений ISAPI,
программы CGI также имеют свои преимущества.
Так как расширения ISAPI работают в рамках серверного процесса, они дол-
жны отлаживаться особенно тщательно. Ошибка в расширении ISAPI способ-
на вызвать аварийное завершение работы всего сервера Microsoft Information
Server. Что же касается программы CGI, выполняющейся как отдельный процесс
в своем собственном адресном пространстве, то она едва ли способна вывести
из строя сервер. Если в программе CGI допущена критическая ошибка, это при-
ведет всего лишь к аварийному завершению самой программы, но не сервера.
Напомним, что расширение ISAPI работает в многопоточном режиме, что
приводит к дополнительным проблемам при отладке.
Вызов расширения ISAPI сервером Web
Структура расширения ISAPI очень проста. Библиотека DLL расширения дол-
жна экспортировать всего две функции с именами GetExtensionVersion и Http-
ExtensionProc. Первая предназначена для того, чтобы расширение могло сооб-
щить серверу версию спецификации, которой оно соответствует, и строку опи-
сания расширения. Функция HttpExtensionProc выполняет всю работу по пере-
даче данных между расширением и сервером.
Дополнительно расширение ISAPI способно экспортировать функцию Termi-
nateExtension. Она вызывается сервером перед тем, как ненужное больше при-
312 Базы данных в Интернете. Практическое руководство

ложенис ISAPI выгружается из памяти. Функция TerminateExtension должна


освободить ресурсы, полученные при инициализации расширения ISAPI.
Функция GetExtensionVersion
Функция G e t E x t e n s i o n V e r s i o n очень проста в реализации и обычно выглядит
следующим образом:
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVersion)
{
pVersion->dwExtensionVersion =
MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR);

lstrcpyn(pVersion->lpszExtensionDesc,
"My ISAPI Application Name", HSE_MAX_EXT_DLL_NAHE_LEN);

return TRUE;

При вызове функции G e t E x t e n s i o n V e r s i o n указатель на структуру типа


HSE_VERSION_INFO передается через единственный параметр. Эта структура и ука-
затель на нее (типа LPHSE_VERSION_INFO) определены в файле httpext.h следую-
щим образом;
#define HSE_MAX_EXT_DLL_NAME_LEN256
typedef struct _HSE_VERSION_INFO
I
DWORD dwExtensionVersion;
CHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN];
} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;
Константы HSE_VERSION_MINOR и HSE_VERSION_MAJOR указывают текущую вер-
сию интерфейса расширения ISAPI и также определены в файле httpext.h:
ttdefine HSE_VERSION_MAJOR 4 // верхний номер версии
ttdefine HSE_VERSION_MINOR 0 // нижний номер версии
Функция HttpExtensionProc
Теперь рассмотрим вторую функцию, которую должна экспортировать библио-
тека DLL расширения ISAPI. Она называется H t t p E x t e n s i o n P r o c и имеет следу-
ющий прототип:
DWORD WINAPI HttpExtensionProc(EXTENSIOfLCONTROL_BLOCK *pECB);
Функция HttpExtensionProc получает единственный параметр — указатель на
структуру типа EXTENSION_CONTROL_BLOCK, определенную в файле httpext.h:
typedef struct _EXTENSION_CONTROL_BLOCK
!
DWORD cbSize; // размер структуры в байтах
DWORD dwVersion; // версия спецификации ISAPI
HCONN ConnID; // идентификатор канала
DWORD dwHttpStatusCode; // код состояния HTTP

CHAR lpszl_ogData[HSE_L.OG_BUFFER_LEN]; // текстовая строка,


// закрытая двоичным нулем, в которой находится информация
// протоколирования, специфичная для данного расширения
Глава?. Расширения CGI и ISAPI сервера Web 313

LPSTR IpszMethod; // переменная REQUEST_METHOD


LPSTR IpszQueryString; // переменная QUERY_STRING
LPSTR IpszPathlnfo; // переменная PATH_INFO
LPSTR IpszPathTranslated; // переменная PATH_TRANSLATED

DWORD cbTotalBytes; // полный размер данных, полученных от


// браузера
DWORD cbAvailable; // размер доступного блока данных
LPBYTE IpbData; // указатель на доступный блок данных
// размером cbAvailable байт
LPSTR IpszContentType; // тип принятых данных

// Функция GetServerVariable для получения значения переменных


BOOL (WINAPI * GetServerVariableHHCONN hConn,
LPSTR IpszVariableName, LPVOID IpvBuffer, LPDWORD IpdwSize);

// Функция WriteClient для посылки данных посетителю


BOOL (WINAPI * WriteClientXHCONN ConnID,
LPVOID Buffer, LPDWORD IpdwBytes, DWORD dwReserved);

// Функция ReadClient для получения данных от посетителя


BOOL (WINAPI * ReadClient) (HCONN ConnID,
LPVOID IpvBuffer, LPDWORD IpdwSize);

// Вспомогательная функция ServerSupportFunction


// для выполнения различных операций
BOOL (WINAPI * ServerSupportFunction)(HCONN hConn,
DWORD dwHSERRequest, LPVOID IpvBuffer,
LPDWORD IpdwSize, LPDWORD IpdwDataType);
i
} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;
Рассмотрим отдельные поля этой структуры, представляющей большой ин-
терес для разработчиков расширений ISAPI.
ф cbSize
В самом начале структуры EXTENSION_CONTROL_BLOCK находится поле cbSize,
в которое при вызове расширения сервер записывает размер структуры в
байтах.
ф dwVersion
Поле d w V e r s i o n содержит номер версии расширения ISAPI. Верхнее и ниж-
нее значения номера версии можно получить при помощи макрокоманд
HIWORD и LOWORD соответственно.
• ConnID
В поле ConnID сервер записывает идентификатор канала, созданного для рас-
ширения. Это поле нельзя изменять.
+ dwHttpStatusCode
Поле dwHttpStatusCode должно заполняться расширением ISAPI. Вам надо за-
писать сюда результат завершения операции (код состояния транзакции). В
случае успеха в это ноле записывается значение 200 (как указано в специфи-
кации HTTP).
314 Базы данных в Интернете. Практическое руководство

ф 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 Базы данных в Интернете. Практическое р/коаодство

Таблица 7-2. Коды ошибок для функции GetServerVariable


Код ошибки Описание
ERROR.INVALID^INDEX Неправильное имя переменной, передаваемой через па-
раметр I p s z V a r i a b l e N a m e
ERROR_INVALID_PARAMETER Непраиильное значение параметра Нсопп
ERROR_INSUFFICIENT_BUFFER Буфер, адрес которого указан с помощью параметра
I p v B u f f e r , слишком мал. Необходимый размер буфера
записывается по адресу, который был передан функции
через параметр IpdwSize
ERROR_MORE_DATA Буфер, адрес которого указан с помощью параметра
I p v B u f f e r , слишком мал. В результате данные прочитаны
частично, причем размер буфера, необходимый для чте-
ния всех данных, неизвестен
ERROR_NO_DATA Данные не получены
Ниже мы показываем пример использования функции GetServerVariable для
получения содержимого переменной с1шенем ALL_HTTP в буфер szTempBuf.
CHAR szTempBuf[4096];
DWORD dwSize;
dwSize = 4096;
lpECB->GetServerVariable(lpECB->ConnID,
(LPSTR)"ALL_HTTP", (LPVOID)szTempBuf, &dwSize);
strcat(szBuff, szTempBuf);

Функция 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 Базы данных в Интернете. Практическое руководство

Через параметр hConn функции S e r v e r S u p p o r t F u n c t i o n передается идентифи-


катор канала, полученный через поле ConnID структуры EXTENSION_CONTROL_BLOCK.
Параметр dwHSERRequest позволит задать один из нескольких кодов запроса,
определяющих операцию, выполняемую этой функцией.
Через параметр I p v B u f f e r передается размер буфера, который используется
при выполнении операции. Размер этого буфера должен быть записан в перемен-
ной типа DWORD, адрес которой передается через параметр IpdwSize. После выпол-
нения операции передачи данных в эту переменную будет записан размер успеш-
но переданного блока данных.
Параметр IpdwDataType применяется для указания дополнительной строки
заголовка или дополнительных данных, которые будут добавлены к заголовку,
передаваемому удаленному пользователю. Если для параметра I p d w D a t a T y p e
указать значение NULL (что допустимо), к заголовку будут добавлены символы
конца строки «\г\п».
Какие операции допустимо выполнять при помощи функции S e r v e r S u p p o r t -
Function?
Ниже мы привели список возможных значений параметра dwHSERRequest, оп-
ределяющего код выполняемой операции.
+ HSE_REQ_SEND_RESPONSE_HEADER
Эта операция предназначена для пересылки удаленному пользователю стан-
дартного заголовка HTTP. Если надо добавить другие заголовки, следует вос-
пользоваться параметром IpdwDataType. В качестве дополнительного заголов-
ка Вы можете указать любую строку, закрытую символами конца строки
«\г\п» и двоичным нулем.
Если Ваше расширение ISAPI динамически формирует документ HTML и
отправляет его пользователю, то ей не нужно вызывать функцию WriteClient.
Все необходимые для этого действия можно сделать средствами одной толь-
ко функции S e r v e r S u p p o r t F u n c t i o n . Ниже мы показали фрагмент кода, в ко-
тором эта функция используется для посылки документа HTML, подготов-
ленного заранее в буфере s z B u f f :
CHAR szBuff[4096];
wsprintffszBuff,
"Content-Type: text/html\r\n\r\n"
"<HTML><HEADXTITLE>Simple ISAPI Extension</TITLE></HEAD>\n"
"<BOOY BGCOLOR=eFFFFFF><H1>Hello from ISAPI Extension!</H1>\n");

strcat(szBuff, "<Н1>Заголовок документа</Н1>");


strcat(szBuff, "<HR>");
strcat(szBuff, "</BODY></HTML>');

lpECB->ServerSupportFunction(lpECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff);

Заметим, однако, что функция ServerSupportFunction не позволяет посылать


двоичные данные. Для отправки двоичных данных Вы обязательно должны
применить функцию W r i t e C l i e n t .
Глава 7. Расширения CG! и 1SAPI сервера Web 321

| 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 Базы данных в Интернете. Практическое р/ководство

Э Single ISAFI Extension- MiciosoH Inleinel Exokiiei piqvided by nwn..

Hello from ISAPI Extension!

Extension Version. 4.0


Method' POST
Qu eryString; P aram 1 |P ararn2 |P агатЗ
PathTranslated D \ketpub\wwwroot
TotalBytes; 0
ContentType: applicahon/x-www-form-urleacoded

Server Variables;
HTTP_ACCEPTmiage/gif, image fe-xbrtmap, image/jpeg, image/pjpeg,
appkcabofi/fflsword, applicabon/vnd ms -excel, */*

HTTP_HOST'satorn HTrP_USER_AGENT Mo (compatible'


MSIE 5.0. Windows NT, DigEst; www aurameda.ru)
HTTP_COOEIEibdiTJsed=c>fF; JavaTJsed^off
HTTP_C ONTENT_LENGTH' 0

HTTP_ACCEPT_ENCODING^ap, deflate
I
g Local intr

Рис. 7-7. Документ HTML, созданный динамически расширением ishello.dll


В верхней части этого документа отображается содержимое некоторых полей
структуры E X T E N S I O N _ C O N T R O L _ B L O C K , а в нижней — содержимое переменной
ALL_HTTP, полученное с помощью функции GetServerVariable.
Исходный текст расширения ishello.dll представлен в листинге 7-10.
Листинг 7-10 Вы найдете в файле ch7\ISHELLO\ishello.c на прилагаемом к кни-
ге компакт-диске.
Наряду с обычным для приложений Windows файлом windows.h мы включи-
ли в наш исходный текст файл httpext.h, в котором определены все необходи-
мые константы, структуры данных и прототипы функций:
ffinclude <windows.h>
Binclude <httpext.h>
Этот файл поставляется в составе Microsoft Visual C++.
В приложении определена функция GetExtensionVersion — ее мы уже рас-
сматривали ранее. Она записывает версию интерфейса ISAPI и текстовую стро-
ку описания расширения в поля структуры типа HSE_VERSION_INFO с именами
d w E x t e n s i o n V e r s i o n и I p s z E x t e n s i o n D e s c соответственно. Адрес структуры
HSE_VERSION_INFO передается функции GetExtensionVersion через параметр.
Функция H t t p E x t e n s i o n P r o c обращается к буферу s z B u f f для подготовки
динамически создаваемого документа HTML, который будет послан удаленно-
му пользователю в результате работы нашего расширения. В качестве вспомо-
гательного буфера применяется буфер szTempBuf:
CHAR szBuff[4096];
CHAR szTempBuf[4096];
Глава 7. Расширения CGI и ISAPI сервера Web 323

Прежде всего мы записываем в поле dwHttpStatusCode нулевое значение:


LpECB->dwHttpStatusCode = 0;
Потом в это поле мы запишем результат выполнения команды.
Далее в буфер s z B u f f копируется заголовок HTTP и начальный фрагмент
документа HTML, для чего используется функция wsprintf:
wsprintf(szBuff,
"Content-Type: text/html\r\n\r\n"
"<HTMLXHEAD><TITLE>Simple ISAPI Extension</TITLEX/HEAD>\n"
"<BODY BGCOLOR=»FFFFFFXH2>Hello from ISAPI Extension! </H2>\n");
После этого к буферу s z B u f f с помощью функции strcat добавляются дру-
гие строки документа. Например, разделительная линия:
strcatCszBuff, "<HR>");
После первой разделительной линии в документ вставляются несколько
строк со значениями некоторых полей структуры типа EXTENSION_CONTROL_BLOCK.
В следующем фрагменте кода показана строка версии интерфейса ISAPI:
wsprintf(szTempBuf, "<P>Extension Version: X d . X d " ,
HIWORD(lpECB->dwVersion), LOWORD(lpECB->dwVersion));
strcat(szBuff, szTempBuf);
Далее в документ выводятся строка с названием метода передачи данных
(иоле IpszMethod), строка параметров запуска расширения ISAPI (поле Ipsz-
Q u e r y S t r i n g ) , физический путь к программному файлу библиотеки DLL расши-
рения (поле IpszPathTranslated), полный размер данных, которые нужно про-
читать (поле cblotalBytes), а также тип данных (поле IpszContentType);
wsprintf(szTempBuf, "<BR>Method: its", lpECB->lpszMethod);
strcat(szBuff, szTempBuf);

wsprintf(szTempBuf, "<Bfi>QueryString: Sis",


lpECB->lpszQuerySt ring);
strcat(szBuff, szTempBuf);

wsprintfСszTempBuf, "<BR>PathTranslated; %s",


lpECB->lpszPathTranslated);
strcatCszBuff, szTempBuf);

wsprintf(szTempBuf, "<BR>TotalBytes: Xd",


lpECB->cbTotalBytes);
st rcat(szBuff, szTempBuf);

wsprintf(szTempBuf, "<BR>ContentType: %s",


lpECB->lpszContentType);
strcat(szBuff, szTempBuf);
После этого в документ снова добавляется разделительная линия и отобра-
жается содержимое переменных сервера с префиксом имени HTTP, для чего
используется рассмотренная ранее функция GetServe("Variable:
strcat(szBuff, "<HRxpxB>Server Variables:</B><BR>");

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

Вызов функций ODBC из ISAPI


Ранее мы рассказывали, как обращаться к базе данных из программы CGI с при-
менением объектного интерфейса ADO. В этом разделе мы приведем исходные
тексты расширения ISAPI, которое тоже работает с базой данных, но с приме-
нением программного интерфейса ODBC.
Для запуска этого приложения ISAPI (с названием ISFORM) мы использу-
ем форму, показанную на рис. 7-8.
С помощью этой формы сотрудник нашего магазина сможет просмотреть
свои собственные права. Для этого ему придется ввести в ней свой идентифи-
катор и пароль, а затем щелкнуть кнопку Submit.
Расширение ISFORM обращается к базе данных BookStore, запуская на вы-
полнение хранимую процедуру M a n a g e r L o g i n . Если пользователь зарегистриро-
ван в таблице m a n a g e r s , расширение ISFORM отправляет ему динамически со-
зданный документ HTML, в котором отображается идентификатор пользовате-
ля, его пароль и права, извлеченные из этой таблицы (рис. 7-9).
Глава 7. Расширения CGI и ISAP1 сервера Web 325

Э EA'Fiussion EfJitionU! Book Interne! DBWouice\ch074SF. . <


, EJe £* ¥«*« Fannies look He

Addi ess 0j E \iRussranEdiliorVJi Book 1п

Просмотр прав сотрудника

Идентификатор \Ша\

Пароль р"Р

12} My C-jnputa

Рис. 7-8. Форма ввода идентификатора и пароля

3 Правл сотрудника - Microsofl Inleinel Explorer ptovided Ь

h(tp/Aeluin/cgi-birv'isfoini dll

Права сотрудника
User: frolw
Pas word 123

. Administrator

Рис. 7-9. Перечень прав сотрудника магазина, извлеченный из базы данных


Похожие действия выполняла консольная программа ODBCPARAM, рас-
смотренная нами в предыдущей главе. Именно эта программа и была положена
в основу при создании расширения ISFORM.
А теперь опишем исходные тексты приложения ISFORM.
Исходный текст документа HTML, предназначенного для запуска этого рас-
ширения, Вы найдете в листинге 7-12.
Листинг 7-12 хранится в файле ch7\ISFORM\managers.html на прилагаемом к
книге компакт-диске.
В этом документе имеется форма, ссылающаяся на загрузочный файл расши-
рения isform.dll:
<form metnod="POST" action="littp://saturn/cgi-bin/isform.dll">
Помимо всего прочего, в этой форме определены два поля, предназначенные
для ввода идентификатора и пароля:
<tr>
<td widtn="134">MfleH^nKaTop:</td><td width="230">
<input type="text" name="UserID" size="20"x/td>
</tr>
<tr>
<td width="134">napCHib;</td><td width="230">
<input type="password" name="Pwd" size="20"></td>
</tr>
326 Базы данных в Интернете. Практическое руководство

Данные из этих полей передаются расширению ISFORM.


Исходный текст главного модуля приложения ISFORM приведен в листин-
ге 7-13.
Листинг 7-13 хранится в файле ch7\ISFORM\isform.c на прилагаемом к книге
компакт-диске.
Рассмотрим наиболее важные фрагменты исходного текста этого модуля.
В области глобальных переменных мы определили массивы для хранения
идентификатора пользователя szLJserlD, его пароля szUserPassword, прав szUser-
Rights, а также текста сообщения об ошибках s E r r M s g (если они возникнут при
обращении расширения'к базе данных):
char szUserID[80];
char szUserPassword[80];
char szUserRights[80];
char sErrHsg[8000];
Исходный текст функции G e t E x t e n s i o n V e r s i o n никаких особенностей не
имеет и полностью аналогичен исходному тексту, примененному нами во всех
примерах расширений ISAPI.
Все основные события происходят внутри функции H t t p E x t e n s i o n P r o c , к
описанию которой мы и приступаем.
В области локальных переменных функции мы определили два вспомогатель-
н ы х массива для храпения текста, а также два указателя, применяемых для из-
влечения параметров из блока данных, отправленных браузером:
CHAR szBuff[4096];
CHAR szTempBuf[4098];
char * szPtr;
char * szParam;
В начале работы мы помещаем нулевое значение в ноле d w H t t p S t a t u s C o d e
блока ЕСВ, а затем записываем в буфер s z B u f f заголовок HTTP и начальный
фрагмент документа HTML, формируемого динамически:
lpECB->dwHttpStatusCode = 0;

wsprintf(szBuff, "Content-Type: text/html\r\n\r\n"


"<НТМ1_><НЕАО><Т1Т1_Е>Права сотрудника</Т1Т|_Е></НЕАО>\п"
"<BODY BGCOLOR=#FFFFFFXH2>npaBa сотрудника</Н2>\п")';
Данные, отправленные браузером, копируются в буфер szTempBuf:
IstrcpynfszTempBuf, lpECB->lpbData, lpECB->cbAvailable + 1);
szTempBuf[lpECB->cbAvailable + 1] = *\0';
Эти данные затем перекодируются функцией DecodeStr:
DecodeStr(szTempBuf);
Исходный текст этой функции мы рассматривали в разделах, посвященных
программам CGI.
Далее мы запускаем цикл извлечения из принятых данных содержимого по-
лей формы с идентификатором сотрудника и его паролем:
szTempBuf[lpECB->cbAvailable] = '&';
szTempBuf[lpECB->cbAvailable + 1] = '\0';
Глава 7. Расширения CGI и ISAPI сервера Web 327

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 Базы данных б Интернете. Практическое руководство

Далее в буфер записывается завершающий фрагмент динамически формиру-


емого документа HTML, после чего содержимое буфера отправляется браузеру
посетителя при помощи функции S e r v e r S u p p o r t F u n c t i o n :
s t r c a t ( s z B u f f , "</BODY></HTHL>"};

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

На следующем этапе функция g e t _ m a n a g e r _ t a b l e выполняет привязку пара-


метров и вызов хранимой процедуры M a n a g e r L o g i n :
re = SQLAHocHandle(SQL_HANDLE_STMT, hDbC, ihStnt);
strcpy((criar*)szAdminName, szUserlD);

re = SQLBindParameter(hStmt, 1, SOL_PARAM_INPUT, SQL_C_CHAR,


SQL_CHAR, 50, 0, szAdminName, 51, ScbAdminName);

strcpy((char*)szAdminPass, szUserPassword);

re = SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR,


SQL_CHAR, 50, 0, szAdminPass, 51, &cbAdminPass);

re = SQLBindParameter(hStmt, 3, SQL_PARAM_OUTPUT, SQL_C_CHAR,


SQL_CHAR, 16, 0, szAdminRights, 51, ScbAdminRights);

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, мы исполь-
зовали здесь обычные функции стандартной библиотеки С.

Обращение к базе данных в отдельном потоке


В предыдущем разделе для запуска хранимой процедуры мы обращались к фун-
кциям интерфейса ODBC непосредственно из функции HttpExtensionProc. Од-
нако этот способ, хотя и работает, обладает одним существенным недостатком.
Этот недостаток связан с тем способом, которым сервер Microsoft Internet
Information Server обрабатывает запросы, получаемые от браузеров посетителей
через протокол HTTP.
Для таких запросов сервер IIS создает пул идентификаторов потоков, обра-
батывающих запросы. Если все потоки из этого пула заняты или находятся в
состоянии ожидания, сервер отвергает вновь поступающие запросы,
Чтобы этого не происходило, запросы к базам данных или другим сервисам,
отнимающие много времени, лучше обрабатывать в отдельных потоках. При
330 Базы данных в Интернете. Практическое руководство

этом поток, получивший запрос от браузера, запустит поток обращения к базе


данных и быстро вернет управление, освободив соответствующий слот пула
потоков для обработки других запросов.
Для иллюстрации того, как нужно выполнять вызов функций обращения к
базам данных из отдельного потока, мы подготовили второй вариант только что
рассмотренного приложения ISFORM.
Исходный текст главного модуля, измененный для использования отдельного
потока, мы привели в листинге 7-15.
Листинг 7-15 хранится в файле ch7\ISFORM THREADMsform.c на прилагаемом
к книге компакт-диске.
Рассмотрим внесенные изменения.
В области глобальных переменных мы определили переменные dwThreadID и
g_dwThreadCount:
DWORD dwThreadID;
DWORD g_dwThreadCount = 0;
Первая из них предназначена для хранения идентификатора потока, созда-
ваемого для обращения к базе данных, а вторая служит счетчиком потоков.
Формирование буфера выходного документа HTML выполняется в два при-
ема, Вначале функция HttpExtensionProc записывает в пего начальный фрагмент
документа без заголовка:
wsprintf(szBuff,
"<HTML><HEAD><TITLE>npaea сотрудника</ТП1_Е></НЕАО>\п"
"<BODY BGCOLOR=#FFFFFFXH2>npaBa сотрудника</Н2>\п");

strcat(szBuff, "User: ");


strcat(szBuff, szUserlD);
strcat(szBuff, "<br>Pasword: ");
strcat(szBuff, szUserPassword);
strcat(szBuff, "<BR>");
Далее функция H t t p E x t e n s i o n P r o c запускает функцию T h r e a d P r o c в отдель-
ном потоке, передавая ей в качестве параметра указатель на блок ЕСВ;
CreateThread(NULL, 0, ThreadProc, IpECB, 0, &dwThreadID);
Для запуска используется функция CreateThread. После запуска потока эта
функция сразу же возвращает управление. Далее функция H t t p E x t e n s i o n P r o c
увеличивает на единицу счетчик запущенных потоков (который Вы можете ис-
пользовать для статистики), а затем возвращает управление:
InterlockedIncrement(&g_dwThreadCount);
return HSE_STATUS_PENDING;
Важно, что при этом функция HttpExtensionProc, возвращая значение HSE_STA-
TUS_PENDING, не закрывает сеанс, а оставляет его открытым. В результате сеанс
взаимодействия браузера посетителя в сервером остается незавершенным, не-
смотря па то, что функция H t t p E x t e n s i o n P r o c закончила свою работу.
Завершение сеанса при этом возлагается на функцию ThreadProc, работаю-
•щую в отдельном потоке.
Глава 7. Расширения CGI и ISAP! сервера Web 331

Получив управление, эта функция вызывает функцию get_manager_table,


выполняющую обращение к базе данных:
if(!get_manager_table(szUserID, szUserPassword, szUserRights))
:
strcat(szBuff, "<br>Rights: ");
strcatCszBuff, szUserRights);
strcat(szBuff, "<BR>");
!
else

strcat(szBuff, "<п2>Произошла ошибка</п2>");


strcat(szBuff, sErrMsg);
}
strcat(szBuff, "</BODY></HTML>");
После вызова этой функции поток продолжает формирование содержимого
буфера выходного документа s z B u f f .
На следующем этапе поток отправляет браузеру заголовок документа HTTP,
вызывая для этого функцию S e r v e r S u p p o r t F u n c t i o n :
char szHeader[] = "Content-type: text/html\r\n\r\n";

header_ex_info.pszStatus = "200 OK";


header_ex_info.pszHeader = szHeader;
header_ex_info.cchStatus = strlen("200 OK");
header_ex_info.cchHeader = strlen(szHeader);
header_ex_info.fKeepConn = FALSE;

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

Создание серверных элементов


управления ActiveX

Б этой главе мы кратко рассмотрим методику создания собственных элементов


управления ActiveX для расширения объектной модели ASP. Мы будем назвать
их серверными элементами управления ActiveX.
В отличие от клиентских элементов управления ActiveX, загружаемых бра-
узером для работы на компьютере посетителя, серверные элементы управления
исполняются на сервере Web. Они не вызывают потенциальных проблем с бе-
зопасностью данных, возникающих при работе с клиентскими элементами
ActiveX, а также проблем с совместимостью браузеров, характерных для техно-
логии ActiveX.
Зачем Вам создавать собственный серверный элемент ActiveX?
Возможности серверных сценариев, размещенных в страницах ASP, ограни-
чены рамками объектной модели ASP. Хотя, как Вы уже убедились, эта модель
предоставляет прекрасные средства для проектирования интерфейса пользова-
теля и для обращения к базам данных, она имеет ограничения в плане вызова
функций программного интерфейса Win32, а также функций, расположенных в
библиотеках динамической загрузки DLL.
Другой недостаток, возникающий из-за применения в серверных сценариях
ASP интерпретируемых языков программирования jScript и VBScript — невы-
сокая производительность. При формировании элементов пользовательского
интерфейса этот недостаток не существенен, однако, если Ваше приложение
предназначено для выполнения интенсивной обработки больших объемов дан-
ных, вопросы производительности становятся значимыми.
Еще одна проблема, связанная с использованием серверных сценариев, име-
ет отношение к защите интеллектуальной собственности разработчика. Дело в
том, что исходные тексты серверных и клиентских сценариев могут быть легко
проанализированы администратором сервера Web, что не всегда желательно. Ре-
ализация бизнес-приложения в виде исполнимых модулей значительно затруд-
нит работу злоумышленника, решившего <<вскрыть* алгоритмы работы Вашего
приложения.
Конечно, все эти проблемы легко решаются при использовании таких расши-
рений сервера Web, как программы CGI или ISAPI, рассмотренные в предыду-
щей главе. Однако этот подход тоже имеет свои недостатки. Наиболее суще-
ственные из них — это невозможность тесного взаимодействия расширений сер-
Глава 8. Создание серверных элементов управпения ActiveX 133

вера Web с серверными сценариями ASP (удобными для создания пользователь-


ского интерфейса и обращения к базам данных), а также сложность отладки.
Намного проще создавать собственные расширения объектной модели в виде
серверных элементов ActiveX. Эта технология допускает тесную интеграцию с
серверными сценариями ASP. В случае реализации серверных элементов ActiveX
на языке программирования C++ обеспечивается возможность вызова любых
функций Win32 и функций из произвольных библиотек DLL. При этом обеспе-
чивается высокая производительность работы созданных таким образом объектов.
Следует заметить, что серверные элементы ActiveX можно создавать и с при-
менением таких языков программирования, как Visual Basic или Java, однако это
не всегда целесообразно. Язык Visual Basic удобен для разработки элементов
ActiveX, но его применение вызывает проблемы с производительностью, так как
Visual Basic относится к интерпретируемым языкам. Приложения Java также не
отличаются высокой скоростью работы и не всегда способны обращаться напря-
мую к интерфейсам операционной системы.
Поэтому в нашей книге мы расскажем лишь о приемах создания элементов
ActiveX, созданных с применением C++. Причем для облегчения работы мы
будем использовать библиотеку шаблонов Active Template Library (ATL). Она
предоставляет мощные средства для создания приложений СОМ, к которым
относятся элементы ActiveX.

Первый проект элемента ActiveX


В этом разделе мы опишем поэтапную процедуру создания простейшего сервер-
ного элемента управления ActiveX с применением Microsoft Visual C++ версии
6.0 и библиотеки шаблонов ATL.
Итак, начнем.

Создание проекта
Запустите Microsoft Visual C++ и выберите из меню File строку New. На экра-
не появится диалоговая панель New, открытая на вкладке Projects (рис. 8-1).

Fib: Projects j Workspaces | С th«Dociraenls

Щ] Cluster Resource Type Wizard iBookSloreLoginf/ad


Jjjj Custom AppWi;ard
Location'
^Database Pio[ect
^OevStudio Add-in Wizard 1 ' "'"" ' ' '^ ••"'
Щ Extended Slored Proc Wizard
filSAPIExlensionWizard
5 Makefile
jj MFC ActiveX CcntiolWizard Г .-
gMFCAppWizardlJ) Г
MFCApoWi:ard|eKe]
^ M ew Database Wizard 1 3
У) Utility Pioiecl
T] Win32 AppLcalion
RatlamiE
3 Win32 Console Application
|":Win32
^]Win32 Dynamic- Li nk LiBraiy

Г~5Г1 c«d |
Рис. 8-1. Диалоговая панель New
334 Базы данных в Интернете. Практическое руководство

В списке типов проектов, расположенном в левой части этой панели, выбе-


рите строку ATL СОМ АррWizard. Далее укажите в поле Location путь к ката-
логу, в котором будет создан новый проект.
В поле Project name задайте имя проекта. Мы советуем добавить к нему стро-
ку Mod или Module, чтобы отличать имя модуля от имени файла, созданного для
хранения исходного текста класса создаваемого объекта.
Заполнив описанным образом поля панели New, щелкните кнопку ОК. За-
пустится мастер проектов, первая панель которого показана на рис. 8-2.
ATI COM Anpwizvd Slep 1 of 1

1 his Wcard cieales an ATL; «eject wlhoLit any


initial COM objects. Aflei competing the Wizaid.
use Ihe New ATL Qfajecl con maud lipm
ClasiView to speedy the iype of obiecl .yeu would
Ike la atfcl to Ihra project

Servei Type •

§«ес1йаЫв ЕЕХЕ1

Service (EXE)

Рис. 8-2. Первая панель мастера проектов для элементов ActiveX


Оставьте отмеченным переключатель Dynamic Link Library (DLL) и отметьте
переключатель Allow merging of proxy/stub code. Далее щелкните кнопку Finish.
Сразу после этого мастер проектов создаст в указанном Вами каталоге фай-
лы исходных текстов, а затем выведет на экран панель со списком основных
созданных файлов (рис. 8-3).

COM wo BoakStorelogrnMod.ds£

FVoject Directory:

Рис. 8-3. Окончание работы мастера приложений


Глава 8. Создание серверных элементов управления ActiveX 335

Добавление объекта
В результате работы мастера проектов создается проект библиотеки DLL, в ко-
торой пока не определено ни одного объекта.
Выберите в меню Insert системы проектирования Microsoft Visual C++ стро-
ку New ATL Object. Откроется первая панели мастера объектов ATL ( рис. 8-4).

Рис. 8-4. Первая панель мастера объектов ATL


Здесь выделите пиктограмму ActiveX Server Component, а затем щелкните
кнопку Next. В результате на экране появится вторая панель мастера объектов
ATL с тремя вкладками (рис. 8-5).

Short Name; BiohSloreLogir

Рис. 8-5. Вкладка Names в панели мастера объектов ATL


В поле Short Name введите имя объекта как BookStoreLogin. Б результате
в остальных нолях этой вкладки появятся имена, выбранные для различных
объектов по умолчанию. Хотя Вы можете оставить все как есть, мы рекоменду-
ем отредактировать поля Туре и ProglD.
В поле Туре нужно ввести текстовое описание создаваемого объекта, удобное
для поиска в списке объектов, отображаемых программой OLE View, входящей
в комплект Microsoft Visual C++. Например, можно указать в начале этого поля
название Вашей фирмы, а в конце — название объекта.
В поле ProglD следует ввести два идентификатора, разделенных точкой. В
качестве первого можете набрать название фирмы (латинскими символами), а
в качестве второго — название объекта. Мы вводим в этом поле строку «Book-
Store .BookStoreLogin».
Теперь откройте вкладку Attributes (рис. 8-6).
336 Базы данных в Интернете. Практическое руководство

Рис. 8-6. Вкладка Attributes в панели мастера обьектов ATL


Здесь оставьте все элементы управления в том состоянии, в котором они
показаны на рисунке. Группа переключателей Threading Model позволяет вы-
брать одну из четырех моделей для работы с потоками. Не вдаваясь сейчас в тон-
кости отличия моделей, скажем, что в большинстве случаев следует выбирать
модель Apartment, как показано на рис. 8-6.
Если Ваш объект не создает событий, то переключатель Support Connection
Points следует оставить без отметки. Однако если в будущем Вы планируете
использовать события, этот переключатель необходимо отметить.
Остальные элементы управления оставьте в состоянии, показанном на рисунке.
Теперь откройте вкладку ASP (рис. 8-7),

П? fiesparse
P Session
P" i^PPfcafo

Рис. 8-7. Вкладка ASP в панели мастера обьектов ATL


Если Вы отметить переключатель OnStartPage/OnEndPage, показанный на
этом рисунке, создаваемый Вами элемент управления получит способность об-
ращаться к объектам, свойствам и методам ASP. Это могут быть, например, пе-
ременные сеанса или приложения, параметры заголовка запроса и другие объек-
ты, доступные серверным сценариям, расположенным на странице ASP.
Заметим, однако, что если все, что Вам нужно для связи серверного сцена-
рия и элемента управления ActiveX, это передача параметров, то достаточно
определить соответствующие методы и свойства. Именно так мы и поступим в
наших примерах.
Итак, мы установили все параметры па вкладках мастера объектов ATL. Те-
перь щелкните кнопку ОК для запуска генерации исходных текстов. Через не-
продолжительное время исходные тексты шаблона Вашего элемента управле-
Глава 8. Создание серверных элементов управления ActiveX 337

ния ActiveX будут построены. А нам надо определить собственные методы и


свойства.
Определение нового свойства
Сначала мы определим свойство с именем CheckResult, получающее в качестве
параметров две входные текстовые строки и возвращающее выходную тексто-
вую строку, созданную слиянием входных строк.
Откройте вкладку ClassView в главном окне Microsoft Visual C++ (рис. 8-8).
••-, Boe,kSt«eLoainMod Mitiosofl Visual C*+ [BookSlDreLoginMod idl]

''Ш Eile Edit View [rasa gfejecf |aM locfe Щпйои \je\(t jJfiJjS

•Й <?Ы0 - ' >• • ilUJSIW %lp1n._SS_S,vnan,e ZJ н

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^| }
'

9 DIGetll ^ _ ' d. FjJ [


uuid(36D176Cl-BCFA-HD3-9
"J ^ DodungView £J version (1 0 i ,
, п.— мим. — . ..- .1
helpstiing( " BookS torelogi iiMcd»|
"-^ ClassView j |jg Resi S* Nil *l
re» . " "
— i.=| rrpjpenies
л)
. LlUJV Bulld /O'buj X f»na in Files 1 \_f\nd m/iltsi X R8!:jlb X S a L D e b u 9 e i ^ S /

Adda fsc^artyla ihe s^eclsd hte'tace


[ 'I
?£.
Рис. 8-8. Добавление нового свойства
Раскройте папку интерфейса IBookStoreLogin, а затем щелкните правой кла-
вишей мыши строку IbookStoreLogin. Затем выберите из контекстного меню
строку Add Property, как это показано на рис. 8-8 (строка Add Method позво-
ляет добавить новый метод).
На экране появится панель Add Property to Interface, показанная на рис. 8-9.
Так как наше свойство предназначено для хранения строк, выберите тип
BSTR в списке Property Type, определяющем тип свойства.
В ноле Property Name Вы должны ввести имя свойства. В нашем случае это
имя CheckResult.
В иоле Parameters необходимо указать параметры метода, разделив их запя-
той. Укажите здесь параметры bsName и hsPassword типа BSTR*. Первый па-
раметр представляет собой указатель на строку т и н а BSTR с именем пользовате-
ля, а второй — указатель на строку BSTR с паролем пользователя.
Как Вы, наверное, знаете, для каждого свойства можно определить две фун-
кции, первая из которых предназначена для чтения содержимого свойства, а
вторая — для записи в свойство нового значения. Чтобы мастер создания свой-
338 Базы данных в Интернете. Практическое руководство

ства добавил исходный текст и описание соответствующей функции в проект,


необходимо отметить переключатели Get Function и Put Function.

Oopsriji CheckResul"))
HRESULT CheckResuKfflSTR taName.BSTR
. [out. retval|BSTR ТАУЦ:

Рис. 8-9. Панель, предназначенная для добавления нового метода


В нашем примере мы создаем только одно свойство, предназначенное для
чтения, поэтому надо отметить только один переключатель Get Function. Сде-
лав это, щелкните кнопку ОК. В проект будет добавлен исходный текст метода
CheckResult. Чтобы увидеть его исходный текст, раскройте папку интерфейса
IBookStoreLogin, расположенную в папке класса CbookStoreLogin, и дважды щел-
кните название метода get_CheckResult. Вот что Вы увидите:
STDMETHODIMP CBookStoreLogin::get_CheckResuU(
BSTR *bsNatne, BSTR *bsPassword, BSTR «pOutVal)

return S_OK;

Редактирование исходного текста свойства


Добавьте в определение метода следующие строки:
USES_CONVERSION;

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 без параметров, Вы увидите на экране краткую инструкцию
по ее использованию.

Подготовка страницы ASP


Теперь мы подготовим страницу ASP, вызывающую созданный нами серверный
элемент управления ActiveX.
На самом деле мы создадим две страницы- Первая из них представляет со-
бой обычный документ HTML с формой, в которой посетитель вводит свой
идентификатор и пароль (рис. 8-10).
Если после ввода информации щелкнуть кнопку Вход, управление будет
передано странице ASP, вызывающей наш элемент управления ActiveX. На
этой странице отображается строка, сформированная элементом BookStoreLogin
(рис. 8-11).
340 Базы данных в Интернете. Практическое руководство

'Э Подключение пользователя - Miciosoll Inter. (41*10


Ete £<Si ^iew Favosiiaa Icofe #e\p

Добро пожаловать!
Имя prolov

Пароль F

Рис. 8-10. Форма для ввода идентификатора и пароля

a httn //fiolov^ft/HnokStoreLogin.asp - Micinsofl IntE

Подключение пользователя
Бы ввели (идентификатор "пар о ль) - frolov:12345

|sj Local tntwief

Рис. 8-11. Результат работы серверного сценария


Исходный текст документа HTML с формой представлен в листинге 8-1.
Листинг 8-1 Вы найдете в файле ch08/BookStoreLoginMod/bslogin.html на при-
лагаемом к книге компакт-диске.
Определенная в нем форма содержит два поля редактирования с именами USR
и PWD:
<FORM ACTION="BookStoreLogin.asp" METHOD="pOSt" TARGET="_top">
<Н1>Добро пожаловать!</Н1>
•CTABLE BORDER=0 CELLPADDIN6=5 CELLSPACING=0>
<TR><TD>HMH</TD><TD>
<INPUT SIZE=10 TYPE="EDIT" NAME="USR"X/TDX/TR>
<ТПхТО>Пароль</ТОХТО>
<nobr>
<INPUT SIZE=10 TYPE="password" NAME="PWD">
<INPUTTYPE="submit" VALUE="Bxofl"x/nobr>
</TDX/TR>
</TABLE>
</FORM>
После щелчка кнопки Вход управление передается странице с именем Book-
StoreLogin.asp, указанным в параметре ACTION тега <FORH>. Исходный текст этой
страницы представлен в листинге 8-2.
Листинг 8-2 Вы найдете в файле chOS/BookStoreLoginMod/BookStoreLogin-
Mod.asp на прилагаемом к книге компакт-диске.
Глава 8. Создание серверных элементов управления AcliveX 341

Первая строка в файле BookStoreLogin.asp задаст язык серверного сценария


JScript:
<*@ LANGUAGE = "JScript" %>
Далее в документе расположен фрагмент серверного сценария, показанный
ниже:
<%
var sUser=Request("USR")(1);
var sPassword=Request("PWD")(1);

var culogin = Server.CreateObject("BookStore.BookStoreLogin"};


var sResult = culogin.CheckResult(sUser, sPassword);
X>

Здесь мы вначале извлекаем параметры с именами USR и PWD, переданные


формой, и сохраняем эти параметры в переменных sUser и sPassword соответ-
ственно.
Далее с помощью метода CreateObject встроенного объекта ASP с именем
Server мы создаем объект с идентификатором BookStore.BookStoreLogin. Это тот
самый идентификатор, который мы определяли при создании проекта в панели,
показанной на рис. 8-5.
Объект будет записан в переменную culogin, при помощи которой сценарий
обращается к свойствам и методам объекта. Строкой ниже мы читаем свойство
CheckResult, передавая ему в качестве параметров идентификатор посетителя и
его пароль.
Значение, прочитанное из свойства, сценарий записывает в переменную
sResult. Далее эта переменная используется при формировании текста докумен-
та HTML, создаваемого сценарием:
<р>Вы ввели (идентификатор: пароль) - <b><%= sResult Jfx/b>

Определения методов элемента


Исходный текст метода C h e c k R e s u l t , а также двух других методов, созданных
мастером проекта, находится в файле BookStoreLogin.cpp (листинг 8-3).
Листинг 8-3 Вы найдете в файле ch08/BookStoreLoginMod/BookStoreLogin.cpp
на прилагаемом к книге компакт-диске.
Рассмотрим его содержимое.
Помимо файла stdafx.h мастер проекта включает в файл BookStoreLogin.cpp
файлы BookStoreLoginMod.h и BookStoreLogin.h:
tfinclude "BookStoreLoginMod.h"
^include "BookStoreLogin.h"
Первый из них создается автоматически и содержит определения интерфей-
сов. Вам не нужно его редактировать, так как это сделает мастер проекта при
добавлении новых методов и свойств.
Второй файл содержит определение класса CBookStoreLogin, который пред-
ставляет собой главный класс нашего объекта. Опять же, при добавлении в оп-
ределение этого класса новых полей мастер проекта произведет все изменения
в файле автоматически.
342 Базы данных в Интернете. Практическое руководство

Методы OnStartPage и OnEndPage генерируются автоматически, если при со-


здании проекта отметить переключатель OnStartPage/OnEndPage на вкладке
ASP (рис. 8-7).
Когда посетитель запрашивав! страницу ASP, управление получаст метод
OnStartPage. Ему передается указатель на интерфейс Illnknown, с помощью кото-
рого получит указатели на интерфейсы, н у ж н ы е для связи нашего элемента со
встроенными элементами ASP:
STDMETHODIMP CBookStoreLogin: :OnStartPage (lUnknown* pUnk)

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;

Автоматическая обработка кредитных карточек


Создавая Интернет-магазин, Вы должны продумать способ оплаты товара. Наи-
более современный способ оплаты — с помощью кредитных карточек. Мы уже
рассказывали о том, как организовать такую оплату с привлечением проиессин-
говых компаний, выполняющих обработку кредиток через Интернет.
Как Вам известно, такие компании предоставляют всем желающим интер-
фейс в виде библиотеки DLL или автономной программы. Модуль интерфейса
Глава 8. Создание серверных элементов управления ActiveX 343

необходимо разместить на сервере Web Вашего магазина, причем вызов функ-


ций этого модуля должен выполняться из программного обеспечения магазина.
Если магазин создан с применением технологии ASP, нужно придумать способ
вызова интерфейсного модуля из серверных сценариев, написанных на языках
JScript или VBScript.
В этом разделе мы расскажем о том, как создать серверный элемент управ-
ления ActiveX, вызывающий функции из библиотеки DLL t имитирующей часть
интерфейса обработки кредитных карточек.

Библиотека для имитации интерфейса


Наш демонстрационный интерфейс обработки кредитных карточек состоит все-
го из одной функции с именем f n S e n d P a y D a t a . Ее задача — отправить информа-
цию о платеже на сервер процессинговой компании. Напомним, что при созда-
нии реального магазина Вы получите интерфейсный модуль от процессинговой
компании.
Прототип функции f n S e n d P a y D a t a показан ниже:
extern "С" declspec(dllexport) int fnSendPayData(
LPSTR szAmount, LPSTR szCurrency, DWORD dwMerchantID,
LPSTR szSuccessURL, LPSTR szErrorURL, LPSTR szNoyificationURL);
Так как предполагается, что мы вызываем функцию из программы, написан-
ной на C++, для отключения механизма «украшения» имени функции (name
mangling) используется определение «extern "С"*.
Чтобы функция f n S e n d P a y D a t a попала в список экспортируемых функций,
определим ее как declspec(dllexport). Это позволит нам не создавать def-файл
определения модуля библиотеки DLL.
При вызове функции fnSendPayData необходимо передать шесть параметров.
Через первые два параметра szAmount и szCurrency передается величина суммы
и название валюты. И тот, и другой параметр задается в виде текстовой строки.
Третий параметр d w M e r c h a n t I D — идентификатор покупателя, назначенный
ему при регистрации и зарегистрированный в процессинговой компании. По
этому идентификатору компания сможет определить, кто выполняет платеж.
Через последние три параметра интерфейсный модуль передает серверу ма-
газина адреса URL-страниц, на которые попадает посетитель в зависимости от
результата выполнения платежа. Если результат успешный, посетитель попада-
ет на страницу, адрес которой задан параметром szSuccessURL, если нет — па стра-
ницу с адресом s z E r r o r U R L , а если при выполнении операции появилась допол-
нительная информация — на страницу с адресом s z N o y i f i c a t i o n U R L .
Функция fnSendPayData возвращает код выполнения операции. Если этот код
равен нулю, операция выполнена успешно, а если нет, то это означает, что были
ошибки.
Рассмотрим действия, выполняемые функцией fnSendPayData. Ее исходный
текст Вы найдете в листинге 8-4.
Листинг 8-4 хранится в файле chOS/CreditCardlnterface/CreditCardlnterface.cpp
на прилагаемом к книге компакт-диске.
Прежде всего эта функция проверяет идентификатор покупателя:
if(dwMerchantID l= 12345)
return 1;
344 Базы данных в Интернете. Практическое руководство

Мы разрешаем выполнение платежа только для посетителя с идентификато-


ром 12345. При ошибке возвращается значение 1. Процессинговая компания
проверяет этот идентификатор но своей базе данных.
Далее функция f n S e n d P a y D a t a убеждается, что параметры s z A m o u n t и s z C u r -
rency не содержат нулевых значений:
if(strlen(szAmount) == 0 || strlen(szCurrency) == 0)
return 2;
В случае ошибки функция возвращает значение 2.
Если же параметры указаны правильно, функция f n S e n d P a y D a t a записывает
имена файлов по адресам, хранящимся в параметрах szSuccessURL, s z E r r o r U R L и
szNoyificationURL:
if(szSuccessURL !<= NULL)
stгоруСszSuccessURL, "sucsess.html");

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;

Она вызывается, когда процесс или поток обращается к библиотеке. Эта


функция была создана мастером Microsoft Visual C++ и не выполняет никаких
действий.

Тестовая программа для вызова имитатора интерфейса


Прежде чем вызывать только что описанный модуль из серверного сценария
ASP, выполним его тестирование. Для этого мы подготовили небольшую кон-
сольную программу, исходный текст которой Вы найдете в листинге 8-5.
Листинг 8-5 хранится в файле chOts/CreditCardlnterfaceTest/
CreditCardlnterfaceTest.cpp на прилагаемом к книге компакт-диске.
Глава 8. Создание серверных элементов управления ActiveX 345

В области глобальных переменных этой программы мы подготовили опреде-


ление типа DLLFN:
typedef int (* DLLFN)(LPSTR szAmount, LPSTR szCurrency,
DWORD dwMerchantID,LPSTR SZSuccessURL, LPSTR szErrorURL,
LPSTR szNoyificationURL);
Как видите, этот тип представляет собой указатель на функцию f n S e n d -
PayData, описанную ранее.
Тестовая программа вызывает функцию fnSendPayOata, предварительно вы-
полняя динамическую загрузку библиотеки DLL с именем CreditCardlnter-
face.dll.
Библиотека DLL загружается функцией L o a d L i b r a r y , как это показано ниже:
HINSTANCE hCreditCardlnterfaceDLL;
hCreditCardlnterfaceDLL = LoadLibraryC"CreditCardInterface.dll");
Идентификатор загруженной библиотеки сохраняется в локальной перемен-
ной h C r e d i t C a r d l n t e r f a c e D L L .
Если загрузка прошла успешно, тестовая программа вызывает функцию
f nSendPayData, предварительно получив на нее указатель с помощью функции
GetProcAddress:
DLLFN fn;

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);

Здесь мы передаем функции сумму 123, название валюты — «usd», иденти-


фикатор покупателя 12345. Адреса URL функция запишет в переменные szSuc-
cessURL, s z E r r o r U R L и szNoyificationURL.
Убедившись с помощью тестовой программы и отладчика, что интерфейсная
библиотека DLL работает нормально, мы переходим к созданию серверного эле-
мента управления ActiveX, доступного из страниц ASP и предназначенного для
вызова функции f nSendPayData,
Элемент управления CreditCard
В начале этой главы мы подробно рассмотрели процедуру создания серверного
элемента управления ActiveX с применением библиотеки шаблонов ATL. Поэто-
му сейчас мы не будем вдаваться в детали.
346 Базы данных в Интернете. Практическое руководство

Нам нужно создать модуль CreditCardMod. Идентификатор элемента уп-


равления должен быть указан как «Frolov. CreditCard». Исходный текст главного
модуля элемента управления, созданный мастером и дополненный нами, Вы
найдете в листинге 8-6.
Листинг 8-6 хранится в файле chOB/CreditCardMod/CreditCard.cpp на прилагае-
мом к книге компакт-диске.
Рассмотрим определения свойств и методов, добавленных нами к объекту
CreditCard.
Для свойства Amount, предназначенного для хранения суммы денег, мы пре-
дусмотрели две функции с именами g e t _ A m o u n t и p u t _ A m o u n t :
STDMETHODIMP CCreditCard::get_Amount(BSTR *pVal)
<
*pVal=m_Amount.Copyf);
return S_OK;

STDMETHODIMP CCreditCard::put_Amount(BSTR newVal)

m_Amount=newVal;
return S_OK;

Обе эти функции обращаются ic полю m _ A m o u n t типа CComBSTR:


CComBSTR m.Amount;
Мы добавили эту переменную в класс C r e d i t C a r d , пользуясь мастером класса.
Функция put_Amount записывает в поле m_Amount значение, полученное через
параметр newVal.
Что же касается функции get^Amount, то она извлекает значение из поля
m _ A m o u n t и возвращает его, вызывая метод Сору.
Аналогичным образом устроены функции для работы со свойствами Currency,
SuccessURL, E r r o r U R L и N o t i f i c a t i o n U R L . Для них в классе C r e d i t C a r d мы опре-
делили следующие поля:
CComBSTR mJErrorURL;
CComBSTR m_NotificationURL;
CComBSTR m_SuccessURL;
CComBSTR m_Currency;
Функции для работы со свойством M e r c h a n t I D , хранящим численное значе-
ние, определены так:
STDMETHODIMP CCreditCard::get_MerchantID(long *pVal)
{
*pVal=m_MerchantID;
return S_OK;

STDMETHODIMP CCreditCard::put_MerchantID(long newVal)

m_MerchantID=newVal;
return S_OK;
Глава 8. Создание серверных элементов управления ActiveX 347

Эти функции обращаются к полю m_MerchantID типа long.


Теперь мы займемся методом SendPayData, предназначенным для вызова на-
шей библиотеки DLL, имитирующей интерфейс процессинговой компании.
Так же как и в тестовой программе, мы определили тип DLLFN, необходимый
для вызова функции fnSendPayData через указатель:
typedef int (* DLLFN)(LPSTR szAmount, LPSTR szCurrency,
DWORD dwMerchantID,LPSTR szSuccessURL, LPSTR szErrorURL,
LPSTR szNoyificationURL);
В самом начале метода SendPayData, вызывающего эту функцию, мы распо-
ложили макрокоманду USES_CONVERSION, необходимую для работы с макрокоман-
дами перекодировки OLE2A и A20LE:
STDMETHODIMP CCreditCard:: SendPayDataO
I
USES_CONVERSION;

В области локальных переменных метода SendPayData мы определили пере-


менную для хранения идентификатора библиотека DLLhCreditCardlnterfaceDLL,
переменную для хранения указатель на функцию fnSendPayData с именем fп, три
массива для хранения адресов URL с именами s z S u c c e s s U R L , s z E r r o r U R L и
s z N o t i f i c a t i o n U R L , а также рабочую переменную bstrTemp типа CComBSTR:
HINSTANCE hCreditCardlnterfaceDLL;
DLLFN fn;
char szSuccessURL[512];
char szErrorURL[512];
char szNotificationURL[512];
CComBSTR bstrTemp;
В начале своей работы метод SendPayData загружает интерфейсную библио-
теку DLL из файла CreditCardInterface.dll и получает указатель на функцию
fnSendPayData:
hCreditCardlnterfaceDLL = LoadLibraryC'CreditCardlnterface.dll");
if(hCreditCardlnterfaceDLL != NULL)
1

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 Базы данных в Интернете. Практическое руководство

Теперь можно вызывать функцию f nSendPayData:


m_Result = fn(pszAmount, pszCurrency, m_Merchant!D,
szSuccessURU, szErrorURL, szNotificationURL);
Ее код возврата мы записываем в поле m _ R e s u l t , определенное в классе
CreditCard как long.
Если параметры для функции указаны без ошибок, метод освобождает иден-
тификатор библиотеки DLL, а затем преобразует полученные строки адресов
URL, записывая их в соответствующие поля класса CreditCard:
FreeLibrary(hCreditCardlnterfaceDLL);
m_SuccessURL = A20LE(szSuccessURL);
m_ErrorURL = A20LE(szErrorURL);
m_NotificationURL = A20LE(szNotificationURL);
При ошибке в ноле m_Result записывается значение -1:
m_Result = -1;
Сценарий определяет результат вызова функции с помощью свойства Result,
доступного только для чтения. Для этого свойства мы подготовили только одну
функцию с именем get_Result:
STDMETHODIMP CCreditCard::get_Result(long *pVal)
\
*pVal=m_Result;
return S_OK;

Вызов элемента управления CreditCard


Для вызова элемента управления CreditCard мы подготовили документ HTML
с формой, позволяющей задать идентификатор платежа, сумму и выбрать валюту
(рис. 8-12).
3 h(lp://iatuin/pay.html • Microsoft Internet Exploiei pmvi.

Введите сведения о платеже


Идентифйкатср

Сумма1

Валюта (Рубли

Отменить

I ideal Nranel

Рис. 8-12. Форма для вызова элемента управления CreditCard


Полный исходный текст этого документа расположен в листинге 8-7.
Листинг 8-7 Вы найдете в файле chOS/CreditCardMod/pay.html на прилагаемом
к книге компакт-диске.
Глава 8. Создание серверных элементов управления ActiveX 349

Этот документ содержит форму, ссылающуюся па страницу ASP с именем


dopay.asp:
<form method="POST" action="dopay.asp">
В форме определены поля ввода идентификатора M e r c h I D , суммы платежа
Amount, а также поле кода валюты C u r r e n c y :
<tr>
<td width="165">HfleHTH<|)HKaTop:</td>
<t<J width="184">
<input type="text" name="MerchID" size="20"x/td>
</tr>
<tr>
<td width="165">CyMMa:</td>
<td width="184">
<input type="text" name="Amount" size="20"></td>
</tr>
<tr>
<td width="165">Baлютa:</td>
<td width="184">
<select name="Currency" size="1">
<option selected value="rub">Py6nH</option>
<option value="usd">flc^apbi</option>
<option value="dm">MapKM</option>
</selectx/td>
</tr>
Исходный текст сценария, выполняющего вызов элемента управления Сге-
ditCard, представлен в листинге 8-8.
Листинг 8-8 Вы найдете в файле ch08/CreditCardMod/dopay.asp на прилагае-
мом к книге компакт-диске.
В начале своей работы сценарий создает объект C r e d i t C a r d , указывая его
идентификатор:
var ее = Server.CreateObject("Frolov. CreditCard");
Ссылка на объект записывается в переменную ее, с помощью которой мы
будем обращаться к свойствам и методам объекта.
Первым делом необходимо извлечь из нолей формы информацию о платеже.
Мы это делаем с помощью объекта Request:
ее,Amount = Request("Amount")(1);
се.Currency = Request("Currency")(1);
var MerchID="";
MerchlD = Request("MerchID")(1);
if(MerchID != "")
cc.MerchantID = parse!nt(MerchID, 10);
else
cc. MerctiantID = 0;
Так как в форме может быть указан пустой идентификатор, мы обрабатыва-
ем такую ситуацию как ошибочную, записывая при этом в свойство MerchantID
нулевое значение.
350 Базы данных в Интернете. Практическое руководство

Далее сценарий вызывает метод S e n d P a y D a t a :


cc.SendPayDataO;
Результаты работы метода SendPayData извлекаются из свойств элемента уп-
равления и сохраняются в соответствующих локальных переменных:
var Result = ее.Result;
var sSuccessURL = cc.SuccessURL;
var sErrorURL = cc.ErrorURL;
var sNotificationURL = cc.NotificationURL;
Далее, если метод выполнен без ошибок (то есть если в свойстве ее. R e s u l t
находится нулевое значение), наш сценарий отображает содержимое всех
свойств объекта:
itXResult == 0)

Х>
<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

В противном случае в окне браузера отобразится лишь код завершения.

Отправка почтового сообщения из сценария ASP


Как правило, в Интернет-магазинах, созданных за рубежом, оплата товаров про-
изводится с помощью кредитных карточек. Что же касается отечественных
Интернет-магазинов, то в них преобладают другие формы: предварительный
сбор заказов и оплата наличными или через сберегательный банк.
При этом торгующий сервер Web выступает в роли автоматизированной си-
стемы сбора заказов, Такие заказы обычно отправляются в магазин в виде элек-
тронных почтовых сообщений и затем обрабатываются сотрудниками магазина
вручную. Для подтверждения покупки сделавшему заказ посетителю сервера
перезванивают по телефону или запрашивают подтверждение по электронной
почте. Это не слишком удобно, но более или менее надежно. В качестве приме-
ра можно привести маленький магазин, расположенный на сервере авторов этой
книги по адресу http://www.frolov.pp.ru, а также крупные отечественные книж-
ные магазины, подобные Bolero (http://www.bolero.ru).
Для практической реализации системы автоматизированного сбора предва-
рительных заявок обычно используются программы CGI типа FormMail. Эти
программы принимают данные из полей формы и отправляют их почтовому
серверу при помощи протокола SMTP.
А сейчас речь пойдет о применении серверного элемента управления ActiveX
с названием MTASend, разработанного Максимом Синевым из компании Spekt-
ruin Web Development (http://www.spektrum.org.ru) и предназначенного для от-
Глава 8. Создание серверных элементов управления ActiveX 351

правки почты. Так как этот элемент реализует интерфейс автоматизации, его
можно вызывать из клиентских и серверных сценариев, а также из любых про-
грамм, способных работать с объектами СОМ.

Основы работы SMTP


Как Вы знаете, простой протокол передачи почты Simple Mail Transfer Protocol
(SMTP) основан на передаче текстовых строк через порт с номером 25. Как
правило, провайдеры требуют, чтобы для передачи почты было установлено
прямое соединение между сервером или рабочей станцией, отправляющей по-
чту, и почтовым сервером провайдера (то есть они должны находиться в сети с
одним адресом IP).
Прежде чем познакомить Вас с исходными текстами серверного элемента
управления MTASend, мы попытаемся отправить почтовое сообщение с помо-
щью программы telnet, входящей в комплект операционной системы Windows.
Итак, подключитесь к своему провайдеру и запустите программу telnet. Вы-
берите из меню Connect строку Remote system. Укажите в поле Host name ад-
рес почтового сервера своего провайдера, а в поле Port — значение 25. Щелкните
кнопку Connect для подключения к почтовому серверу провайдера.
Если адрес сервера указан правильно, в окне программы появится сообще-
ние от почтового сервера. Пример такого сообщения для сервера snitp.galsnet.ru
показан ниже:
220-hawk.glas.apc.org Smail-3.2.0.109 («4 1999-Nov-10) ready at Sun, 26 Dec 1999
20:42:55 +0300 (HSK)
220 ESMTP supported
Для другого сервера внешний вид приглашения может отличаться от приве-
денного выше, однако текст должен начинаться с цифры 220.
Теперь отправьте серверу команду подключения HELO:
НЕЮ smtp.glasnet. ru
Сервер должен ответить сообщением с кодом 250:
250 hawk.glas.apc.org Hello smtp.glasnet.ru (smtp.glasnet.ru from address
[195.178.200.74]).
Продолжим диалог с сервером: командой MAIL FROM укажите адрес отправи-
теля (тое есть свой адрес):
MAIL FROM: alexandre@frolov.pp.ru
В ответ Вы должны получить сообщение с кодом 250:
250 2.1.0 alexandre@frolov.pp.ru Sender Okay.
Теперь введите адрес получателя. Мы указали здесь свой собственный адрес,
так как отправляем тестовое сообщение сами себе:
RCPT ТО: alexandre@frolov.pp.ru
Вам придет подтверждение следующего вида:
250 2.1.0 'alexandre@frolov.pp.ru' Recipient Okay.
Далее мы должны ввести текст сообщения. Выдайте серверу команду DATA:
DATA
, Базы данных в Интернете. Практическое руководство

Вы получите приглашение для ввода текста сообщения с кодом 354:


354 Enter mail, end with " . " on a Line by i t s e l f . . .
Введите тему сообщения командой SUBJECT:
SUBJECT:Test
Тема указывается в одной строке, непосредственно после команды. Закончите
ввод строкой, состоящей из одной точки в первой позиции:
This is test

Сообщение будет отправлено, а Вы получите извещение об этом в следующем


виде:
250 2.6.0 Mail accepted, queue ID m122Hir-001oz7n.
Теперь Вы можете разорвать соединение с почтовым сервером и завершить
работу программы telnet. Запустив свою почтовую программу, убедитесь, что Вы
получили новое сообщение.

Элемент управления MTASend


Мы покажем, как использовать элемент управления MTASend для отправки
почтовых сообщений из формы, расположенной на Вашем сервере Web.
Форма показана на рис. 8-13.
3 ''"Р //£uturn/SBndnait.html - Miciosoft Inleinel Enplmer piovided by wn . ИИЙ1ЕЗ

Заполните поля н отправьте сообщение

Кому: jftolovSlglBsnetru

От кого. |trolDV©glosnettij

Subject: [fesTT-om ASP

Сервер SMTP [srntp.glesrel.ru


Это тестовое сообщение
Сообщение

_дтгудвитьj Отивиить

Рис. 8-13. Форма для отправки почты


При ее з а п о л н е н и и н у ж н о указать адрес отправителя и получателя, тему
письма Subject, а также адрес почтового сервера Вашего провайдера. Для отправ-
ки сообщения щелкните кнопку Отправить.
Если электронное письмо отправлено без ошибок, в окне браузера появится
сообщение, показанное на рис. 8-14.
Полный исходный текст формы отправки сообщения показан в листинге 8-9.
Листинг 8-9 Вы найдете в файле chOS/MTASend/sendmail.html на прилагаемом
к книге компакт-диске.
Глава 8. Создание серверных элементов управления ActiveX 353

-3 Результаты отправки почты - Micr.

Рис. 8-14. Сообщение об успешной отправке почты


Форма ссылается на страницу ASP с именем mta.asp, которая и выполняет
обращение к элементу управления MTASend:
<form method="POST" action="mta.asp">
В форме также определены поля с именами То, From, S u b j e c t , SMTPServer и
Message:
<trxtd width="171">KoMy:</tdXtd width="297">
<input type="text" name="To" size="20"x/td>
</tr><tr>
<td width="171">OT Koro:</tdXtd width="297">
<input type="text" name="From" size="20"x/td>
</tr><tr><td width="171">Subject:</td><td width="297">
<input type="text" name="Subject" size="20"></td>
</trxtrxtd width="171">Cepeep SMTP:</td><td width="297">
<input type="text" name="SMTPServer" size="20"x/td>
</tr><tr><td width="171">Coo6meHHe:</td> <td width="297">
<textarea rows="4" name="Message" cols="31"X/textarea>
Исходный текст страницы mta.asp Вы найдете в листинге 8-10.
Листинг 8-10 Вы найдете в файле cliOS/MTASend/mta.asp на прилагаемом к
книге компакт-диске.
В самом начале этого листинга находится тег <OBJECT>, закрытый символами
комментария:
<!- <OBJECT RUNAT=SERVER ID=MTAMail CLASSID="CLSID:F477288F-ABB2-11D3-9DE9-
204C4F4F5020"X/OBJECT> ->
Вы можете использовать его вместо метода CreateObject для создания объекта
MailMTA, средствами которого выполняется передача почты:
var MTAHail = Server.CreateObjectC'MailATL.MTA 1 1 );
После того как сценарий создает объект MailMTA и записывает его в перемен-
ную MTAMail, он получает данные из полей формы п помещает их в соответству-
ющие свойства:
MTAMail.To=flequest("To")(1);
MTAMail.From=Request("From")(1);
MTAMail.Subject=Request("Subject">(1);
MTAMail.Message=Request("Message"}(1);
MTAMail.SMTPSe rver=Request("SMTPServer")(1);
MTAMail.SMTPPort=25;
В свойство SMTPPort мы записываем значение 25, так этот порт используется
стандартными почтовыми серверами.
354 Базы данных в Интернете. Практическое руководство

Подготовив свойства, сценарий вызывает метод Send, выполняющий отправку


почты:
MTAHail.SendQ;
Результат выполнения операции отправки почты сохраняется в свойстве
Status, и наш сценарий может его проверить. Если почта отправлена без оши-
бок, сценарий записывает в создаваемый документ HTML соответствующее со-
общение:
if(MTAMail. Status == 1)
I
%>
<html>
<head>
<Ш1е>Результаты отправки почты<ДпЛ1е>
</head>
<body>
<р>Почта успешно отправлена
</body>
</html>
<%}
В противном случае в текст сообщения об ошибке включается код заверше-
ния из свойства Status, а также уточняющий код ошибки из свойства StatusEx:
else {
*>
<html>
<head>
<title>Pe3ynbraTbi отправки no4Tbi</title>
</head>
<body>
<р>0шибка. RC = <K=MTAMail.Status*> - <X=MTAMail.StatusExX>
</body>
</html>

Tejjcpb мы кратко. рассмотрим исходные тексты элемента управления


MTASend.
Прежде всего, обратите внимание на файл MailATL.cpp (листинг 8-11).
Листинг 8-11 Вы найдете в файле ch08/MTASend/MailATL.cpp на прилагаемом
к книге компакт-диске.
В нем нам интересны функции DllRegisterServer и DllUnregisterServer, пред-
назначенные соответственно для регистрации и для отмены регистрации элемен-
та управления:
STDAPI DURegisterServer(void)

WSADATA wsaData;
WSAStartupfO, &wsaData);
return Module. RegisterServer(TRUE);
Глава 8. Создание серверных элементов управления ActiveX 355

STDAPI DllUnregisterServer(void)

WSACleanupC);
return _Module.UnregisterServer(TRUE);

В них мы вызываем функции W S A S t a r t u p и WSACleanup, предназначенные для


инициализации сокетов и для освобождения ресурсов приложения, полученных
для работы с сокетами.
Определение методов Вы найдете в файле МТА.срр (листинг 8-12).
Листинг 8-12 хранится в файле chOS/MTASend/МТА.срр на прилагаемом к кни-
ге компакт-диске.
Для сокращения объема исходных текстов, определяющих функционирова-
ние сходных методов, в этом файле определена макрокоманда MTAStringProperty:
«define MTAStringProperty(v) \
STDMETHODIMP MTA::get_Sffv(BSTR *pVal) \
{ \
AFX_MANAGE_STATE(AfxGetStaticModuleState<)} \
CComBSTR bstrTemp(mJf#v); \
*pVal = bstrTemp.DetachO; \
return S_QK; \
} \
STDMETHODIMP MTA::put_tt»v(BSTR newVal) \
! \
AFX_MANAGE_STATE(AfxGetStaticModuleState()) \
m_##v=newVal; \
return S_OK;

Она определяет два метода, один из которых извлекает значение свойства из


соответствующего поля класса, а другой — сохраняет значение в поле класса.
Вот как просто выглядит определение нескольких свойств, сделанное с по-
мощью этой макрокоманды:
MTAStringProperty(From)
MTAStringProperty(To)
MTAStringProperty(SMTPServer)
MTAStringProperty(Message)
MTAStringProperty(Subject)
MTAStringProperty(Encoding)
MTAStringProperty(Format)
В классе МТА определен конструктор, выполняющий начальную инициализа-
цию свойств значениями, взятыми из ресурсов элемента управления:
МТА::МТА()
I
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));

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;

Прежде чем приступить к работе, этот метод устанавливает код завершения,


равный 1, в ноле m_Status. Этот код впоследствии можно извлечь из свойства
Status.
Далее метод Send устанавливает соединение с почтовым сервером, вызывая
метод C o n n e c t . Если эта процедура выполнена успешно, метод Send вызывает
метод T r a n s f e r , выполняющий передачу данных. Далее программа отключается
от почтового сервера при помощи метода Disconnect. В случае возникновения
каких-либо ошибок устанавливается новое состояние в поле m Status.
Глава 8. Создание серверных элементов управления ActiveX 357

Исходный текст метода Connect мы рассмотрим ниже.


Получив управление, метод Connect и н и ц и а л и з и р у е т структуру srv_addr типа
s o c k a d d r _ i n , записывая в нее адрес почтового сервера:
m_StatusEx=0;
int rc=TRUE;
struct sockaddr^in srv_addr, cli_addr;

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)
'

Этот сокет затем привязывается функцией b i n d к адресу IP почтового сер-


вера:
cli_addr.sin_family=AF_INET;
cli_addr.sin_addr,s_addr=INADDR_ANY;
cli_addr.sin_port=0;

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 Базы данных в Интернете. Практическое руководство

Теперь мы займемся методом T r a n s f e r , выполняющим обмен данными с по-


чтовым сервером по каналу, созданному методом C o n n e c t .
В начале с помощью макрокоманды WAIT этот метод дожидается получения
от почтового сервера сообщения о готовности:
WAIT(220);
Когда сообщение, имеющее код 220, приходит, мы посылаем почтовому сер-
веру команду НЕЮ, указав ей в качестве параметра адрес почтового сервера:
W r i t e L n f H E L O "+m_SMTPServer);
Для отправки почтовому серверу текстовой строки мы вызываем функцию
W r i t e L n , определенную в нашей программе.
Дождавшись подтверждения с кодом 250, мы сообщаем серверу адрес отпра-
вителя:
СНЕСК(250);
WriteLn("MAIL FROM:<"+m_From+">") ;
Далее метод снова ожидает подтверждения и посылает почтовому серверу
аналогичным образом адрес получателя и содержимое письма, включая все стан-
дартные .заголовки. Отправка заканчивается пустой строкой и строкой, содер-
жащей одну точку в первой позиции:
WriteLnC'");
WriteLnC".");
СНЕСК(250);
Далее наша программа отключается от почтового сервера:
WriteLn("QUrr);
СНЕСК(221);
Работа методов W r i t e L n и ReadLn, с помощью которых наш элемент управле-
ния общается с почтовым сервером, основана на применении функций send и
recv. Эти функции предназначены для обмена данными через потоковые сокеты.
Метод Disconnect, выполняющий отключение от почтового сервера, закры-
вает сокет функцией closesocket:
void MTA::Disconnect()

if(m Socket!=INVALID SOCKET)


{
closesocket(m_Socket);
m_Socket=INVALID_SOCKET;

m_Stoped=-1;
ГЛАВА 9

Применение аплетов Java

Последняя глава нашей книги посвящена использованию аплетов Java в прило-


жениях Интернета. Как Вы знаете, программы, написанные на языке програм-
мирования Java, способны работать практически на всех распространенных ком-
пьютерных платформах. В результате одна из разновидностей таких программ,
а именно аплеты Java, получили широкое распространение па страницах серве-
ров Web. Именно так реализуется главное преимущество аплетов Java — совме-
стимость с различными платформами, так как посетители этих страниц могут
запускать свои браузеры под управлением различных операционных систем.
Заметим, однако, что в большинстве случаев аплеты Java используются толь-
ко для достижения всевозможных визуальных эффектов, создания «интеллек-
туальных» графических ссылок, меню и т. д. Между тем, они способны решать
и более сложные задачи, такие, как непосредственное взаимодействие с расши-
рениями сервера Web в виде приложений CGI и ISAPI и передача параметров
страницам ASP. Мы рассмотрим именно эти вопросы, не нашедшие, на наш
взгляд, достойного отражения в многочисленных книгах, посвященных прило-
жениям Java.
Тем из Вас, кто еще никогда не создавал программ на языке Java, мы предла-
гаем наше руководство, размещенное на сервере создателя этого языка Sun
Microsystems по адресу http://www.sun.ru/java/books/online/index.html.

Система Layout Manager


В наших примерах мы покажем, как создавать аплеты с формами, позволяющи-
ми вводить информацию о кредитных карточках. Эти формы содержат такие
элементы управления, как текстовые поля и кнопки.
Начинающих программистов, особенно тех, кто создавал приложения для
Windows или OS/2, может шокировать способ, которым в приложениях Java
выполняется размещение компонентов и контейнеров внутри окна. Самая боль-
шая и неприятная па первый взгляд особенность заключается в невозможности
размещения компонентов с указанием точных координат (хотя с применением
специальной техники это все же достижимо).
Другая особенность — программы Java не имеют ресурсов, подобных ресур-
сам исполнимых файлов Windows и описывающих диалоговые панели или эле-
менты управления. Внешний вид пользовательского интерфейса определяется
динамически во время выполнения программы.
Поясним, в чем тут дело и для чего нужно преодолевать такие трудности.
133ак. 3571
360 Базы данных в Интернете. Практическое руководство

Создавая приложения 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

Установка режима GridLayout выполняется при помощи метода setLayout,


как это показано ниже:
setl_ayout(new Grid Layout ());
Далее компоненты добавляются в окно контейнера методом add, например:
TextField tf;
Button btnGetName;

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 Базы данных в Интернете. Практическое руководство

Всем указанным методам, кроме метода show, передается через единственный


параметр ссылка на родительский контейнер, в котором выполняется размеще-
ние. Методу show через второй параметр дополнительно передается имя компо-
нента (как строка класса String).

Режим 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

Далее Вам нужно задать значения полей объекта класса G r i d B a g C o n s t r a i n t s ,


например, так (позже мы расскажем о назначении отдельных полей):
с.anchor = GridBagConstraints.NORTH;
с,fill = GridBagConstraints. NONE;
c.gridheight = 1;
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridx = GridBagConstraints.RELATIVE;
c.gridy = GridBagConstraints.RELATIVE;
c.insets = new Insets(40, 0, 0, 0);
c.ipadx = 0;
c.ipady = 0;
c.weightx = 0.0;
c.weighty = 0.0;
Подготовив объект класса G r i d B a g C o n s t r a i n t s , Вам нужно установить его в
системе Layout Manager методом setConstraints и добавить очередной компо-
нент в окно контейнера методом add:
tf = new TextField(30);
gbl.setConstraints(tf, с);
add(tf);
Далее описанная процедура выполняется над всеми остальными добавляемы-
ми компонентами, причем объект класса GridBagConstraints можно не создавать
каждый раз заново, а использоватыювторно.
Но если все так просто, то в чем же тогда сложность работы с режимом раз-
мещения GridBagLayout?
Очевидно, дело в выборе значений параметров объекта класса G r i d B a g C o n s t -
raints.
Перечислим эти поля и дадим их краткую характеристику. Полную инфор-
мацию Вы найдете в документации JDK.
Поля gridx и gridy
Поля g r i d x и g r i d y задают соответственно номер столбца и номер строки для
ячейки, в которую будет помещен компонент. Левой верхней ячейке соответ-
ствуют нулевые значения.
В качестве значений для этих полей можно также указывать константу Grid-
B a g C o n s t r a i n t s . RELATIVE. Если эта константа указана в поле gridx, номер стол-
бца размещаемого компонента будет на единицу больше номера столбца для
компонента, размещенного ранее. Аналогично и для поля gridy.
Вы можете использовать значение G r i d B a g C o n s t r a i n t s . RELATIVE в данных
полях при последовательном размещении компонентов в ячейках таблицы в
направлении слева направо и сверху вниз.
Поля gridwidth и gridheight
Поля g r i d w i d t h и g r i d h e i g h t определяют количество ячеек, занимаемых добав-
ляемым компонентом.
Если компонент полностью помещается в одну ячейку, Вы вправе задать в
этих полях значение, равное единице. Если же компонент должен занимать, на-
пример, две смежные ячейки в одной строке, то для gridwidth нужно задать зна-
чение, равное двум, а для g r i d h e i g h t — значение, равное единице.
364 Базы данных е Интернете. Практическое руководство

Специальное значение G r i d B a g C o n s t r a i n t s . REMAINDER указывает, что компо-


нент должен занять все оставшееся место в текущей строке (для ноля g r i d w i d t h )
или в текущем столбце (для поля g r i d h e i g h t ) .
В поля g r i d w i d t h и g r i d h e i g h t можно также записать значение G r i d B a g -
Constraints. RELATIVE. В этом случае будет задано такое расположение компонен-
та, при котором он займет все оставшееся место в строке (для ноля gridwidth)
или столбце (для поля g r i d h e i g h t ) , оставив при этом одну свободную ячейку в
последнем столбце или строке.
Поле fill
Поле f i l l определяет стратегию распределения свободного пространства ячей-
ки (или ячеек) таблицы для компонента, если его размеры меньше размеров вы-
деленного для него места,
Возможные значения приведены в таблице 9-2.
Таблица 9-2. Значения поля f i l l
Значение Стратегия выделения места
G r i d B a g C o n s t r a i n t s . NONE Компонент не изменяет своих размеров
G r i d B a g C o n s t r a i n t s . BOTH Изменяется высота и ширина, причем таким образом,
чтобы компонент занимал все отведенное для него
пространство
G r i d B a g C o n s t r a i n t s . HORIZONTAL Компонент растягивается по горизонтали
G r i d B a g C o n s t r a i n t s . VERTICAL Компонент растягивается по вертикали

Поле 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 Вниз влево

Поля weightx и weighty


Эти поля определяют стратегию изменения размеров компонента, отвечая за
выделение пространства для столбцов (weightx) и строк (weighty).
Глава 9. Применение аплвтов Java 365

Если записать в них нулевые значения, все добавленные компоненты займут


место в центре контейнера и будут выровнены по центру (как по вертикали, так
и по горизонтали).
Чтобы размеры компонента изменялись по горизонтали или вертикали, в
поля weightx и w e i g h t x нужно записать значения от 0,0 до 1,0.
Если в столбце несколько компонентов, то его ширина определяется компо-
нентом с максимальным значением w e i g h t x . Аналогичное утверждение верно и
для строк.
Заметим, что дополнительное пространство добавляется к строкам и столб-
цам снизу и справа соответственно.
Поля ipadx и ipady
В полях ipadx и i p a d y Вы можете указать, что размеры компонента необходимо
увеличить на заданное количество пикселов по горизонтали и вертикали соот-
ветственно.
Поле insets
Поле insets позволяет задать для компонента отступы от краев выделенной ему
области. По умолчанию такие отступы отсутствуют.
В поле insets необходимо записать ссылку на объект класса Insets, создан-
ную соответствующим конструктором. Этот конструктор имеет следующий про-
тотип;
public Insets(
int top, // отступ сверху
int left, // -"- слева
int bottom, // -"- снизу
int right); // -"- справа

Аплет с формой
В качестве примера демонстрации режима размещения 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

"JP МИйе name Caicet |

_ jSidcirov i_asl пгвпе

Jl-111' ZIPcode

'| Russia Courifii

ЙЬвги ^МуСоДО

Рис. 9-1. Форма для регистрации посетителей


366 Базы данных в Интернете. Практическое руководство

В окне нашего аплета находится форма для регистрации посетителей серве-


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

name Ivan
le name; P
.asl name S idoiov
ZIPcodelinil
Couitrji Russia

.1

Рис. 9-2. Отображение введенной информации


Как в окне основного аплета, так и в окне диалоговой панели мы установи-
ли режим размещения компонент GridBagLayout.
Перейдем к рассмотрению исходного текста аплета. Полностью он приведен
в листинге 9-1.
Листинг 9-1 Вы найдете в файле ch09/Gridbag/GridBag.java на прилагаемом к
книге компакт-диске.

Главный класс аплета GridBag


Главный класс аплета GridBag создан на базе класса Applet и реализует интер-
фейс A c t i o n L i s t e n e r :
import Java. applet. Applet;
import java.awt. *;
import java.awt. event.*;

public class GridBag2 extends Applet


implements ActionListener

Данный интерфейс необходим для обработки событий, вызываемых щелчком


кнопок.
В главном классе мы определили несколько полей, предназначенных для
хранения ссылок на компоненты — текстовые поля, метки класса Label и кноп-
ки:
TextField tfFirstName;
Label IbFirstName;

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");

tfMiddleName = new TextField(20);


IbMiddleHame = new Label("Middle name");

tfLastName = new TextField(20);


IbLastName = new Label("Last name");

tfZip = new TextField(IO);


IbZip = new Label("ZIP code");

tfCountry = new TextField(S);


IbCountry = new Label("Country");

btnOK = new Button("OK">;


btnCancel = new Button("Cancel");
Далее мы устанавливаем режим размещения GridBagLayout и создаем объект
класса G r i d B a g C o n s t r a i n t s , необходимый для задания параметров размещения
отдельных компонентов:
GridBagLayout gbl = new GridBagLayoutO;
GridBagConstraints с =
new GridBagConstraintsO;
setLayout(gbl); |
Ниже мы расскажем о выборе этих параметров.
Поле First name
Заполнение параметров и размещение этого поля выполняется следующим об-
разом:
с.anchor = GridBagConstraints.NORTHWEST;
с.fill = GridBagConstraints.NONE;
c.gridheight = 1;
c.gridwidth = 1;
(см. след, стр.)
368 Базы данных в Интернете. Практическое руководство

c.gridx = GridBagConstraints.RELATIVE;
c.gridy = GridBagConstraints.RELATIVE;
c.insets = new Insets(10, 10, 0, 0);

gbl.setConst raints(tfFirstName, c);


add(tfFirstName);
В параметре a n c h o r мы указываем, что выравнивание поля следует выполнять
в направлении вверх влево, поэтому поле будет прижато к верхнему левому углу
контейнера.
Параметр f i l l имеет значение GridBagConstraints. NONE, а значит, при коррек-
тировке размеров контейнера размеры ноля изменяться не будут.
Так как значение полей g r i d h e i g h t и g r i d w i d t h равно единице, поле занима-
ет одну ячейку таблицы.
Поля gridx и gridy содержат значение GridBagConstraints. RELATIVE, поэто-
му добавление поля выполняется в направлении слева направо и сверху вниз.
И наконец, поле insets задает отступы сверху и слева, равные 10 пикселам.
Метка для поля First name
Для этой метки мы используем те же параметры, что и для самого поля:
gbl.setConstraints(lbFirstName, c);
add(lbFirstName);
В результате метка займет положение справа от поля First name.
Кнопка OK
Вот как заполняются параметры размещения для кнопки ОК:
с.gridwidth = GridBagConstraints.REMAINDER;
c.ipadx = 32;
gbl.setConstraints(btnOK, с);
add(btnOK);
Как видите, параметр 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. В результате кнопка будет последним компонентом в первой
строке. Ее размеры останутся неизменными при корректировке размеров кон-
тейнера, так как поле f i l l имеет значение G r i d B a g C o n s t r a i n t s . NONE.
Чтобы несколько увеличить размеры кнопки ОК по горизонтали, мы задали
в поле ipadx значение, равное 32 пикселам.
Поле Middle name и метка этого поля
Перед добавлением поля и его метки мы восстанавливаем параметры ipadx и
g r i d w i d t h , измененные на предыдущем этапе:
с.ipadx = 0;
с. gridwidth = 1;
gbl.setConstraints(tfMiddleName, с);
add(tfMiddleName);
gbl.setConstraints(lbMiddleName, с);
addUbMiddleName);
В результате поле Middle name будет размещено в первой ячейки второй
строки.
Глава 9. Применение аплетов Java 369

Кнопка 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);

Метка поля ZIP


Для этого компонента мы выделяем всю оставшуюся часть строки, задавая в
поле 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:
с.gridwidth = GridBagConstraints.REMAINDER;
gbl.setConst raints(lbZip, c);
add(lbZip);
370 . Базы данных в Интернеге. Практическое руководство

Поле Country
Это поле занимает одну ячейку последней строки, поэтому в поле g r i d w i d t h мы
записали значение I:
с.gridwidth = 1;
gbl.setConstraints(tfCountry, с);
add(tfCountry);

Метка поля Country


Для этой метки мы установили следующие параметры:
с.weighty = 1.0;
с.gridwidth = GridBagConstraints.REMAINDER;
gbl.setConstraints(lbCountry, c);
add(lbCountry);
Так как в поле w e i g h t y указано значение 1, для последней строки таблицы
отводится все оставшееся снизу пространство контейнера. Если же записать
сюда нулевое значение, все компоненты будут центрированы в окне контейне-
ра по вертикали.
Перед завершением работы метод init регистрирует обработчики событий от
кнопок:
btnOK.addActionListener(this);
btnCancel.addActionListener(this);

Метод 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 Базы данных в Интернете. Практическое руководство

Задавая в поле anchor значение G r i d B a g C o n s t r a i n t s . CENTER, мы добиваемся


центрирования редактора внутри выделенного ему пространства.
Так как поле f i l l имеет значение G r i d B a g C o n s t r a i n t s . BOTH, размеры окна
редактора изменяются таким образом, чтобы он занимал всю поверхность вы-
деленной ему ячейки таблицы.
Мы расположили окно редактора в одной ячейке (значение поля g r i d h e i g h t
равно единице), причем так, чтобы оно заняло всю первую строку (в поле g r i d -
width мы установили значение G r i d B a g C o n s t r a i n t s . REMAINDER).
Что же касается кнопки, то ее размеры останутся постоянными при коррек-
тировке размеров контейнера:
с. fill = GridBagConstraints. NONE;
с. ipadx = 35;
gbl.setConstraintsfbtnQK, с);
add(btnOK);
Заметим, что мы не ввели значения в полях w e i g h t x и weighty. В результате
при изменении размеров окна диалоговой панели и редактор, и кнопка остают-
ся в его центре.
Перед завершением своей работы конструктор регистрирует обработчик со-
бытий для кнопки btnOK:
btnOK.addAction Listener (this);

Метод actionPerformed
Этот метод скрывает окно диалоговой панели, когда пользователь щелкает кноп-
ку ОК:
public void actionPerformed(ActionEvent e)
!
if (e. getSourceO . equals(btnOK))
!
setVisible(false);

Классы Java для работы в сети


Язык программирования Java отличается богатой и продуманной библиотекой
классов, предназначенной для решения самых разных задач — от создания ар-
хивов ZIP и работы с растровыми графическими изображениями до задач орга-
низации взаимодействия приложений Java через сеть.
В этом разделе кратко описаны основные классы сетевой библиотеки Java,
которые потребуются нам для связи аплетов с расширениями сервера Web,

Класс InetAddress
Для работы с адресами IP в библиотеке классов Java предназначен класс Inet-
A d d r e s s . С его помощью приложение определяет адрес IP локального узла, а
также адреса удаленного узла, заданного своим доменным именем.
Вот примеры наиболее интересных методов этого класса:
Глава 9. Применение аплетов Java 373

public static InetAddress getLocalHostC);


public static InetAddress getByName(String host);
public static InetAddress[] getAHByName(String host);
public byte[] getAddressf);
public String toStringO;
public String getHostName();
public boolean equalsfObject obj);
Заметим, что создание объекта класса InetAddress выполняется не с помощью
оператора new, а с применением статических методов getLocalHost, getByName и
getAHByName.
Метод getLocalHost создаст объект класса I n e t A d d r e s s для локального узла,
то есть для той рабочей станции, на которой выполняется приложение Java:
InetAddress iaLocal;
iaLocal = InetAddress.getLocalHost();
Б том случае, если Вас интересует удаленный узел сети, Вы можете создать
для него объект класса I n e t A d d r e s s , используя методы g e t B y N a m e или g e t A l l -
ByName. Первый возвращает адрес узла, а второй — массив всех адресов IP, свя-
занных с данным узлом.
Если узел с указанным именем не существует, при выполнении методов
getByName и getAHByName возникает исключение UnknownHostException.
Методам getByName и getAHByName допустимо передавать не только имя узла,
например www.sun.com, но и строку адреса IP в виде четырех десятичных чисел,
разделенных точками.
Кратко рассмотрим другие методы класса I n e t A d d r e s s .
Метод getAddress возвращает массив из четырех байт IP-адреса объекта. Байт
с нулевым индексом этого массива содержит старший байт адреса IP.
Метод toString возвращает текстовую строку, которая содержит имя узла, раз-
делитель «/» и адрес IP в виде четырех десятичных чисел, разделенных точками.
Средствами метода getHostName Вы можете определить имя узла, для кото-
рого был создан объект класса InetAddress.
И наконец, метод equals предназначен для сравнения адресов IP как объек-
тов класса InetAddress.

Класс URL
Для работы с ресурсами, заданными адресами URL, в библиотеке классов Java
имеется очень у д о б н ы й и мощный класс с названием U R L .
С помощью определенных в нем конструкторов и методов нетрудно извлечь
и проверить отдельные компоненты адреса; протокол, адрес узла, номер порта,
имя файла. Вы также можете открыть поток, связанный с ресурсом и прочитать
сто для отображения, обработки или для копирования в другой поток.
Расскажем кратко о классе U R L
Конструкторы класса
В этом классе предусмотрено четыре конструктора.
Первый из них создает объект U R L для сетевого ресурса, адрес URL которого
передается в виде текстовой строки через единственный параметр spec:
public URLCString spec);
374 Базы данных в Интернете. Практическое руководство

В процессе создания объекта проверяется заданный адрес URL. Если адрес


указан неверно, возникает исключение M a l f o r m e d U R L E x c e p t i o n . Это же происхо-
дит при попытке использовать протокол, с которым данная система не может
работать.
Второй вариант конструктора класса U R L допускает раздельное указание про-
токола, адреса узла, номера порта, а также имя файла:
public URL(String protocol, String host, int port, String file);
Третий вариант предполагает использование номера порта, принятого по
умолчанию:
public URL(String protocol, String host, String file);
Для протокола HTTP, например, это порт с номером 80.
И наконец, четвертый вариант конструктора допускает указание контекста
адреса URL и строки адреса URI.:
public URL(URL context, String spec);
Строка контекста позволяет указывать компоненты адреса URL, отсутству-
ющие в строке spec, такие, как протокол, имя узла, файла или номер порта.
Методы класса URL
Кратко рассмотрим самые интересные методы, определенные в классе U R L .
Метод openStream позволяет создать входной поток для чтения файла ресурса,
связанного с созданным объектом класса URL:
public final InputStream openStream();
Для выполнения операции чтения из созданного таким образом потока Вы
можете использовать метод read, определенный в классе I n p u t S t r e a m .
Метод getHost позволит Вам определить имя узла, соответствующей) данно-
му объекту URL:
public String getHost();
Метод getFile позволяет получить имя файла, связанного с данным объек-
том URL:
public String getFile();
Метод getPort предназначен для определения номера порта, на котором вы-
полняется связь для объекта URL:
public int getPort();
Методом getProtocol Вы можете определить протокол, использование кото-
рого приводит к установлению соединения с ресурсом, заданным объектом URL:
public String getProtocol();
Метод getRef возвращает текстовую строку ссылки на ресурс, соответству-
ющий данному объекту URL:
public String getRef();
Метод hashCode возвращает хэш-код объекта U R L :
public int hashCodef);
Вы можете использовать метод equals для определения идентичности адре-
сов URL, з а д а н н ы х двумя объектами класса URL:
Глава 9. Применение аплетов Java 375

public boolean equals(Qbject obj);


Если адреса URL идентичны, метод e q u a l s возвращает значение true, если
нет — значение false.
Метод toExternalForm возвращает текстовую строку внешнего представления
адреса URL, определенного данным объектом класса U R L :
public String toExternalFormO;
Метод t o S t r i n g возвращает текстовую строку, представляющую данный
объект класса URL:
public String toStringO;

Класс 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 .
средствами которых Вы сможете создать соответственно потоки вывода и вво-
да, привязанные к каналу.

Взаимодействие приложений Java и расширений


сервера Web
Библиотеки классов Java позволяют организовать взаимодействие между при-
ложением Java и такими расширениями сервера Web, как CGI или ISAPI. В этом
случае приложения или аплеты Java смогут посылать произвольные данные
расширению сервера Web для обработки, а затем получать результат этой обра-
ботки.
Методика организации взаимодействия приложений Java и расширений сер-
вера Web основана па применении классов URL и URLConnection.
Приложение Java, желающее работать с расширением сервера Web, создает
объект класса U R L для программы расширения (то есть для исполняемого моду-
ля расширения CGI или библиотеки динамической компоновки DLL расшире-
ния ISAPI).
Далее приложение получает ссылку на канал передачи данных с этим расши-
рением, представленную в качестве объекта класса URLConnection. Затем, пользу-
ясь методами getOutputStream и g e t l n p u t S t r e a m из класса U R L C o n n e c t i o n , прило-
жение создает с расширением сервера Web выходной и входной капал переда-
чи данных.
Когда приложение отправляет данные в выходной канал, созданный подоб-
ным образом, они попадают в стандартный поток ввода приложения CGI или
ISAPI. Все выглядит так, как будто бы данные отправлены методом POST из
формы, определенной в документе HTML.
Обработав полученные данные, расширение сервера Web записывает их в
свой стандартный выходной поток. После этого они становятся доступны при-
ложению Java через входной поток, открытый методом g e t l n p u t S t r e a m класса
URLConnection.
376 Базы данных в Интернете. Практическое руководство

Аплет для передачи номера кредитной карточки


Некоторые процессинговые компании предлагают своим клиентам систему бе-
зопасного ввода информации о кредитных карточках, реализованную на базе
аплетов Java. Их работа основана на шифровании на стороне клиента сведений
о карточке, введенных покупателем. При этом аплет шифрует информацию пе-
ред передачей ее через сеть, а специальное приложение, работающее на сервере
процессинговой компании, получает се и расшифровывает.
Заметим, что подобная задача также решается при помощи клиентского эле-
мента управления ActiveX, работающего на компьютере покупателя, или при
помощи специальной программы, Однако первый способ, основанный на при-
менении ActiveX, не пользуется особой популярностью из-за потенциальной
опасности клиентских элементов управления ActiveX, Второй, предполагающий
загрузку специальной программы, неудобен для покупателя.
Еще один способ безопасной передачи номера кредитной карточки связан с
использованием протокола Secure HTTP. При этом информация шифруется
средствами браузера, а расшифровывается на стороне сервера Web. Но и этот
способ не без недостатков. Во-первых, длина ключа, равная для интернациональ-
ных версий браузера Microsoft Internet Explorer, составляет 40 бит, чего не все-
гда достаточно. Во-вторых, прежде чем применять указанный протокол, необхо-
димо приобрести специальный сертификат, за использование которого в даль-
нейшем придется вносить ежемесячную или ежегодную плату.
Аплеты Java полностью обезопасят Вашу информацию. Их можно применять
для защищенной передачи данных независимо от наличия соответствующих
браузера, кроме того, Вам не придется приобретать какие-либо сертификаты.
Однако, если Вы решили создать собственное средство защищенной переда-
чи критичной информации через Интернет, то мы настоятельно рекомендуем
выполнять шифрование с использованием каких-либо известных и хорошо про-
веренных методов. В Интернете немало доступных исходных текстов криптог-
рафических программ, созданных на основе серьезных математических исследо-
ваний. Что же касается самодельных криптографических систем, то, скорее все-
го, они не смогут противостоять серьезным попыткам дешифрования.
- Miciosoll Inlemel Exploie
Fie §cft Ifiew

_Л-

Your name OR

Credl sad numfaa Cancel

Expiation date

Done J=J My C

Рис. 9-3. Ввод номера кредитной карточки в окне аплета


Глава 9. Применение аплетов Java 377

Мы не будем описывать криптографические алгоритмы и программы, так как


это выходит за рамки рассматриваемого в книге материала. Мы приведем исход-
ные тексты аплета CreditCard, передающего информацию о кредитной карточ-
ке расширению сервера Web в открытом виде. При необходимости Вы сами до-
бавите к аплету криптографические модули.
Внешний вид аплета CreditCard показан на рис. 9-3.
После ввода информации о кредитной карточке посетитель должен щелкнуть
кнопку ОК. При этом данные из полей формы ввода будут переданы расшире-
нию сервера Web, созданному нами в виде приложения ISAPI. Это приложение
извлечет данные формы и отправит их обратно аплету. Аплет отобразит строку,
принятую расширением ISAPI, в диалоговой панели, показанной на рис. 9-4.

Name=lvsn%2QPetrQvbcc"230D34-E73I.expii-1 2/1 2/

Ц 1 >1
ок 1
Waiting: Applet Window

Рис. 9-4. Информация о кредитной карточке, полученная от расширения ISAPI


Реальная система передачи номера кредитной карточки обязательно шифрует
информацию,перед передачей се на сервер. При этом аплет, выполняющий шиф-
рование, не должен содержать в себе никаких ключей. Дело в том, что злоумыш-
ленники могут вскрыть байт-код аплета с целью извлечения секретного ключа.
Поэтому лучше, если для получения ключа аплет, например, установит прямую
связь с сервером процессинговой компании с применением сокетов и получит
ключ через этот канал.
Расширение сервера Web в реальной системе должно расшифровать получен-
ную информацию и выполнить платежную операцию через сервер банка.
Для разработки программных модулей, выполняющих все описанные выше
операции, необходимо привлекать специалистов процессинговой компании или
банка (если Вы создаете сервер процессинговой компании). Поэтому мы не ста-
нем описывать этот процесс более подробно, а займемся исходными текстами
аплета, выполняющего обмен данными с расширением сервера Web. Полный
исходный текст аплета Вы найдете в листинге 9-2.
Листинг 9-2 хранится в файле ch09/CreditCard/CreditCard.java на прилагаемом
к книге компакт-диске.

Инициализация аплета
При инициализации аплета метод in it вначале создает все необходимые компо-
ненты и устанавливает режим размещения GridBagLayout:
tfName = new TextField(20);
IbName = new Label("Your name");

tfNumber = new TextField(20);


IbNumber = new LabelC'Credit card number"};
(см. след, стр.)
378 Базы данных а Интернете. Практическое руководство

tfExpiration = new TextField(20);


IbExpiration = new Label("Expiration date");

btnOK = new ButtonC'OK");


btnCancel = new Button("Cancel");

GridBagLayout gbl = new GridBagLayoutO;


GridBagConstraints с = new GridBagConstraintsO;

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;

При этом создается новый объект класса U R L , представляющий собой адрес


URL запускаемого расширения ISAPI. Все операции выполняются в блоке try-
catch, так как при работе приложения могут возникать ошибки, связанные, на-
пример, с разрывом связи между браузером покупателя и сервером.
Далее мы создаем канал связи с расширением ISAPI как объект класса URLCon-
nection:
URLConnection с;
с = u.openConnectionQ;
с.setDoOutput(true);
c.setDoInput(true);
С помощью методов setDoOutput и setDoInput для канала разрешается выпол-
нение операций вывода и ввода соответственно.
Глава 9. Применение аплетов Java 379

Чтобы принять данные от расширения ISAPI, мы создаем входной поток


данных класса DatalnputStream, основанный на потоке InputStream. Этот поток,
в свою очередь, получен при помощи метода g e t l n p u t S t r e a m :
DatalnputStream is;
is = new DataInputStrearo(c.getInputStream(});
Чтение из потока выполняется в цикле:
String str="";
String str1="";
while(true)
{
strl = is.readLineC);
IffstM == null)
break;
str += strl;
>
Здесь в строке str накапливаются полученные данные.
По достижении конца потока мы его закрываем:
is.close();
После получения всех строк от расширения сервера Web мы отображаем со-
держимое строки str в диалоговой панели:
amsgbox = new AppletMsgBoxCstr, "Information");
amsgbox. showQ;

Размещение аплета в документе HTML


Аплет, выполняющий обмен д а н н ы м и с сервера Web, надо загружать именно с
этого сервера, а не с какого-либо другого. Поэтому в документе HTML с апле-
том правильно указывайте параметры тега <APPLET>:
<applet name="CreditCard" code="CreditCard"
codebase="http://www.YourServer.ru/AppletOir"
width="400" height="150" align="Top">
</applet>
Параметр CODE должен задавать имя аплета, а параметр CODEBASE — адрес URL
каталога, в котором будет расположен аплет.
Полный исходный текст документа HTML, подготовленный нами для апле-
та Credit Card, Вы найдете в листинге 9-3.
Листинг 9-3 хранится в файле ch09/CreditCard/CreditCard.html на прилагаемом
к книге компакт-диске.

Исходный текст расширения ISAPI


В листинге 9-4 находится полный исходный текст расширения ISAPI, подготов-
ленного нами для совместной работы с аплетом CreditCard.
Листинг 9-4 Вы найдете в файле ch09/lsCard/lsCard.c на прилагаемом к книге
компакт-диске.
Все основные действия выполняются расширением в функции H t t p E x t e n -
sionProc.
380 Базы данных в Интернете. Практическое руководство

Получив управление, эта функция записывает в буфер szBuf f заголовок фор-


мируемого документа:
wsprintf(szBuff, "Content-Type: text/plain\r\n\r\n");
Обратите внимание: в заголовке мы указали, что документ представляет со-
бой текст без форматирования, а не страницу HTML. Передаваемый текст из
буфера будет прочитан анлетом CreditCard из входного потока.
Далее функция H t t p E x t e n s i o n P r o c получаст строку параметров, переданную
аплетом при загрузке расширения, и добавляет ее в буфер:
wsprintf(szTempBuf, "Sis", lpECB->lpszQueryString);
strcat(szBuff, szTempBuf);
Таким образом, наше расширение ISAPI отправляет обратно аплету получен-
ные от него параметры.
Содержимое буфера посылается аплету следующим образом:
if(!lpECB->ServerSupportFunction(lpECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff))
<
return HSE_STATUS_ERROR;

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

Это приложение содержит краткую справочную информацию по языку сцена-


риев JavaScript. Она пригодится Вам при составлении как клиентских, так и
серверных сценариев.

Переменные
В сценариях 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

Присвоение значения переменным


Вы можете присвоить значение переменной при помощи оператора присвоения
«=». Например, ниже мы объявляем переменную и затем записываем в пес-тек-
стовую строку:
var szHelloMsg;
szHelloMsg = "Hello, world!";
В любом месте программы мы можем присвоить переменной szHelloMsg чис-
ленное значение, например, так:
szHelloMsg = 4;
После выполнения такой операции тип переменной изменится, причем в
процессе интерпретации сценария браузер не отобразит никаких предупрежда-
ющих сообщений.
Вы вправе присвоить переменной специальное значение n u l l :
szHelloMsg = n u l l ;
Такое присвоение не назначает переменной никакого типа. Оно применяет-
ся в тех случаях, когда Вам нужно объявить переменную и проипициализиро-
вать ее, не присваивая этой переменной никакого начального значения и типа.
В частности, в только что приведенном примере переменной s z H e l l o M s g не при-
сваивается пи численное значение 0, ни пустая текстовая строка.
Типы данных
Хотя при создании переменной Вы не можете присвоить ей тип, в языке Java-
Script все же существует несколько типов данных. Это числа, текстовые стро-
ки, логические данные, объекты, данные неопределенного типа, а также специ-
альный тип n u l l .
Числа
Язык сценариев JavaScript допускает использование чисел в различных форма-
тах: целые числа, числа в формате с плавающей десятичной точкой и числа в
научной нотации. Целые числа могут быть представлены по основанию 8, 10
или 16.
В некоторых случаях арифметические функции возвращают так называемое
«нечисло», которое в JavaScript называется NaN (Not a Number). «Нсчисло» -
это специальное значение, которое не соответствует никакому числу. Оно гене-
рируется в тех случаях, когда результат выполнения операции над числами не
может быть представлен в виде числа. Средства функции isNaN позволят Вам
проверить, является ли значение «нечислом».
Текстовые строки
Текстовые строки — это последовательность символов Unicode, заключенных в
одинарные или двойные кавычки, например:
"Hello, world!"

"12345"
'Привет!'
384 Базы данных в Интернет!!. Практическое руководство

Строка "" — пустая. Заметим, что следующие два присвоения не эквивалентны:


szStr=""
szStr1=null
В первом случае в переменной szStr хранится текстовая строка (хотя бы и
пустая), но втором — совсем ничего.
Логические данные
Логические данные имеют только два значения: t r u e и false. Эти значения ни-
как не соотносятся с числами 1 и 0. Они предназначены главным образом для
выполнения операций сравнения, а также для использования в условных опе-
раторах.
Данные неопределенного типа
Если переменная была объявлена, но ей еще ни разу не присваивалось значение,
она имеет неопределенный тип. Например, в следующей строке сценария объяв-
лена переменная M y V a r i a b l e , которая имеет неопределенный тип:
var HyVariable;
Если же этой переменной присвоить значение null, то тип переменной изме-
нится — теперь она будет содержать значение n u l l :
MyVariable = null;

Преобразование типов данных


Когда в выражениях встречаются переменные разных типов, интерпретатор
JavaScript автоматически преобразует численные данные в текстовые строки,
Обратное же преобразование (строк в числа) приходится выполнять с помощью
специальных функций — parselnt и parseFloat.
Рассмотрим следующий сценарий:
var szTextBuf = "";
szTextBuf = 4 + "&nbsp;- число четыре " + "<BR>";
szBuf2 = (parselnt("2") + 2) + "&nbsp;- чиспо четыре" + "<BR>";
document.write(szTextBuf + szBuf2);
Здесь мы объявили переменную szTextBuf и проинициализировали ее пустой
строкой. Далее мы присвоили этой строке сумму числа 4 и двух текстовых строк:
szTextBuf = 4 + "&nbsp;- число четыре" + "<BR>";
При вычислении этого выражения значение 4 автоматически преобразуется
в текстовую строку. Дальнейшее суммирование выполняется как слияние (кон-
катенация) трех текстовых строк.
Обратите внимание на символ нсразделяющсго пробела &nbsp;, который ис-
пользуется в документах HTML. Если заменить его обычным пробелом, то пос-
ле конкатенации строк этот пробел исчезнет.
В следующей строке мы преобразуем текстовую строку «2» в численное зна-
чение с помощью функции p a r s e l n t , прибавляем к результату преобразования
число 2, а затем выполняем конкатенацию с двумя текстовыми строками:
szBuf2 = (parse!nt("2")+2)+"&nbsp;- число четыре "+ "<BR>";
В результате переменная s z B u f 2 будет содержать ту же самую строку, что и
переменная SzTextBuf.
Приложение 1. Элементы языка JavaScript 385

Операторы
Операторы языка сценариев JavaScript напоминают общеизвестные операторы
языка С.

Унарные операторы
Они применяются для изменения знака, выполнения операции дополнения,
инкремента и декремента (таблица П-2).
Таблица П-2. Унарные операторы
Унарный оператор Назначение
Изменение знака па противоположный
: Дополнение. Используется для реверсирования значения логи-
ческих переменных
++ Увеличение значения переменной. Также применяется как пре-
фикс переменной или как ее суффикс
Уменьшение значения переменной. Также применяется как пре-
фикс переменной или как ее суффикс

Бинарные операторы
Бинарные операторы соединяют два операнда. В языке сценариев JavaScript
предусмотрены бинарные операторы для вычитания, сложения, у м н о ж е н и я ,
деления и вычисления остатка деления (таблица П-3).
Таблица П-3. Бинарные операторы
Бинарный оператор Назначение
Вычитание
+• Сложение
* Умножение
/ Деление
X Вычисление остатка от деления

Операторы для выполнения логических операций


В своих сценариях Вы можете применять операторы, выполняющие над бита-
ми переменных такие логические операции, как «И», «ИЛИ», «ИСКЛЮЧАЮ-
ЩЕЕ ИЛИ», «НЕ» (таблица ГТ-4). Данные операторы перед выполнением сво-
их функций преобразуют значения переменных в 32-разрядные целые числа.
Таблица П-4. Операторы для в ы п о л н е н и я логических операций
Оператор Логическая операция
& И
ИЛИ
ИСКЛЮЧАЮЩЕЕ ИЛИ
НЕ
Базы данных в Интернете. Практическое руководство

Операторы сдвига
Для выполнения операций сдвига в языке 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

Таблица П-8. Комбинации оператора присваивания и других операторов


Оператор Описание
Простое присваивание
+= Увеличение численного значения или слияние строк
Уменьшение численного значения
*= Умножение
/= Деление
(= Вычисление остатка от деления
»= Сдвиг вправо
>»= Сдвиг вправо с заполнением освобождаемых разрядов нулями
«= Сдвиг влево
|= ИЛИ
&= И
-= ИСКЛЮЧАЮЩИЕ ИЛИ

Условные операторы
Любой язык программирования бесполезен, если в нем не предусмотрены тс или
иные средства ветвления при выполнении программы. В языке 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:
ипПе(условие)

строки тела цикла

Если в результате оценки условия получается значение true, тогда итерация


выполняется, если false — цикл прерывается.
Оператор break
С помощью оператора b r e a k можно прервать выполнение цикла, созданного
операторами f o r или while, в любом месте. Например:
var i = 0;
while(true)
Приложение 1. Элементы языка JavaScript

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)

if(i < 10)


continue;

Здесь фрагмент тела цикла, отмеченный многоточием, будет выполняться


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


В таблице старшинства операторов JavaScript показан порядок вычисления со-
ставных операторов. Не забудьте, что все логические операции выполняются
слева направо. Первыми вычисляются операторы, расположенные в начале таб-
лицы старшинства.
Таблица П-10. Таблица старшинства операторов JavaScript
Оператор Описание
. [] С) Доступ к полю, индексирование и массиве, вызои функции
Унарные операторы
Умножение, деление, вычисление остатка от деления
Сложение, вычитание, слияние текстовых строк
Битовые сдвиги
< < = > > = Меньше, меньше или равно, больше, больше или равно
Равенство, неравенство
Логическая операция «И»
Логическая операция «ИСКЛЮЧАЮЩЕЕ ИЛИ*
Логическая операция «ИЛИ»
Логический оператор отношения «И»
Логический оператор отношения «ИЛИ»
Условный оператор
= += -= *= /= %= »: Присваивание
>»= «= |= Я= "=

Многократное вычисление

Функции
Вы можете оформить фрагменты исходного текста в виде функции, вызывая эту
функцию по мере необходимости из различных мест сценария JavaScript.
Для клиентских сценариев функции обычно определяются в разделе заголов-
ка документа HTML, отмеченного тегами <HEAD> и </HEAD>. Как мы уже говори-
ли, функцию надо определить перед вызовом. Размещение всех определений
функций в разделе заголовка документа HTML гарантирует доступность этих
функций при обработке документа. В серверных сценариях ASP функции необ-
ходимо определить до их использования.
Общий вид определения функции показан ниже:
function имя([параметр 1] [.параметр 2] [ . . . . п а р а м е т р N])
Приложение 1. Элементы языка JavaScript 391

строки тела функции

[return значение]

Все параметры передаются функции по значению. Поэтому функция не мо-


жет изменить содержимое переменных, передаваемых ей в.качестве параметров.
Ключевое слово r e t u r n позволяет функции вернуть значение.

Встроенные объекты
В таблице 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 редко применяют для математических вычислений, в
нем все же есть встроенный класс Hath, предназначенный как раз для этого.

Свойства
Перечислим свойства класса 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);

Встроенный класс Date


С помощью методов встроенного класса Date сценарий JavaScript выполняет
различные действия с часами компьютера, например получает и устанавливает
Приложение 1. Элементы языка JavaScript 397

текущую дату и время. Ниже мы кратко опишем конструкторы и методы этого


класса.
Конструкторы класса Date
Для использования большинства методов класса Date необходимо создать объект
этого класса при помощи одного из трех конструкторов.
Конструктор первого вида вызывается следующим образом:
var dtNewDate;
dtNewDate = new Date<);
Здесь создается объект Date, в котором хранится информация о текущей дате
и времени. Это время задается по Гринвичу, или, пользуясь современным опре-
делением, с использованием времени Universal Coordinated Time (UCT).
Конструктор второго вида позволяет указать дату через единственный пара-
метр:
var dtNewDate;
dtNewDate = new Date(nMilliseconds);
Параметр nMilliseconds задает дату в миллисекундах, считая от 1 января
1970 года.
И наконец, конструктор третьего вида предназначен для раздельной установ-
ки компонентов даты и во многих случаев более удобен для использования:
var dtNewDate;
dtNewDate=new Datefyear, month, date, hours, min, sec, ms);
Параметры этого конструктора перечислены в таблице П-12.
Таблица П-12. Параметры конструктора Date
Параметр Описание
year Год, например 1998 и;ш 2012
month Номер месяца от 0 (январь) до 11 (декабрь)
date Календарная дата в диапазоне от 1 до 31
hours Необязательный параметр, задающий час дня в диапазоне от 0 до 23. Не-
обходимо указывать УТОТ параметр только в том случае, если задан пара-
метр rain
min Необязательный параметр, задающий минуты в диапазоне от 0 до 59. Не-
обходимо указывать этот параметр только в том случае, если задан пара-
метр sec
sec Необязательный параметр, задающий секунды в диапазоне от 0 до 59.
Необходимо указывать этот параметр только в том случае, если задан
параметр ms
ms Необязательный параметр, задающий миллисекунды в диапазоне
^ от 0 до 999

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 Базы данных в Интернете. Практическое руководство

Параметр nMinutes может принимать любые положительные или отрицатель-


ные значения. При необходимости происходит соответствующее изменение ка-
лендарной даты, записанной в объекте класса Date.

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

Встроенные объекты ASP

В этом разделе кратко описаны свойства и методы встроенных объектов, доступ-


ных серверным сценариям ASP: Server, R e q u e s t , Response, Application и Session.

Объект 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, заменяя символы пунктуации специальными символьными объектами
типа «&lt;».
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

Таблица П-13. Переменные среды (продолжение)


Параметр Описание
QUERY.STRING Строка, переданная в запросе HTTP после знака «?»
REMOTE_ADDR Адрес IP узла посетителя сервера. Может быть использован
для определения, с какого адреса IP посетитель выполнил
регистрацию
REMOTE_HOST Имя узла посетителя (то есть имя его компьютера)
REMOTE_USER Имя пользователя
REQUEST_METHOD Метод, использованный для выдачи запроса (GET, HEAD, POST
и т. д.)
SCRIPT_NAME Виртуальный путь к странице со сценарием
SERVER^NAME Имя сервера
SERVER_PORT Номер порта, на котором сделан запрос
SERVER_PORT_SECURE Если эта строка содержит 1, запрос был создан с примене-
нием безопасного порта. В противном случае строка содер-
жит значение О
SERVER_PROTOCOL Имя и версия информационного протокола, с применением
которого создан запрос
SERVER_SOFTWARE Имя и версия программного обеспечения сервера

Свойства
Для данного объекта определено только одно свойство TotalBytes. Оно содер-
жит количество байт данных, полученных в результате выполнения запроса.

Методы
Для объекта Request определен метод BinaryRead, позволяющий извлечь данные,
отправленные серверу как часть запроса POST.

Объект Response
Объект R e s p o n s e используется для управления информацией, отправляемой
клиентским сценарием в браузер посетителя. Он позволяет передавать данные
непосредственно, выполнять переадресацию браузера на другой адрес URL, a
также устанавливать значения Cookie.

Наборы
В объекте Response предусмотрен набор Cookies, позволяющий устанавливать
значения Cookie. Об использовании этого набора мы рассказали в третьей гла-
ве нашей книги.

Свойства
Рассмотрим свойства объекта Response.
Buffer
Признак буферизации вывода страницы. Если вывод буферизован, в этом свой-
стве хранится значение true, в противном случае — false.
406 Базы данных в Интернете. Практическое руководство

При использовании буферизации сервер не отправляет ответ клиенту до тех


пор, пока не завершится обработка серверных сценариев, расположенных на
текущей странице, или пока не будут вызваны методы Flush или End, определен-
ные в рамках объекта Response.
CacheControl
Если это свойство имеет значение P u b l i c , страницы сервера будут кэшировать-
ся внешними прокси-серверами. В том случае, если свойство содержит строку
Private, внешнее кэширование отменяется.
Charset
Имя набора символов, которое нужно указать в заголовке определения содер-
жания (content-type).
ContentType
Тип содержимого, такой, как «text/HTML», «image/GIF» или «text/plain*.
Expires
Период времени в минутах, по прошествии которого содержимое документа
будет считаться устаревшим.
ExpiresAbsolute
Абсолютная дата, по достижении которой содержимого документа будет считать-
ся устаревшим.
IsClientConnected
Это свойство позволяет проверить, не отключился ли посетитель от сервера.
Если это так, возможно, сценарию следует прекратить свою работу.
Status
Строка состояния, возвращаемая сервером. Например, следующий фрагмент
кода устанавливает строку диагностического сообщения:
Response.Status = "401 Access denied - password do not match"

Методы
Ниже перечислены методы объекта 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.
Первое из них возникает в начале работы сеанса, а второе — перед ее завер-
шением.
Предметный указатель

^import 208 adSmalllnt 126


©LANGUAGE 88 adTinylnt 126
uuidof 211 adt'nsignedBiglnt 126
_bstr_t 211 adUnsignedlnt 126
_com_error 208, 214 adUnsignedSmalllnt 126
_com_ptr_t 208 adUnsignedTinylnt 126
adUserDefined 126
A adVarBinary 126
abs 394 adVarChar 126,205
acos 394 yd Variant 126
ActiveConnection 123 adVarWChar 126
ActiveX 9, 332 ad Vector 126
ActiveX Data Objects 120 adWChar 126
ActiveX Template Library 258 aiert 40,41
ad Array 125 ALL_11TTP 404
adBiglnt 125 ALL_RAW 404
adBinary 125 Anchor 22
adBoolean 125 anchor 22, 53
HdBSTR 125 Applet 22
adByRef 125 Application_OnEnd 96
adChar 125 Application_On Start 96
adCmdStoredProc 124 Area 22
adCmdTable 124 Array 391
adCmdText 124 as in 394
adCmdUnknown 124 ASP 4, 86
adCurrency 125,205 Assessor 250
ad Date 125 atan 394
adDBDate 126 ATL 258,333
adDBTime 126 Attributes 133
adDBTimeStamp 126,205
adDecimal 126 В
adDoublc 126 BEGIN_COLUMN>1AP 261
AddRcf 208 binary 246
adEmpty 126 BOOKMARK 272
adError 126 Boolean 391
aclGUID 126 Border Layout 361
adIDispatch 126 BSTR 208
ad Integer 126,205
adIUnknown 126
ad LongVar Binary 126 CardLayout 361
adLongVarChar 126 catch 130
adLongVarWChar 126 CCommand 260
adNumerie 126 CDataSource 259
ADO 5,13,120 ceil 394
adojavas.inc 133, 134 CGT 2,208,289
adovbs.inc 134 char 246
ad Paramlnpu t 127 Close 123
ad Paramlnput Output 127 dosesocket 358
adParamOutput 127 CLSCTX_INPROC_SERVER 235
adParamRcturn Value 127 CLSID_MSDASQL 235
adSingle 126 CoCreatelnstancc 208, 222, 234, 240
Базы данных в Интернете. Практическое руководство

Colnitialize 210,222,232 DHTML 30


COleCurrcncy 221 document 22
COleDateTimc 221 double 125
collection 122 DSN 121
COM 12
Command 121-123,241
CommandText 123 elements 22
CommandTi m eout 123 ENCTYPE 293
CommandType J 24 EOF 210
confirm 40,42 Error 122.214
connect 123 Errors 122,130
Connection 121, 122,210 escape 38,194,401
CONTENT_LE\GTH 293,404 eval 56, 401
CONTENTJTYPE 404 event 22
Cookie 7,69,97, 107 Execute 127,233,242
Cookies 107 cxp 395
cos 395 EXTENSION CONTROL BLOCK 315
CoUninilialize 210,222
CreateAcccssor 233,243 Field 127
CrcateCommand 24.1 Fields 127
Createl n stance 211
float 246
CreateSession 232,211 floor 395
CreateThread 330 FlowLayout 360
CSession 259
Form 22
CString 221 forms 22
D Frame 22
DAO 266 frames 22
Data Source Name 121 Function 391
Date 391,396
datetime 246,247 GetColumnlnfo 243
DB Library 11, 120
GetData 233, 243, 252
DB_NUMER1C 246 gctdate 139
DBBINDJNG 247 gctDale 32,397
DBCOLUMNINFO 244,248 get Day 398
DBGU1D_DEFAULT 242 getenv 292
DBGUID_SQL 242
GetExtensionVersion 311, 312
DBPROP 235
getFullYear 32
DBPROPSET 238 gctHours 398
DBTIMESTAMP 247 getTnput Stream 375
DBTYPE_BYTES 246 gctMinutcs 398
DBTYPE_CY 247 getMonth 32,398
DBTYPE_DATE 246
GetNextRows 233,254
DBTYPE_DBTIMESTAMP 247 getOutputStream 375
DBTYPE_I2 246
getSeconds 398
DBTYPEJ4 246
GetS erver Variable 315
DBTYPE_NUMERIC 246 getTimc 398
DBTYPE_R4 246 getTJmeZoncOl't'set 398
DBTYPE_R8 246
getYear 399
DBTYPE_STR 246 GIF 60
DBTYPE_UDT 247 Global 391
DBTYPJ£_UI1 246 globai.asa 94, 134. 152,208
debugger 118
GridBagConstraints 362
decimal 246
Grid BagLayou t 362
default.asp 112
GridLavout 360
Description 130,214
GUID 126
description 130,214
Предметный указатель

н 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
и Базы данных в Интернете. Практическое руководство

pow 396 SMTP 351


Prepare 233 Source 130
prompt 40,42 SQL Server Enterprise Manager 135
Properties 132 SQL Server Query Analyzer 136, 141
SQL_C_DOUBLE 272
Q SQL_C_FLOAT 272
QUERY_STRING 292,405 SQL_C_GUID 272
Querylnterfacc 208. 239, 241 SQL_C_SBIGINT 272
R SQL_C_TYPE_DATE 272
SQL_C_TYPE_T1 M E 272
random 396
SQL_C_TYPE_TIMESTAMP 272
RDO 12, 120, 266
SQL_C_UBIGINT 272
ReadClient 315,318
real 246 SQL_C_ULONG 272
SQL_DATE_STRUCT 272
Recordset 121, 122, 127,233
recv 358 SQL_ERROR 273
SQL_INVALID_HANDLE 273
Release 208,241,243
SQL_NO_DATA 273
ReleaseRows 252
REMOTE ADDR 405 SQL_NUMERIC_STRUCT 272
SQL_T1ME_STRUCT 272
REMOTE_HOST 405
REMOTEJJSER 405 SQL_TIMESTAMP_STRUCT 272
Request 91, 107,403 SQLAllocHandle 266.267
SQLBIG1NT 272
REQUEST_METHOD 405
round 396 SQLBindCol 270
SQLBindParameter 283
s SQLConncct 268
screen 22 SQLDOUBLE 272
SCRIPT_NAME 405 SQLExccDirect 269. 283, 285
send 358 SQLFetch 273
Server 122,402 SQLFLOAT 272
SERVER_NAME 405 SQLFreeHandlc 267
SERVER_PORT 405 SQLGetDiagField 273,275
SERVER_PORT_SECURE 405 SQLGetDiagRec 273,274
SERVER_PROTOCOL 405 SQLGUID 272
SERVER_SOFTWARE 405 SQLMoreResults 285
Server Support Function 319 SQLOLEDB 120.234
Session 97,241 SQLParamData 269
Session_OnEnd 96 SQLPuiData 269
Session_OnStart 96 SQLREAL 272
SctCommandTcxt 233,242 SQLSetEnvAttr 266, 267
Set-Cookie 71 SQLState 130,214
setDate 399 SQLUBIGINT 272
setl lours 399 SQLU1NTEGER 272
setLayout 360 sqrt 396
set Minutes 399 SQRT1 2,394
setMonth 400 SQRT2 394
SetPropcrties 232, 233, 238, 239 String 391
setSeconds 400 substring 74
serTimc 73,400 SUCCEEDED 211
setTimeout 66 SUCCESS 235
setYear 400 SysFreeString 227
showModalDialog 40,44
Т
Simple Mail Transfer Protocol 351
sin 396 tan 396
smalldatetime 246 Terminate Extension 311
smallint 246 text 246
smallmoney 247 timestamp 246
smart pointer 208 tinvint 246
Предметный указатель !

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
Библиографический список

1. Айзеке С. Dynamic HTML: пер. с англ. СПб.:ВНУ, 1999.


2. Армстронг Т. ActiveX: создание Web-приложений: пер. с англ. К.: BHV, 1998.
3. Винкоп С. Использование Microsoft SQL Server 7.0. Специальное издание.:
пер. с англ. К.; М.; СПб.: Издательский дом «Вильяме», 1999.
4. Козье Д. Электронная коммерция: пер. с англ. М.: «Русская Редакция», 1999.
5. Фролов А.В., Фролов Г.В. Программирование для Windows NT: В 2 ч. Ч. 1
(Библиотека системного программиста, т. 26), ч. 2 (Библиотека системного
программиста, т. 27). М.ДИАЛОГ-МИФИ, 1996.
6. Фролов А.В., Фролов Г.В. Сервер Web своими руками. (Библиотека системно-
го программиста, т. 29). М.:ДИАЛОГ-МИФИ, 1997.
7. Фролов А.В., Фролов Г.В. Microsoft Visual J++. Создание приложений на языке
Java: В 2 ч. Ч. 1 (Библиотека системного программиста, т. 30), ч. 2 (Библио-
тека системного программиста, т. 32). М.ДИАЛОГ-МИФИ, 1997.
8. Фролов А.В., Фролов Г.В. Сценарии JavaScript в активных страницах Web.
М.ДИАЛОГ-МИФИ, 1998. (Библиотека системного программиста, т. 34).
9. Хилайер С., Мизик Д. Программирование Active Server Pages: пер. с англ.-
М.: «Русская Редакция», 1999.
10. Фролов А.В., Фролов Г.В. Грузите файлы на Web-сервер браузерами. М.: От-
крытые системы, МИР ПК, № 3-4, 1999.
11. Фролов А.В., Фролов Г.В. Немного Java — и страница ожила. М.: Открытые
системы, МИР ПК, № 12, 1998, № 1, 1999.
12. Фролов А.В., Фролов Г.В. Активный сервер Web: расширения CGI. M.: От-
крытые системы, МИР ПК, № 8, 1997.
Об авторах

Эта книга написана братьями Александром и Григорием Фроловыми, знакомым


читателям по книгам серий «Библиотека системного программиста» и «Персо-
нальный компьютер. Шаг за шагом».
Александр и Григорий Фроловы закончили МИФИ (Александр — факуль-
тет автоматики и электроники, а Григорий — факультет кибернетики).
В 1991 году братья Фроловы написали первый том серии «Библиотека сис-
темного программиста», а в 1994 году вышла первая книга серии «Персональ-
ный компьютер. Шаг за шагом». Сегодня они авторы более 40 книг по компью-
терной тематике, а также множества статей для различных периодических из-
даний. Многие работы братьев Фроловых размещены на авторском компакт-
диске (информацию о нем Вы найдете на домашней Web-страничке авторов).
Кроме того, Александр и Григорий Фроловы занимаются Интернет-проекта-
ми, создают документацию и программы различного назначения. В частности,
они разрабатывали технологию восстановления разрушенных разделов NTFS.
Вы можете связаться с авторами по электронной почте (alexandre@fro-
lov.pp.ru) или через домашнюю страничку http://www.frolov.pp.ru.
Александр Вячеславович Фролов,
Григорий Вячеславович Фролов

Базы данных в Интернете


практическое руководство по созданию
Web-приложений с базами данных
Издание 2-ое, исправленное

Редактор Ю. П. Леонова
Компьютерная верстка В. Б. Хилъченко
Технический редактор Н. Г. Тимченко
Дизайнер обложки Е. В. Козлова

Оригинал-макет выполнен с использованием


издательской системы Adobe PageMaker 6.0

TypeMarketFon/Z,ibrar)> гащвм

Главный редактор А. И. Козлов

Подготовлено к печати
Издательско-торговым ломом «Русская Редакция*

Лицензия Л Р № 066422 от 19.03.99 г.


Подписано в печать 05.10.2000 г. Тираж4 ОООэкэ.
Зак. 3571

Отпечатано с оригинал-макета в Академической типографии «Наука* РАН


199034. Санкт-Петербург. 9 линия, 12
книги по современным
информационным
технологиям

новый
интернет-магазин
http://ITbook.ru

tUi , •
|Ыиевныйкуес

базы даиньи в Компыагернык магазин


Интернета Учебный к)рс
/Мелей* Q>r. «Компьютерная
Нэогй)»щий ys^&
— п^регод трегь и деловая книга»
язда-ия 6ect-fs
г. Москва/Ленинский пр-т, 38
'на гянг, представляет
с об ей практическое тел.: (095) 778-7269
руководство по гоздлнию
при ложен™ Web с бззаин
дани» для И-дерн^з, а
также для и

БОГАТЫЙ
ВЫБОР
НИЗКИЕ
ЦЕНЫ
Новые журналы 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

Книги этой серии помогут Вам приобрести


фундаментальные знания и подготовиться н сдаче
экзаменов по программам сертификации Microsoft.

Каждая книга — это серьезное изложение темы,


полная справочная информация от первоисточника;
занятия, упражнения для самостоятельной работы,
видеоролики и вопросы для самопроверки
и закрепления знаний.

издательство компьютерной литературы

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*
мдлодого
йца

Издательство «Русская Редакция»


— партнер Microsoft Press в России —
предлагает широкий выбор литературы
по современным информационным технологиям.
Мы переводим на русский язык бестселлеры ведущих издательств мира,
а также сотрудничаем с компетентными российскими авторами.

Наши книги Вы можете приобрести • в Санкт-Петербург в:


• в MDCKIB: ЗАО «Диалект», 'ел. (812) 247-1483
.<Б кото-Глобус» ул. Мясницкаи, 6. тел.1 (095) 928-3567 • 1 Новосибирец":
..Москомкий дом книги» ул Новый Арбэт 8. тел: (095) 290-4507 000 «Топ-книга-, тел.: (38321 36-1026
«Дом технической книги» Ленинский по т, 40 ТЕЛ.. (ОЭ5'| 137-6019 • в НайвремнЫ! Челнах:
«Молодая гварряЯ" уп Багьшая Полячка. 28. тел.. (095) 233-5001 000 «Аспею-С», тел.. {8552)53-8013

«Мир» Ленинграда™ пр-т. 73, тел..(095)153-4511 • я Ална-Ате (Казахстан):


»Мир печати» уг 2-я Таерскэя-Ямская, 54, ТЕЛ.. (095] Э7В-50Л7 ЧПБолагАнренв.тап.. S-3?7-290-191-2f. (3272)26-1404

Торговый доч «Москва» ул. Тверская. 8. та.1 (095) 229-6483 • в Киеве {Украина):
000 Издательство .Ирина", теп. (044) 2119-0423
«йпекс и К"» Магазин «Книги» г. Зелеипград.
^Техническая книга на Петровке», тел.. (П44) 464-685S

• в Минске (Бвяпргсеня):
Фирменный магазин «Компьютерная и деловая книга» ОТО ..Попурри- тел.. {0171 223-5726
Москва, Ленинский проспект, строение 38, тел.: (095) 773-7269

ВНИМАНИЕ! Новый книжный интернет-магазин WWW.ITbOOk.ru

Н Р У С С К И ЩЩУ ТЕП.: (095! 142-0571; тел./факс: (095) 145-4519


e-mail: info@rusedii.rj; Irttp:// www.rjsedit.rL

You might also like