Лабораторная работа 10
CGI-сценарии
Цель работы
Ознакомиться с организацией исполняемых приложений на web-сервере и изучить технологию программирования CGI-сценариев.
Теоретическая справка
Создание модуля Web
Серверные приложения строятся в C++Builder на основе модуля Web — компонента типа TWebModule. Этот компонент может служить контейнером для ряда других компонентов. Сервер типа TWebModule отсутствует в палитре компонентов. Чтобы спроектировать его, надо выполнить команду File | New | Other и в диалоговом окне New Items на странице New выбрать пиктограмму Web Server Application. Перед вами откроется окно, в котором вы должны указать тип модуля. Выберите тип CGI Stand-alone executable — выполняемый модуль CGI. После этого C++Builder создаст в Редакторе Кода заготовку соответствующего модуля и вы увидите окно модуля.
Это окно различается в C++ Builder 6 и 5. В C++Builder 6 вы увидите просто пустое окно, в котором можно размещать компоненты, создающие страницы, осуществляющие связи с базами данных и т.п. Поскольку пока все эти компоненты нам не будут нужны, мы можем о нем временно забыть, кроме одного его свойства: из контекстного меню этого окна мы сможем вызвать редактор действий, с которым будем работать. Если вы откроете окно дерева объектов (Object TreeView), то увидите в нем дерево действий, которые будете вводить в модуль (вначале, конечно, вершина Actions в нем будет пустой). Это окно помогает осуществлять навигацию по действиям.
В C++ Builder 5 окно модуля Web отличается только тем, что два указанных выше окна отображаются отдельными панелями одного окна. К тому же там имеется страница Data Diagram, позволяющая строить диаграммы связей между компонентами. В С++ Builder б подобная страница в модуле просто не нужна, так как аналогичная страница имеется в окне Редактора Кода.
В окне Инспектора Объектов вы можете видеть свойства модуля Web (в С++ Builder 5 вы увидите их, если выделите вершину WebModulel). Основное свойство модуля — Actions. Это собрание действий — объектов типа TWebActi-onltem. Заполняется это список специальным редактором действий, вызываемым щелчком на кнопке с многоточием в окне Инспектора Объектов рядом со свойством Actions. То же окно редактора действий откроется, если вы щелкнете в окне модуля (в C++Builder 5 — на вершине WebModulel) правой кнопкой мыши и выберете из контекстного меню раздел Action Editor. В открывшемся окне быстрой кнопкой Add New вы можете добавить новое действие, кнопкой Delete Selected (вторая слева) — удалить выделенное действие, кнопками со стрелками — изменить последовательность действий.
Каждое действие — объект типа TWebActionltems со своими свойствами, методами, событиями. Свойства вы можете увидеть в Инспекторе Объектов, выделив одно из действий. Основное свойство действия — Pathlnfo. Чтобы понять его назначение, надо сообщить некоторые дополнительные сведения об URL. В более общем случае URL может выглядеть, например, так:
http://www.myserv.com/pi/servl.exe/actionl?value=5&Resp=Yes
Начинается URL с указания протокола (HTTP). Далее следует имя хоста — адрес сервера (в приведенном примере «www.myserv.com»). Затем следует имя серверного приложения и путь к нему (в приведенном примере «/pl/servl.exe»). После этого, начинаясь с последнего символа слэша и вплоть до вопросительного знака записывается необязательная часть URL, именуемая Pathlnfo. В приведенном примере это «/actionl». Эта информация определяет способ обработки информации серверным приложением. Например, может указываться процедура, которая должна обрабатывать данное сообщение. После символа вопросительного знака следует часть URL, называемая Query — запрос. Обычно она состоит из списка, включающего идентификаторы, символы равенства и значения этих идентификаторов. Отдельные элементы списка отделяются друг от друга символами "&". В приведенном примере введено имя value со значением «5» и имя Resp со значением «Yes». Это информация, которая передается процедуре серверного приложения для обработки.
Теперь мы можем вернуться к рассмотрению свойств действия и к главному свойству — Pathlnfo. Это свойство совпадает с элементом Pathlnfo запроса URL. При получении запроса производится поиск действия, в котором Pathlnfo совпадает с запроса Pathlnfo. Если такое действие найдено и оно имеет обработчик события OnAction, то выполняются операторы, записанные в этом обработчике. Заголовок обработчика имеет вид:
void __fastcall TWebModulel::WebModulelWebActionItemlAction(
TObject *Sender, TWebRequest *Request, TWebResponse *Response, bool SHandled)
Параметр Request определяет объект запроса типа TWebRequest. Основное свойство этого объекта Query — строка типа string, представляющая часть Query запроса URL. В приведенном выше примере URL она имеет вид: «value=5&Resp=Yes». Другое свойство объекта запроса — QueryFields типа TStrings. Это свойство представляет Query запроса URL в виде списка строк. В приведенном примере он будет содержать две строки: «value=5» и «Resp=Yes». Таким образом, через объект QueryFields обработчик события получает доступ к содержащейся в запросе информации. Обращаться к значениям параметров запроса удобно с помощью метода Уа1иев[имя]. Например, в приведенном примере выражение Request—>Query-Fields—>Values["Resp"] вернет строку «Yes».
Параметр Response определяет объект ответа типа TWebResponse. Основное свойство этого объекта Content — ответ пользователю типа string в виде текста HTML, имени файла HTML и некоторых других видов.
Параметр Handled определяет, завершен ли ответ, или объект Response должен дополнительно формироваться другими действиями. Если требуется, чтобы формирование Response завершили другие действия, следует в обработчике задать значение Handled равным false. Тогда, если ответом займется другое действие, ему будет передан объект Response, в котором свойство Content содержит текст, занесенный предыдущими действиями.
Мы рассмотрели свойство Pathlnfo объекта действия и обработчик события OnAction. У объекта действия имеется также свойство Default. Если оно в одном из действий установлено в true, то это действие является действием по умолчанию. Если в модуле не нашлось действия, в котором Pathlnfo совпадает с запросом, то наступает событие OnAction действия по умолчанию. Свойство Enabled объекта действия определяет его доступность.
У самого сервера типа TWebModule, содержащего собрание действий, имеются два события, которые нередко используются для управления сервером. Это события BeforeDispatch и AfterDispatch, наступающие в начале и в конце обработки запроса. Их заголовки подобны приведенному выше для события OnAction. Прежде всего, при получении запроса наступает событие BeforeDispatch. В его обработчике можно проанализировать запрос и дать на него ответ или произвести настройку сервера, например, сделать с помощью свойств Enabled доступными или недоступными отдельные действия. Затем наступают события действий. В конце наступает событие AfterDispatch. В его обработчике можно проанализировать ответ, сформированный действиями, и что-то в нем изменить.
Пример серверного приложения
Попробуем посмотреть, как применить на практике все, изложенное в разд. 1. Построим простое серверное приложение, в котором был бы обмен информацией между клиентом и программой. Пусть наше приложение задаст клиенту вопрос, сколько будет дважды два, и предоставит ему выбор из двух ответов: 4 или 5. Получив ответ клиента, приложение должно сообщить ему результаты этого сложного тестирования.
Закройте все открытые проекты, если они у вас имеются, и создайте модуль сервера (команда File | New | Other, пиктограмма Web Server Application). Откройте редактор действий и занесите в него два действия. Первое действие будет формировать ответ на выбор пользователя.
Его свойство Pathlnfo задайте равным «/1». Второе действие будет действием по умолчанию. Его назначение — задать пользователю вопрос. Установите свойство Default этого действия в true.
Осталось написать обработчики событий OnAction этих действий. Для действия по умолчанию обработчик может иметь вид:
void __fastcall TWebModulel::WebModulelWebActionItem2Action (
TObject *Sender, TWebRequest *Request, TWebResponse *Response, bool SHandled){
Response->Content = "<hl> Знаете ли вы дважды два? </hl>" "<р>Сколько будет 2x2?</p><HR>"
"<А HREF=\"/cgi-bin/TwiceTwo.exe/l?4\"> 4</A> или " "<А HREF=\"/cgi-bin/TwiceTwo.exe/l?5\">5</A>?, <HR>"; Handled = true; }
Этот код возвращает документ HTML. Пользователю предоставляются на выбор две ссылки с текстами «4» и «5». В обеих ссылках Pathlnfo равняется «/1». Таким образом, обе ссылки направляют запрос первому действию, у которого задано это значение Pathlnfo. Но запрос Query в этих двух ссылках разный: «4» и «5». Так что по значению запроса в первом действии можно распознать, какую из ссылок выбрал пользователь. Обработчик события OnAction первого действия может иметь вид:
void __fastcall TWebModulel::WebModulelWebActionItemlAction(
TObject *Sender, TWebRequest *Request, TWebResponse *Response, bool SHandled) { if(Request->Query == "4")
Response->Content = "Ответ 4. Вы прекрасный математик!"; else Response->Content =
"Ответ 5. Вы немного забыли таблицу умножения."; Handled = true; }
Этот обработчик формирует разные ответы в зависимости от значения свойства Request.Query.
Сохраните свой проект под именем TwiceTwo. Скомпилируйте его и созданный модуль TwiccTwo.exe поместите в выполняемый каталог вашего сервера. С помощью любого браузера выполните ваше приложение.
Приведенный пример демонстрирует модуль сервера с несколькими действиями. Но тот же самый пример можно реализовать проще. Вместо формирования исходного документа в действии по умолчанию, что не очень удобно, можно было бы сформировать с помощью любого редактора HTML документ, задающий пользователю сакраментальный вопрос о дважды два. В простейшем случае документ, воспроизводящий приведенный ранее вид вопроса, мог бы иметь вид:
<html>
<head>
<title>3naeTe ли вы дважды nBa</title>
</head>
<body>
<p>Знаете ли вы дважды два?</p>
<р>Сколько будет 2x2?</р>
<hr>
<р><а HREF="http://mycomputer/cgi-bin/TwoTwo.exe/l?4">4</a>
или
<а HREF="http://mycomputer/cgi-bin/TwoTwo.ехе/1?5">5</а>? </р>
<hr>
</body>
</html>
Основное отличие от ответа, формировавшегося ранее сервером, заключается в том, что ранее ссылки на выполняемый файл содержали только имя файла «TwiceTwo.exe», а в приведенном тексте ссылки полные: «http://mycomputer/cgi-bin/ TwoTwo.exe». Это необходимо, если файл HTML с данным текстом и выполняемый файл TwoTwo.exe, который вы сейчас создадите, расположены в разных местах. В предыдущем примере такого быть не могло, так как работа велась с одним файлом — TwiceTwo.exe.
Сохраните созданный документ в файле TwoTwo.html. Теперь спроектируйте сервер TwoTwo, который будет воспринимать ответ пользователя. Делается это так же, как в рассмотренном ранее примере. Только в данном случае нужно предусмотреть всего одно действие с Pathlnfo = "\1"< обработчик которого ничем не отличается от рассмотренного ранее. Так что вы просто можете взять тот же модуль и удалить из него второе действие. Сохраните модуль этого нового сервера под именем «TwoTwo» и nepeHecnfe его выполняемый файл в выполняемый каталог сервера. Откройте любым браузером подготовленный ранее документ TwoTwo.html и убедитесь, что все работает так же, как в предыдущем примере.
Обработка данных из форм
Разберем пример простейшей формы. Как делать формы можно узнать в большинстве учебников по HTML. Особое внимание обратим на передачу параметров.
<form name="form1" method="post" action="project2.exe">
<input type="text" name="textfield">
<input type="submit" name="Submit" value="Запрос">
</form>
После заполнения формы и нажатия кнопки «Запрос» серверному приложению будет передан список всех параметров формы в не слишком хитро зашифрованном виде:
http://server/my/project2.exe?textfield=%D2%E5%F1%F2&Submit=%C7%E0%EF%F0%EE%F1
Нетрудно догадаться, что список параметров идет после знака вопроса в виде «Имя параметра»=«Значение параметра». Параметры отделены друг от друга амперсандом. Если используется местная кодировка, то после процента следует код символа.
Подобную строчку можно увидеть на экране своего браузера, заменив метод передачи параметров с post на get. Мы же будем использовать метод post. Именно этот метод позволяет использовать все возможности библиотеки VCL.
Теперь приведем текст не менее простой программы-сценария.
void __fastcall TWebModule1::WebModule1WebActionItem1Action(
TObject *Sender, TWebRequest *Request, TWebResponse *Response,
bool &Handled)
{
AnsiString A=Request->ContentFields->Values["textfield"];
AnsiString R="<html>\n<h1>Good day, dear "+A+"</h1>\n";
TDateTime D;
D=D.CurrentTime();
R+=D.TimeString() +"</html>";
Response->Content=R;
}
Интерес предоставляют формальные параметры Request и Pesponse. В первом передаются параметры формы, а в Response->Content заключается текст ответа в формате HTML.
Чтобы не писать самому программу разбора строки параметров запроса, можно воспользоваться возможностями библиотеки VCL:
Request->ContentFields->Values["textfield"]
Логика программирования серверной части заключается в формировании строки-ответа Response->Content. По окончании работы программы эта строка передается клиенту:
<html>
<h1>Good day, dear Guest</h1>
13:50:07</html>