Программирование Интернет приложений в Borland
Kylix. Часть II:
Разработка приложений для Web-сервера.
Автор: Андрей
Боровский (www.kylixportal.chat.ru)
Эта статья продолжает серию, посвященную
программированию Интернет-приложений в Borland Kylix.
На этот раз речь пойдет о CGI-приложениях и модулях
сервера Apache.
CGI-приложения
Изначально протокол HTTP
создавался для передачи по Сети документов в
формате HTML и сопутствующей им графики, однако
Web-броузеры оказались настолько удобны, что
всемирная паутина очень быстро начала обрастать
различными дополнительными функциями. Весьма
заманчиво выглядит возможность использовать
броузер клиента в качестве интерфейса для
взаимодействия с удаленным приложением. Такое
взаимодействие, разумеется, требует наличия
механизма передачи команд и данных от клиента
серверу. Для стандартизации механизма передачи
команд клиента серверу был разработан стандарт
CGI. В общих чертах работа CGI выглядит следующим
образом: клиент загружает HTML страницу,
содержащую форму и посылает серверу
определенный в форме запрос (request), в котором
указано действие
(action) и список параметров. Действие представляет
собой указатель на CGI-приложение, которое должно
выполнить запрос. Например действие /cgi-bin/frenchwines
указывает на приложение frenchwines. Для одного
приложения можно определить несколько действий,
которые идентифицируются как отдельные сетевые
ресурсы: /cgi-bin/frenchwines, /cgi-bin/frenchwines/, /cgi-bin/frenchwines/welcome
– это разные действия приложения frenchwines. Список
параметров представляет собой перечень имен
параметров и их значений. Если HTML форма
использует для передачи параметров метод GET,
список параметров можно увидеть в адресной
строке броузера после идентификатора ресурса (он
отделен от идентификатора символом ‘?’). С точки
зрения протокола HTTP CGI-запрос является командой
на загрузку некоторого ресурса. В ответ на запрос
сервер обычно посылает клиенту HTML документ,
который как правило является динамической
страницей, т. е. не хранится на диске в виде файла,
а генерируется приложением.
CGI–функции могут быть реализованы на сервере в
двух вариантах: в форме CGI-сценариев (CGI scripts) и в
виде CGI приложений. CGI приложения представляют
собой исполнимые модули, вызываемые Web-сервером.
Схема взаимодействия между броузером, сервером и
CGI-модулем приводится на рисунке. По своей
структуре CGI приложение является обычной
консольной программой. Сервер передает данные CGI
приложению через переменные окружения и
стандартный поток ввода, а CGI приложение передает
данные серверу через свой стандартный поток
вывода. Сервер запускает новую копию приложения
для обработки каждого запроса. По окончании
обработки запроса CGI-приложение завершается .
Далее следует пример простейшего CGI приложения,
написанного в Borland Kylix. Этот пример не использует
специальные средства разработки CGI приложений,
входящие в состав Kylix. Модуль Libc используется для
того, чтобы сделать приложение более компактным.
В принципе нам было бы достаточно одного модуля
SysUtils.
program simplecgi;
{$APPTYPE CONSOLE}
uses
Libc;
var
Method : PChar;
Query : String;
begin
Method := getenv( 'REQUEST_METHOD' );
if Method = nil then
begin
WriteLn( 'Error : no request method' );
Halt(0);
end;
WriteLn( 'Content-Type: text/html' );
WriteLn;
Write( '<HTML><HEAD><TITLE>Simple CGI Application</TITLE><BODY>' );
Write( '<H2>This page was generated on the fly by the CGI application</H2>' );
Write( '<P>You have sent data by ', String(Method), ' method</P>' );
Write( '<P>The user agent is "', String(getenv('HTTP_USER_AGENT')), '"</P>' );
Write( '<P>The HTTP_REFERER variable is "', String(getenv('HTTP_REFERER')), '"</P>' );
if Method = 'GET' then
Write( '<P>The query string is "', String(getenv('QUERY_STRING')), '"</P>' )
else begin
Write( '<P>The content length is ', String(getenv('CONTENT_LENGTH')), '</P>' );
Read( Query );
Write( '<P>The query string is "', Query, '"</P>' )
end;
Write( '</BODY></HTML>' );
end.
|
Для того, чтобы проверить работу примера,
скомпилируйте проект simplecgi.dpr (исходный текст
лежит здесь) и скопируйте
полученный исполнимый файл в каталог
CGI-приложений Web-сервера. В Linux Mandrake этим каталогом
по умолчанию является /var/www/cgi-bin/ (естественно, сам
Web-сервер должен быть установлен и запущен в
вашей системе). Затем откройте в броузере
страницу TestCGI.html. Страница TestCGI содержит две формы
для передачи параметров приложению. Одна форма
использует для передачи данных метод GET, другая –
метод POST.
Кроме возможности создания CGI-приложений “с
нуля”, Borland Kylix располагает весьма удобными
специальными средствами разработки приложений
для Web-сервера. Kylix может создать для вас
“заготовку” CGI-приложения. В окне New выберите
элемент Web Server Application и в открывшемся диалоговом
окне – пункт CGI standalone executable.
В качестве примера такого приложения мы
рассмотрим программу, реализующую Web-интерфейс к
базе данных Borland Interbase. Для компиляции и
выполнения этого примера вам понадобится сервер
баз данных Interbase (входит в комплект поставки Kylix
SDE). Перед началом работы с примером Web-интерфейса
базы данных, убедитесь, что серверы Web и Interbase
установлены и нормально работают.
Приложение-пример (исходный текст лежит здесь) называется fenchwines и
представляет собой Web-интерфейс к базе данных
французских вин. (Напомню, что “Kylix” в переводе
означает “винная чаша”, так что “винная”
тематика вполне соответствует рассматриваемому
продукту. Для правильной компиляции и запуска
демонстрационного приложения необходимо
выполнить следующее: скачайте архив с
базой данных, распакуйте его, скопируйте файл
vina.gdb в какой-нибудь каталог. В корневом каталоге
http документов вашего сервера (часто это /var/www/html) создайте каталог wdb и
скопируйте в него файлы wctempl.html и barrels.jpg из архива
приложения. Теперь откройте приложение в среде
Kylix. При помощи менеджера проекта откройте окно
Web-модуля, которое связано с модулем main. В окне
Web-модуля щелкните правой кнопкой мыши по
компоненту SQLConnection1 и в открывшемся меню выберите
пункт Edit Connection Properties. В открывшемся диалоговом
окне создайте новое соединение. В качестве
драйвера базы данных выберите INTERBASE. В поле Database
задайте абсолютный путь к файлу vina.gdb.
Убедитесь, что поля Password и User_Name содержат
значения masterkey и SYSDBA соответственно. Свойство
Connection компонента SQLConnection1 должно указывать на
новое соединение. Далее отредактируйте свойство
HTMLFile компонента PageProducer1 так, чтобы оно содержало
полный путь к файлу wctempl.html. Скомпилируйте
приложение frenchwines и скопируйте исполнимый файл в
каталог CGI-приложений (cgi-bin). Для того, чтобы
проверить работу нашего приложения в окне
броузера наберите http://localhost/cgi-bin/frenchwines. При этом в
окне броузера появится приветственная
HTML-страница. Форма на странице позволяет
выбирать вина из базы данных по происхождению и
категории. Для того, чтобы отобразить все вина,
содержащиеся в базе, необходимо ввести “any” в
обоих полях формы.
Структура CGI-приложения очень похожа на
структуру обычного приложения Kylix. Вместо
объекта TApplication в Web-приложении используется
объект TWebApplication, а роль класса TForm выполняет класс
TWebModule. Потомок класса TWebModule является владельцем
всех компонентов, используемых в Web-приложении и
ему же принадлежат методы обработки событий.
Окно компонента TWebModule с дочерними
компонентами
Важнейшим элементом TWebModule является список
действий Actions. В этом списке хранятся объекты
TWebActionItem, определяющие действия приложения.
Свойство PathInfo объекта TWebActionItem позволяет задать
идентификатор действия. Если свойство PathInfo
содержит пустую строку, идентификатором данного
действия является само имя CGI-приложения. В
других случаях идентификатор действия должен
предваряться символом “/”. В следующей таблице
показаны примеры значений PathInfo и
соответствующие им действия для приложения
frenchwines, расположенного в директории /cgi-bin/.
Значение PathInfo |
Действие |
“” |
/cgi-bin/frenchwines |
“/” |
/cgi-bin/frenchwines/ |
“/welcome” |
/cgi-bin/frenchwines/welcome |
/error |
/cgi-bin/frenchwines/error |
Список Actions можно заполнить в процессе
разработки приложения при помощи редактора
списка. Для того, чтобы открыть окно редактора
нужно в окне инспектора объектов щелкнуть по
кнопке с многоточием в поле свойства Actions.
Свойство MethodType объекта TWebActionItem позволяет
указать метод передачи параметров для данного
действия. Метод, указанный в свойстве MethodType
должен соответствовать методу, указанному в тэге
FORM HTML страницы, генерирующей запрос. Значение mtAny
позволяет действию обрабатывать любые запросы.
Для объекта TWebActionItem определено единственное
событие – OnAction. Когда Web-сервер получает
CGI-запрос, он запускает соответствующее
CGI-приложение и передает ему всю необходимую
информацию. В Kylix-приложении диспетчер запросов
определяет, какому действию адресован запрос, и
вызывает обработчик события OnAction этого действия.
После выполнения обработчика OnAction приложение
завершается.
Заголовок обработчика выглядит следующим
образом:
type THTTPMethodEvent = procedure
(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled:
Boolean) of object;
Объект Request содержит данные о поступившем
запросе. Свойства этого объекта позволяют
получить сведения об адресе удаленной машины,
программном обеспечении клиента, методе запроса
и т. п. Для CGI-запроса в объекте Request передается
также список параметров запроса. Список
параметров хранится в свойстве ContentFields (если
запрос передан посредством метода POST) или QueryFields
(если использовался метод GET). Оба
свойства имеют тип TStrings и содержат набор строк
вида
ParamName=ParamValue
Объект Response описывает ответные действия
приложения. Текст HTML страницы, возвращаемой
приложением, передается в свойстве Content объекта
Response. В простейшем CGI-приложении обработчик OnAction
может выглядеть следующим образом:
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.Content := '<HTML><BODY><H2>Hello, World!</H2></BODY></HTML>';
end;
|
Рассмотрим другие примеры. В приложении frenchwines
главная HTML-страница выводится при выполнении
действия frenchwines/welcome. Однако, пользователь может
набрать в строке броузера просто .../frenchwines или
.../frenchwines/ и было бы очень удобно, если бы в этой
ситуации приложение автоматически
перенаправляло броузер пользователя на страницу
frenchwines/welcome. Для того, чтобы приложение fenchwines
выполняло такое перенаправление, в нем заданы
два действия, значениями PathInfo которых являются
соответственно пустая строка и символ “/”. Обоим
действиям можно назначить один и тот же
обработчик OnAction:
procedure TWebModule1.WebModule1WebActionItem3Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.Location := Request.InternalScriptName+'/welcome';
Response.StatusCode := 301;
Response.Content := '<HTML><BODY><H2>This page is
redirected to <A HREF='+ Response.Location +'/>'
+ Request.Host + Response.Location + '</A></H2></BODY></HTML>';
end;
|
Статус-код 301 сообщает клиенту о необходимости
перенаправить запрос по новому адресу. А что
произойдет, если клиент пошлет запрос на
выполнение действия, которое не определено в
CGI-приложении, например frenchwines/querydb ? В этом
случает Kylix-приложение сгенерирует исключение и
Web-сервер выдаст неприятное сообщение о
внутренней ошибке. Для того, чтобы обращение к
неопределенному действию не приводило к
исключительной ситуации, необходимо создать еще
одно действие и присвоить
свойству Default соответствующего объекта TWebActionItem
значение True. Теперь это действие будет
вызываться для всех запросов, вызывающих
неопределенные действия. Обработчик события
OnAction этого действия может выглядеть следующим
образом:
procedure TWebModule1.WebModule1WebActionItem4Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.Title := 'Resource Not Found';
Response.StatusCode := 404;
Response. Content := '<HTML><BODY><H2>Error 404</H2><P>Requested resource "'
+ Request.InternalScriptName+Request.PathInfo + '" is not found
on this server.</BODY></HTML>';
end;
|
Генераторы контента
Во всех приведенных выше примерах HTML страницы,
возвращаемые сервером задавались в явном виде,
путем присвоения строки HTML команд свойству Content.
Очевидно, что для вывода сложных страниц с
большим числом элементов такой подход
непригоден. Для облегчения решения этой проблемы
Kylix предоставляет несколько специальных
компонентов-генераторов контента (content producers). На
вкладке Internet панели компонентов Kylix раположены
четыре компонента-генератора: TPageProducer,
TDataSetTableProducer, TSQLQueryTableProducer и TDataSetPageProducer.
Компонент TPageProducer генерирует страницу на
основе заданного шаблона. Идея этого компонента
заключается в том, что на многих динамических HTML
страницах изменяются только отдельные
фрагменты, основная же часть страницы остается
неизменной. Шаблоном для компонента TPageProducer
может служить обычная HTML-страница, в которую
добавлены специальные тэги, невидимые для
броузера. Формат тэга имеет следующий вид:
<#TagName Param1=Value1
Param2=Value2 ...>
Шаблон может быть задан в свойстве HTMLDoc (в виде
набора строк) или в свойстве HTMLFile (как имя файла).
Для получения HTML страницы, построенной на основе
шаблона, никаких специальных команд вызывать не
нужно. Необходимо лишь прочитать свойство Content
компонента TPageProducer. При этом функция, лежащая в
основе свойства Content, анализирует заданный
шаблон и всякий раз, когда в шаблоне встречается
тэг, описанный выше, вызывает обработчик события
OnHTMLTag, которому передается информация о тэге,
вызвавшем событие. Обработчик события OnHTMLTag
обычно возвращает строку, которой следует
заменить соответствующий тэг в готовой странице.
Таким образом, при обращении к
свойству content возвращается строка HTML команд, в
которой прозрачные тэги уже заменены
динамическими значениями. При использовании
компонента TPageProducer, обработчик события OnAction,
возвращающий клиенту динамическую страницу, мог
бы выглядеть следующим образом:
procedure TWebModule1.WebModule1WebActionItem4Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.Content := PageProducer1.Content;
end;
|
Код выглядит очень просто, однако все можно
упростить еще больше. Если все, что нужно сделать
в ответ на запрос, это передать страницу,
созданную компонентом-генератором контента,
можно вообще обойтись без обработчика OnAction.
Достаточно присвоить свойству Producer
соответствующего объекта TWebActionItem ссылку на
компонент-генератор.
Специально для отображения содержимого баз
данных в Kylix и Delphi существуют еще три
компонента-генератора Web-страниц: TDataSetTableProducer,
TSQLQueryTableProducer и TDataSetPageProducer.
Компонент TDataSetTableProducer генерирует HTML страницу,
на которой записи из базы данных отображаются в
виде таблицы. Для того, чтобы связать компонент
TDataSetTableProducer с базой данных, необходимо указать
соответствующее значение в свойстве DataSet.
HTML-страница генерируется при обращении к
свойству Content, также, как и в случае с TPageProducer.
Компонент TDataSetTableProducer обладает множеством
свойств, позволяющих задать различные элементы и
внешний вид генерируемой страницы. Событие
OnFormatCell, возникающее в процессе генерации
страницы, позволяет динамически форматировать
отдельные ячейки создаваемой таблицы.
Страница, созданная компонентом TDataSetTableProducer
TSQLQueryTableProducer выполняет те же функции, что и
TDataSetTableProducer с тем отличием, что TSQLQueryTableProducer
работает с SQL запросами к базам данных,
содержащими параметры, что позволяет не
отображать всю таблицу целиком, а производить
выборку данных на основе некоторого признака.
Источником данных для компонента TSQLQueryTableProducer
является компонент TSQLQuery, на который должно
указывать свойство TSQLQueryTableProducer.Query. Примеры
использования компонентов TDataSetTableProducer и
TSQLQueryTableProducer можно видеть в приложении frenchwines.
Компонент TDataSetPageProducer позволяет создавать
Web-страницу, содержащую информацию из одной
(текущей) записи базы данных.
Разделяемые модули для сервера Apache.
Рассмотренные в предыдущей части приложения
используют универсальный интерфейс CGI и могут
работать совместно с любым Web-сервером. Web-сервер
Apache позволяет также использовать модули,
обладающие более широкой функциональностью.
Модули могут быть скомпилированы как часть ядра
Apache или же реализованы в виде разделяемых
библиотек. Borland Kylix позволяет создавать только
модули в виде разделяемых библиотек. Для того,
чтобы сервер Apache мог подключать разделяемые
библиотеки, необходимо пересобрать ядро сервера,
установив опцию, разрешающую загрузку
разделяемых модулей. Для пересборки ядра вам
потребуются исходные тексты apache (скачать их
можно с www.apache.org
). Распакуйте архив дистрибутива
и в коневом каталоге дистрибутива создайте файл
config.status, содержащий следующий текст:
#!/bin/sh
LIBS="/usr/lib/libpthread.so" \
./configure \
"--with-layout=Apache" \
"--enable-module=so" \
"--enable-rule=SHARED_CORE" \
"$@"
|
Далее этому файлу следует присвоить статус
исполнимого:
chmod 755 config.status
запускаем созданный скрипт:
./config.status
и даем команду
make
Теперь у нас есть версия Apache, поддерживающая
разделяемые модули. Для того, чтобы установить
сервер автоматически, необходимо в режиме root
дать команду
make install
Учтите при этом, что после выполнения этой
команды новая версия Apache может быть установлена
в каталог, отличный от того, в который она была
установлена ранее. Возможно, вам придется
отредактировать файлы сценариев запуска и файл
httpd.conf.
Если Apache был установлен в каталог /usr/local/apache/, то
для запуска сервера вручную можно
воспользоваться командой:
/usr/local/apache/bin/apachectl start
а для остановки:
/usr/local/apache/bin/apachectl stop
Теперь можно перейти к написанию простейшего
модуля. Для создания заготовки модуля Apache в Kylix в
окне New выберите элемент Web Server Application и в
открывшемся диалоговом окне – пункт Apache shared module
(DSO). Kylix создаст новый проект и в нем Web-модуль,
аналогичный Web-модулю CGI-приложения. Разработка
модулей для Apache практически аналогична
разработке CGI-приложений. Сохраните ваш модуль
под именем apachedemo. Создайте новое действие (при
помощи свойства Actions объекта TWebModule1 и назначьте
следующий обработчик событию OnAction созданного
объекта-действия:
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.Content := '<HTML><BODY><H2>Hello,
World!</H2></BODY></HTML>';
end;
|
Откройте окно исходного текста проекта (Project|View
Source) и отредактируйте текст, так чтобы он
выглядел следующим образом:
library apachedemo;
uses
WebBroker, HTTPD, ApacheApp, Main in 'Main.pas' {WebModule1: TWebModule};
exports
apache_module name 'hello_module';
begin
ModuleName := 'Hello_Module';
ContentType := 'hello-handler';
Application.Initialize;
Application.CreateForm(TWebModule1, WebModule1);
Application.Run;
end.
|
Значения переменных ModuleName и ContentType необходимы
серверу Apache для идентификации модуля.
Модуль для сервера готов! Скомпилируйте проект.
В результате компиляции должен быть создан файл
libapachedemo.so. Скопируйте этот файл в каталог
разделяемых модулей сервера Apache (если сервер был
установлен в каталог /usr/local/apache/, каталогом
разделяемых модулей будет /usr/local/apache/libexec. Далее
внесите следующие строки в файл httpd.conf:
LoadModule hello_module
libexec/libHelloModule.so
<Location /hello>
SetHandler hello-handler
</Location> |
После директивы LoadModule следует имя модуля
(указанное в директиве exports библиотеки) и путь к
файлу, в котором размещен модуль. Команда Location
позволяет задать URL, соответствующую обработчику
hello-handler. Для того, чтобы проверить работу модуля,
перезапустите Web-сервер и в окне броузера
наберите:
http://localhost/hello.
Теперь рассмотрим более интересный пример.
Допустим, на вашем Web-сервере размещены файлы с
исходными текстами Kylix-программ (файлы *.pas и *.dpr) и
вы хотите, чтобы пользователям эти файлы
передавались не в текстовом формате, а в формате
HTML, с разметкой синтаксиса, характерной для Delphi и
Kylix. Конечно, эту проблему можно решить,
конвертируя все файлы исходных текстов в формат
HTML перед размещением их на сервере, но мы пойдем
другим путем. Файлы исходных текстов размещаются
на сервере в обычном текстовом формате, а
специальный модуль сервера Apache будет
конвертировать их в формат HTML “налету”, перед
посылкой клиенту. Исходный текст модуля
находится здесь.
Текстовый файл reserved содержит список
зарезервированных слов (которые выделяются
жирным шрифтом). Скопируйте этот файл в
какой-нибудь каталог (например /usr/local/apache/libexec/) и
укажите путь к этому файлу в константе ReservedPath в
файле main.pas. Скомпилируйте проект и скопируйте
полученную библиотеку libpas2html.so в каталог libexec. В
файл httpd.conf добавьте следующие строки:
LoadModule pas2html_module
libexec/libpas2html.so
AddType text/html .pas
AddHandler pas2html-handler .pas
AddType text/html .dpr
AddHandler pas2html-handler .dpr |
После этого следует перезапустить Web-сервер.
Теперь все файлы с расширениями .pas и .dpr,
находящиеся в Web-подкаталогах, будут
автоматически конвертироваться в формат HTML
перед посылкой клиенту.
Исходные тексты:
Простое CGI-приложение
Приложение frenchwines
База данных для приложения frenchwines
Модуль pas2html
Статья и примеры программ © 2001 Андрей
Наумович Боровский. Воспроизведение возможно
только с разрешения автора.
|