15.4. CG 1-сценарии
Глава 15 Язык Perl и CGI-программирование
🕛 02.11.2006, 16:07
Назначение CGI-программы - создать новый HTML-документ, используя данные, содержащиеся в запросе, и передать его обратно клиенту. Если такой документ уже существует, то передать ссылку на него. Какой язык можно использовать для написания CGI-программ? Сам интерфейс CGI не накладывает ограничений на выбор языка программирования. Зная, какую задачу решает CGl-программа и каким образом она получает входную информацию, мы можем назвать свойства, которыми должен обладать язык CGI-программирования.Средства обработки текста. Необходимы для декодирования входной информации, поступающей в виде строки, состоящей из отдельных полей, разделенных символами-ограничителями.
Средства доступа к переменным среды. Необходимы, так как с помощью переменных среды данные передаются на вход CG.I-программы.
Возможность взаимодействовать с другими программами. Необходима для обращения к СУБД, программам обработки графики и другим специальным программам.
Выбор языка зависит и от операционной системы Web-сервера. Большая часть имеющихся серверов предназначена для работы под управлением операционной системы UNIX. Учитывая эти соображения, мы можем заключить, что язык Perl, обладающий развитыми средствами обработки текста и создания сценариев, первоначально созданный для работы в ОС UNIX и перенесенный на множество других платформ, является наиболее подходящим средством создания сценариев CGI. Кроме того, CGI-програм-мирование на языке Perl имеет поддержку в виде готовых модулей CPAN, свободно доступных в сети Internet.
CGI-сценарий на языке Perl - это программа, имеющая свою специфику. Она, как правило, генерирует HTML-документ, посылаемый клиенту в виде ответа сервера. Ответ сервера, так же, как и запрос клиента, имеет определенную структуру. Он состоит из ^следующих трех частей:
1. Строка состояния, содержащая три поля: номер версии протокола HTTP, код состояния и краткое описание состояния, например:
НТТР/1.0 200 OK f запрос клиента обработан успешно
НТТР/1.0 404 Not Found # Документ по указанному адресу
не существует
2. Заголовки ответа, содержащие информацию о сервере и о возвращаемом HTML-документе, например:
Date: Mon, 26 Jul 1999 18:37:07 GMT # Текущая дата и время
Server: Apache/1.3.6 ::,, -.# Имя и номер версии сервера
Content-type: text/html tt Описывает медиа-тип содержимого
3. Содержимое ответа - HTML-документ, являющийся результатом выполнения CGI-программы.
CGI-программа передает результат своей работы (HTML-документ) серверу, который возвращает его клиенту. При этом сервер не анализирует и не изменяет полученные данные, он может только дополнять их некоторыми заголовками, содержащими общую информацию (например, текущая дата и время) и информацию о самом себе (например, имя и версия сервера). Информация о содержимом ответа формируется CGI-программой и должна содержать как минимум один заголовок, сообщающий браузеру формат возвращаемых данных:
Content-type: text/html
Замечание Информацию о заголовках можно найти в спецификации протокола HTTP. Мы же ограничимся еще одним примером. Если в качестве ответа клиенту посылается статический документ, например, подтверждение о получении заполненной формы, то неэффективно каждый раз создавать его заново. Лучше создать один раз и сохранить в файле. В этом случае CGI-сценарий вместо заголовка Content-type: media-type, описывающего формат данных, формирует заголовок Location: URL, указывающий серверу местонахождение документа, который следует передать клиенту.
Заголовки отделяются от содержимого документа пустой строкой.
Напишем простейший CGI-сценарий, посылающий пользователю HTML-страницу с приветствием
#!/usr/bin/perl
print "Content-type:text/html\n\n";
print "<html><head><title>HELLO</titlex/head>\n";
print "<body>\n";
print "<h2>Bac приветствует издательство ВХВ - Санкт-Петербург</п2>\п"; print "</bodyx/html>\n";
Если поместить файл hello.cgi в каталог CGI-программ Web-сервера, а затем обратиться к нему из браузера, то браузер отобразит HTML-документ, созданный программой hello.cgi (рис. 15.2).
Замечание Большинство Web-серверов по умолчанию предполагают, что файлы GGi-сце-нариев находятся в специальном каталоге, обычно называемом cgi-bin. Можно настроить сервер таким образом, чтобы все файлы, находящиеся в определенном каталоге, он воспринимал не как обычные документы, а как выполняемые сценарии. Можно также указать серверу, что все файлы с определенным расширением (например, .cgi) должны рассматриваться как CGI-сценарий. Когда пользователь открывает URL, ассоциированный с CGI-программой. клиент посылает запрос серверу, запрашивая файл. Сервер распознает, что запрошенный адрес является адресом CGI-программы, и пытается выполнить эту программу. Подробности конфигурирования Web-серверов можно найти в соответствующей литературе и документации на конкретный сервер.
Рис 15.2. Web-страница, сформированная программой hello.cgi
15.4.1. Переменные среды CGI
В зависимости от метода данные формы передаются в CGI-программу или через стандартный ввод (POST), или через переменную среды QUERY_STRING (GET). Помимо этих данных CGI-программе доступна и другая информация, поступившая от клиента в заголовках запроса или предоставленная Web-сервером. Эта информация сохраняется в переменных среды UNIX. С некоторыми из них мы уже познакомились ранее. В табл. 15.1 перечислены переменные, обычно используемые в CGI.
Таблица 15.1. Переменные среды CGI
Переменная среды
Описание
GATEWAYJNTERFACE
Версия CGI, которую использует сервер
SERVER_NAME
Доменное имя или IP-адрес сервера
SERVER_SOFTWARE
Имя и версия программы-сервера, отвечающей на запрос клиента (например, Apache 1 .3)
SERVER_PROTOCOL
Имя и версия информационного протокола, который был использован для запроса (например, HTTP 1 .0)
SERVER_PORT
Номер порта компьютера, на котором работает сервер (по умолчанию 80)
REQUEST_METHOD
Метод, использованный для выдачи запроса (GET, POST)
PATHJNFO
Дополнительная информация о пути
PATHJRANSLATED
Та же информация, что и в переменной PATHJNFO с префиксом, задающим путь к корневому каталогу дерева Web-документов
SCRIPT_NAME
Относительное маршрутное имя CGI-сценария (например, /cgi-bin/program.pl)
DOCUMENT_ROOT
Корневой каталог дерева Web-документов
QUERY_STRING
Строка запроса- информация, переданная в составе URi запроса после символа "?"
REMOTE_HOST
Имя удаленной машины, с которой сделан запрос
REMOTE_ADDR
IP-адрес удаленной машины, с которой сделан запрос
REMOTE_USER
Идентификационное имя пользователя, посылающего запрос
CONTENT_TYPE
Медиа-тип данных запроса, например, "text/html".
CONTENT_LENGTH
Количество байт в теле запроса, переданных в CGI-программу через стандартный ввод
HTTP_HOST
Хост-имя компьютера, на котором работает сервер
HTTP_FROM
Адрес электронной почты пользователя, направившего запрос
HTTP_ACCEPT
Список медиа-типов, которые может принимать клиент
HTTP_USER_AQENT
Браузер, которым клиент пользуется для выдачи запроса
HTTP_REFERER
URL документа, на который клиент указывал перед обращением к CGI-программе
Имена переменных среды CGI на разных Web-серверах могут различаться. Следует обратиться к документации на соответствующий сервер.
CGI-программа на языке Peri имеет доступ к переменным среды через специальный предопределенный хеш-массив %ENV, к элементам которого можно обратиться по ключу, совпадающему с именем переменной среды. Ниже приведены пример CGI-сценария, формирующего HTML-документ с информацией о всех установленных переменных среды, и отображение этого документа в окне браузера.
#!/usr/bin/perl
print "Content-type:text/html\n\n";
print "<html>\n";
print "<head><title>IlepeMeHHbie cpe№</titlex/head>\n";
print "<Ьоду><Ь2>Переменные среды</Ь2>\п";
print "<hrxpre>\n";
foreach $name (sort(keys %ENV))
{
print "$name: $ENV($name}\n";
}
print "<hrx/pre>\n";
print "</bodyx/html>\n";
Рис 15.3. Информация о переменных среды CGI
15.4.2. Обработка данных формы
Данные формы поступают в CGI-программу в закодированном виде, поэтому в качестве первого шага CGI-сценарий должен выполнить декодирование полученной информации. При пересылке данных методом GET данные формы присваиваются переменной среды QUERY_STRING, при передаче методом POST - передаются в программу через стандартный ввод и тоже могут быть присвоены некоторой внутренней переменной. Таким образом, декодирование данных сводится к следующей последовательности манипуляций со строкой:
замена каждой группы %hh, состоящей из шестнадцатеричного ASCII-кода hh с префиксом %, на соответствующий ASCII-символ;
замена символов "+" пробелами;
выделение отдельных пар имя=знанение> разделенных ограничителем &;
выделение из каждой пары имя=значение имени и значения соответствующего поля формы.
Программа декодирования HTML-формы может выглядеть, например, так:
#!/usr/bin/perl
# Декодирование данных формы, переданных методом GET $form_data = $ENV{'QUERY_STRING'};
# преобразование цепочек %hh в соответствующие символы $form_data-=- s/%(..)/pack ("С", hex ($1))/eg; i преобразование плюсов в пробелы $form_data =~ tr/+/ /;
# разбиение на пары имя=значение @pairs = split (/&/, $form_data);
# выделение из каждой пары имени и значения поля формы и сохранение
# их в ассоциативном массиве $fom_fields
foreach $pair (@pairs)
{
($name, $value)=split(/=/,$pair);
$ form_fields{$name}=$value; }
Если данные формы переданы методом POST, то в приведенном тексте следует заменить оператор присваивания
$form_data = $ENV{'QUERY_STRING'};
оператором
read(STDrN,$fom_data,$ENV{'CONTENT_LENGTH' }} ;
считывающим из стандартного ввода программы CONTENT_LENGTH байтов, составляющих содержимое запроса клиента, в переменную $form_data.
В приведенном примере используются две новые функции: packQ и hex(). Поясним их назначение прежде, чем перейти к обсуждению текста программы.
Функция
pack template, list
упаковывает список значений list в двоичную структуру по заданному шаблон у template.
Аргумент t
emplate представляет собой последовательность символов, определяющих формат представления пакуемых данных:
а/А Текстовая строка, заполненная нулями/пробелами
ь/в Двоичная строка, значения расположены в порядке возрастания/ убывания
с/с Обычное символьное значение/ Символьное значение без знака
f/d Значение в формате с плавающей точкой одинарной/двойной точности
ь/н Шестнадцатеричная строка, младший/старший полубайт первый
i/i Целое со знаком/ без знака
I/L Значение типа long со знаком/без знака
П/N Значение типа short/long с "сетевым" порядком байтов ("старший в старшем")
P/U Указатель на строку/Ш-кодированная строка s/s Значение типа short c$> знаком/без знака
v/v Значение типа short/long с VAX-порядком байтов ("старший в младшем")
х/х Нулевой байт/резервная копия байта
@ Заполнение нулевыми байтами (до абсолютной позиции)
За каждым символом может следовать число, обозначающее счетчик применений данного символа в качестве формата. Символ * в качестве счетчика означает применение данного формата для оставшейся части списка.
$х = pack "cccc", 80, .101, 114, 108; $х = pack "c4", 80, 101, 114, 108;
$х = pack "B32", "01010000011001010111001001101100";
$х = pack "H8", "5065726С";
$х = pack "H*", "5065726C"; '
$х = pack "сВ8Н2с",80,"01100101",12,108;
Значение переменной $х во всех случаях равно "peri". Функция
hex expr
Интерпретирует аргумент ехрг как шестнадцатеричную строку и возвращает ее десятичное значение.
В тексте программы примера 15.4 все представляется очевидным. Разберем только наиболее насыщенную строку
$fonn_data =~ s/%(..)/pack ("С", hex ($l))/eg;
Образец для поиска задан в виде регулярного выражения %(..). Этому образцу удовлетворяет произвольная последовательность вида %ху, где х, у - любые символы. В результате кодирования данных в качестве х, у могут появиться только шестнадцатеричные цифры, поэтому можно не задавать более точный, но менее компактный шаблон %([0-9A-Fa-f][0-9A-Fa-f]j. Часть выражения заключена в скобки (..). При нахождении подходящего фрагмента %hh его часть, содержащая шестнадцатеричное число hh, сохраняется в переменной, которая затем будет использована в качестве аргумента функции hex($ij для преобразования в десятичное значение. Функция pack упакует это десятичное значение в двоичную структуру, которая в соответствии с шаблоном "с" будет интерпретироваться как символ. Этот символ заменяет в тексте найденную цепочку %hh.
После выделения и декодирования данных можно приступить к их обработке. Попробуем написать CGI-сценарий, обрабатывающий данные формы из примера 15.1.
15.4.3. Пример создания собственного CGI-сценария
Программа должна декодировать полученные данные, проверять заполнение обязательных полей формы и правильность подтверждения пароля, в зависимости от результатов проверки формировать документ для отсылки клиенту. Сохраним сценарий в файле /cgi-bin/registrar, cgi. Полный маршрут к данному файлу определяется параметрами конфигурации Web-сервера. Местоположение каталога cgi-bin обычно указывается относительно корня дерева документов Web-сервера, а не корневого каталога файловой системы. Например, если корнем является каталог /home/httpd/htmi/, то файл сцена
рия будет иметь маршрутное ИМЯ /home/httpd/html/cgi-bin
/registrar.cgi, которое в запросе клиента будет указано как /cgi-bin /registrar.cgi. В первом приближении текст сценария может выглядеть следующим образом.
#!/usr/bin/perl
print "Content-type:text/html\n\n"; $method = $ENV{'REQUEST_METHOD'}; if ($method eq "GET") { ^
$form_data = $ENV{'QUERY_STRING'}; } else {
read (STDIN, $form_data, $ENV{'CONTENT_LENGTH'}); }
$form_data =~ s/%(..)/pack ("C", hex ($l))/eg; $form_data =~ tr/+/ /; @pairs = split (/&/, $form_data); foreach $pair (Spairs) {
($name, $value)=split(/=/,$pair);
$FORM{$name}=$value; /
1 ("
Шроверка заполнения обязательных полей
if (!$FORM{'regname'} I I !$FORM{'passwordl'}) { print «goback
<html>
<headxtitle>HencxiiHbie n.aHHbie</title></head>
<body><h2>M3BHHHTe, Вы пропустили обязательные данные</п2>
<br>
<а href="http://www.klf.ru/welcome.Мл11">Попробуйте еше раз, пожалуйста</а>
</body>
</html> goback ;}
#Проверка правильности ввода пароля elsif ($FORM{'passwordl1} eq $FORM{'password2'}){ print«confirmation <htral>
<headxtitle>no3flpaBnHeM!</titlex/head> <Ьойу><Ь2>Поздравляем! </h2xbr>
Ваша регистрация прошла успешно. Вы можете пользоваться нашей библиотекой. Спасибо за внимание.
</body>
</html>
confirmation
;}
else {
print«new_form
<htmlxhead><title>Oira6Ka при вводе napc«w</titlex/head>
<ЬойуХпЗ>Введенные Вами значения пароля не совпадают
<brxform method="get" action="/cgi-bin/registrar.cgi">
<pre>
Введите пароль: <input type="password" name="passwordl">
Подтвердите пароль: <input type="password" name="password2">
</pre>
new_form
foreach $key ( keys %FORM) {
if ($key ne "passwordl" && $key ne "password2") {
print "<input type=\"hidden\" name=$key value=$FORM{$key}>\n";
} } print «EndOfHTML
<br><br>
<input type="submit" value="OK"> <input type="reset" value="Отменить">
<i/form></body></html> EndOfHTML ;)
После вывода строки заголовка осуществляется считывание переданной серверу информации в переменную $form_data. В зависимости от метода передачи, эта информация считывается из переменной среды QUERY_STRING (метод GET) или из стандартного ввода программы (метод POST).
Считанная информация декодируется и помещается в ассоциативный массив %FORM.
Отсутствие обязательных данных - регистрационного имени и пароля - проверяется С ПОМОЩЬЮ УСЛОВИЯ if (! $FORM{' regname'} I I ! $FORM{ 'passwordl'}).
В случае отсутствия необходимых данных формируется виртуальный HTML-документ, предлагающий повторить попытку, который и посылается клиенту (рис. 15.4).
Рис 15.4. Ответ сервера в случае отсутствия обязательной информации
При выводе этого документа в операции print использована конструкция "документ здесь". Она позволяет использовать внутри себя символы, которые при заключении в обычные двойные кавычки необходимо маскировать символом "\", например, сами, двойные кавычки ", символы "@", -$", "%".
Условие elsif ($FORM{ 'passwordl1 } eq $FORM{ 'password2 ' }} предназначено
для проверки совпадения дву?с копий введенного пользователем пароля. Если значения совпадают, то пользователю посылается сообщение, подтверждающее успешную регистрацию (рис. 15.5).
Рис 15.5. Подтверждение регистрации
В противном случае формируется HTML-документ, предлагающий ввести пароль повторно (рис. 15.6). Этот новый документ содержит форму, в состав которой входят два видимых поля типа "password" - для ввода и подтверждения пароля, и скрытые поля типа "hidden" - для сохранения остальных данных, введенных при заполнении исходной формы. Каждое скрытое поле новой формы наследует у соответствующего поля исходной формы атрибуты name и value. Если эти данные не сохранить, то их придется вводить заново, принуждая пользователя повторно выполнять уже сделанную работу. Информация, сохраненная в скрытых полях, невидима пользователю и недоступна для изменения.
Рис 15.6. Повторное приглашение для ввода пароля
Культура Perl допускает различные уровни владения языком. В рассмотренном варианте использован минимальный набор средств. Очевидно, что часть кода, например, декодирование, требуется при обработке не только данной, но и любой другой формы. Естественным шагом в развитии исходного варианта сценария является выделение этой части в отдельную подпрограмму и предоставление доступа к ней другим сценариям. Для этого преобразуем исходный текст в соответствии с планом, изложенным в примере 15.7.
1. Часть исходного кода может быть использована другими CGI-программами. Преобразуем ее в отдельный модуль, сохраняемый в файле CGI_UTILS.pm.
package CGI_UTILS; require Exporter;
@ISA = qw(Exporter);
^EXPORT = qw(print_header process_input);
# Подпрограмма вывода заголовка ответа sub print_header {
print "Content-type: text/html\n\n"; } .
# Подпрограмма декодирования данных форьы sub process_input {
my ($form_ref)=(?_.;
my ($ form_data,@pai rs);
my ($temp)=""; /
if ($ENV{'REQUEST_METHOD'} eq 'POST') {
read(STDIN,$form_data,$ENV{'CONTENT_LENGTH'}); } else {
$form_data=$ENV{'QUERY_STRING'}; }
$form_data=~s/%(..)/pack("c",hex($1))/ge; $form_data=~tr/+/ /; $form_data=~s/\n/\0/g; @pairs=split(/&/,$form_data); foreach $item(@pairs) {
($name,$value)=spl£t (/=/,$item) ; if (!defined($forih_ref->{$name})) <
$ form_ref->{Sname}=$value; } else {
$form_ref->{$name} .= "\0$value"; } }
foreach $item (sort keys %$form_ref) { $temp.=$item."=".$form_ref->{$item}."&";} return($temp); } 1;
2. Текст основного сценария обработки формы registrar.cgi преобразуем следующим образом:
#!/usr/bin/perl use cgi_utils;
my 8FORM, $file_rec; $file_rec=&process_input(\%FORM); ^Проверка заполнения обязательных полей
#if {$FORM{'regname'} eq "" || $FORM{'passwordl'} eq "") { if (!$FORM{'regname'} || !$FORMTpasswordl'}) {
print "Location: /goback.html\n\n"; }
#Проверка правильности ввода пароля
elsif ($FORM{'passwordl'} eq $FORM{'password2'}}{
print "Location: /confirmation.html\n\n";
open (OUTF, "»users");
print OUTF $file_rec, H\n";
close OUTF }
else {
&print_header; print«new_form
<html>
<headxtitle>OtiM6Ka при вводе napontt</titlex/head>
<ЬойухпЗ>Введенные Вами значения пароля не совпадают
<br><forro method="get" action="/cgi-bin/registrar.cgi">
<pre>
Введите пароль: <input type="password" name="passwordl">
Подтвердите пароль: <input type="password" name="password2">
</pre> new_form / foreach $key ( keys %FORM) {
if ($key ne "passwordl" && $key ne "password2") {
print "<input type=\"hidden\'J name=$key value=$FORM{$key}>\n";
} } print«EndOfHTML
<brxbr>
<input type="submit" value="OK">
<input type="reset" value="OTMeHHTb">
</form>
</body>
</html> EndOfHTML
} exit
3. В исходном варианте сценария в качестве ответов сервера при получении неполных данных и для подтверждения регистрации пользователя формируются виртуальные HTML-документы. В этом нет необходимости, так как они содержат только статическую информацию. Соответствующие фрагменты сценария преобразуем в HTML-код ротовых документов, которые сохраним в отдельных файлах. В основном сценарии в качестве ответа сервера возвращаются ссылки на эти документы.
Файл confirmation.html содержит документ, посылаемый клиенту в качестве сообщения об успешной регистрации:
<html>
<head><title>Пoздpaвляeм!</title></head> <body><h2>Пoздpaвляeм! </h2xbr>
Ваша регистрация прошла успешно. Вы можете пользоваться нашей библиотекой. ..-
<br> /
Спасибо за внимание.
</body>
</html>
Файл goback.html содержит документ, посылаемый клиенту при получении неполных данных:
<html>
<head><title>Heпoлныe данные</^^1ех/пеай>
<body><h2>HsBHHMTe, Вы пропустили обязательные данные</Ь2>
<br>
<а href="http://www.klf.ru/welcome.Ылп1">Попробуйте еще раз,
4>пожалуйста</а>
</body>
</htral>
В приведенном тексте появились некоторые новые элементы, которые необходимо пояснить.
Подпрограмма process_input модуля cgi_utiis.pm передает декодированные данные через вызываемый по ссылке параметр - ассоциативный массив. [Кроме того, она возвращает при помощи функции return () те же данные,
но в виде строки, состоящей из пар имя=значение, разделенных символом "&". Обратите внимание на то, как подпрограмма вызывается в основной программе:
$file_rec=sprocess_input(\%FORM);
В качестве аргумента ей передается ссылка на ассоциативный массив. В тексте подпрограммы появилась проверка наличия полей формы с совпадающими именами и разными значениями:
if (!defined($form_ref->{$name})) {
$form_ref->{$name}=$value; }
else { }}
Этот фрагмент необходим для того, чтобы правильно обработать следующую ситуацию из нашего примера. Выбраны несколько переключателей, определяющих языки, которыми владеет пользователь: русский, английский, французский. Так как соответствующие элементы формы имеют одинаковые
имена name=language, TO без проверки В ассоциативный массив %fcrm_ref,
куда помещаются обработанные данные, попадет только информация от последнего обработанного Элемента name=language value=french. В Подобном случае обычное присваивание заменяется операцией присваивания с конкатенацией
$form_ref->{$name} .= "\0$value",
которая к переменной $fom_ref->{$name) добавляет нулевой символ и значение $value.
В основной программе registrar.cgi обратим внимание на то, как передается ссылка на готовый HTML-документ. Для этого вместо заголовка content-type: text/html выводится заголовок Location: URL, сообщающий серверу адрес документа.
Еще один новый элемент в основной программе - сохранение данных в файле с именем users.
15.4.4. Модуль CGI.pm
Пример, рассмотренный выше, демонстрирует наивный подход, когда кажется, что все необходимые программы надо писать самостоятельно с самого начала. Но программирование CGI - это такая область, в которой Per] давно и активно применяется, и многое из того, что может потребоваться, уже давно кем-то написано. Надо только найти и использовать. В. данном разделе мы сделаем краткий обзор одного из таких готовых средств, предназначенных для поддержки разработки CGI-приложений.
Модуль CGI.pm, созданный Линкольном Штейном, входит в состав дистрибутивного комплекта Perl, начиная с версии 5.004, и его даже не нужно специально инсталлировать.
Этот модуль содержит большой набор функций для создания и обработки HTML-форм. Мы посвятили значительную часть предыдущего раздела изучению многочисленных тэгов, чтобы затем написать HTML-код для создания формы в примере 15.1. Модуль CGI позволяет сделать то же самое, но без использования HTML. С его помощью можно описать форму на языке Perl, используя вместо тэгов обращения к функциям модуля. В результате получится не документ HTML, а сценарий на языке Perl, который при вызове будет "на лету" генерировать HTML-форму и передавать серверу для отправки клиенту.
Модуль CGI является не просто модулем, а классом, что позволяет использовать преимущества объектно-ориентированного подхода. Модуль предоставляет пользователю на выбор два вида интерфейса с самим собой: процедурно-ориентированный и объектно-ориентированный.
При использовании процедурно-ориентированного способа работы с модулем CGI функции модуля нужно явным образом импортировать в пространство имен вызывающей программы, а затем обращаться к ним как обычно. В этом случае в вызывающей программе должны быть строки, аналогичные следующим:
#!/usr/bin/perl ; use CGI qw/:standard/; \ print header(), \
start_html('Пример формы'),
hi('Пример формы'),
Директива use импортирует в пространство имен вызывающей программы некоторый стандартный набор функций. Помимо него, существуют другие наборы функций модуля CGI. Их можно импортировать, указав имя соответствующего набора в списке импорта директивы use. Имена всех наборов можно просмотреть в файле CGI.pm, где они содержатся в хеш-массиве
%EXPORT_TAGS,
Функции header (), start_html О , hi () ЯВЛЯЮТСЯ функциями модуля CGI. Они будут рассмотрены ниже.
При использовании объектно-ориентированного интерфейса в директиве use вызывающей программы не нужно указывать список импортируемых имен функций. В этом случае взаимодействие с модулем CGI осуществляется через объект класса CGI, который нужно создать в вызывающей программе при помощи конструктора new (). Объектно-ориентированный вариант приведенного выше фрагмента выглядит следующим образом:
#!/usr/bin/perl
use CGI;
Squery = new CGI;
print $query->header(),
$query->start_html {'Пример формы'),
$query->hl ('Пример формы1) ,
Замечание Функции модуля CGI.pm являются методами класса CGI. Для того чтобы их можно было вызывать и как функции, и как методы, синтаксис не требует в качестве обязательного первого параметра указывать объект класса CGI. Поэтому в качестве функций к ним можно обращаться обычным образом, а как к объектам - только используя форму $object->method ().
Модуль CGI, как мы отметили выше, содержит большой набор методов, и в наши планы не входит их подробное изучение. Документация, входящая в состав самого модуля, достаточно подробно описывает его компоненты. Чтобы получить представление о работе модуля CGI, создадим с его помощью небольшой сценарий. Для этого вернемся к рассмотрению формы из примера 15.1.
Будем для определенности использовать традиционный процедурно-ориентированный интерфейс. Рассмотрим следующий сценарий.
#!/usr/bin/perl
use CGI qw(:standard);
print header;
print start_html('Пример формы'),
h2('Регистрационная страница Клуба любителей фантастики'),
'Заполнив анкету, вы сможете пользоваться нашей электронной
"^библиотекой.',
Ьг,
start_form,
"Введите регистрационное имя:",textfield('regname'),
P.
"Введите пароль: ", password_field(-name=>'passwordl',
-maxlength=>'8'),
Р,
"Подтвердите пароль: ", password_field(-name=>'password2',
-maxlength=>'8'),
Р/
"Ваш возраст",
Р'
radio_group(-name=>'age',
-value=>['lt20','20_30', '30_50', 'gt50'],
-default=>'Lt20',
-labels=>{'It20'=>'flo 20',I20_30'=>'20-30', 4> '30_50'=>'30-50','gt50'=>'старше 50'}),
br,br,
"На каких языках читаете:",
checkbox_group(-name=>'language', 4> -values=>
4>
['русский', 'английский1,'французский', 'немецкий'],
^ -defaults=>['русский']), br,br,
"Какой формат данных является для Вас предпочтительным ", Ьг, popup_menu(-name=>'type',
-values=>['Plain text','PostScript','РОГ']), br,br, \
N
"Ваши любимые авторы:", x-~-_.
Ьг„
textarea(-name=>'wish', -cols=>40, -rpws=>3),
br,
subrai t{-name=>'OK'), reset{-name=>'Отменить'),
end_form,
hr;
if (paramO) { print
"Ваше имя: ",em(param('regname')),
P,
"Ваш возраст: ", em(param('age')),
P, '
J
"Вычитаете на языках: ",em(join(", ",param('language'))),
P,
"Предпочтительный формат данных для Вас: ",em(param ('type')),
P,
"Ваши любимые авторы: ", em(join(", ",param('wish1))),
hr; } print end_html;
Обсудим приведенный текст. Директива use, как мы отметили выше, осуществляет импорт стандартного набора функций модуля CGI.pm в пространство имен вызывающего пакета. В самом сценарии на месте тэгов исходного HTML-кода стоят обращения к функциям модуля: каждому тэгу соответствует вызов функции. Вызов функции модуля CGI можно осуществлять двумя способами: с использованием позиционных параметров print textfield('regname','начальное значение',50,80); с использованием именованных параметров
print textfield(-name=>'regname',
-default=>'начальное значение",
-size=>50,
-maxlength=>80);
Обработка позиционного параметра внутри функции зависит от его места в списке параметров. Обработка именованного параметра не зависит от его места в списке параметров. Функции модуля CGI могут иметь большое число параметров, порядок следования которых трудно запомнить, поэтому в этом модуле была реализована возможность вызова функций с именованными параметрами. Кроме того, применение именованных параметров делает текст программы более понятным. В тексте примера функции вызываются с именованными параметрами, если параметров больше одного. Познакомимся с функциями, использованными в примере.
Функция header о без параметров создает для виртуального ответа сервера стандартный HTTP-заголовок Content-Type: text/html и вставляет после него необходимую пустую строку. Параметры позволяют задать дополнительную информацию для заголовка, например, указать другой медиа-тип содержимого или код ответа, посылаемый браузеру:
print header(-type=>'image/gif',
-status=>'404 Not Found1);
Функция start_htmi () создает HTML-заголовок и начальную часть документа, включая открывающий тэг <вооу>. При помощи параметров функции можно задать дополнительные тэги внутри тэга <HEAD>, а также значения атрибутов. Все параметры являются необязательными. В примере функция start_htmio вызвана с одним позиционным параметром, определяющим название документа.
Модуль CGI содержит методы (функции) для поддержки многих тэгов HTML2, HTML3, HTML4 и расширений, используемых в браузерах Netscape. Тэгам соответствуют одноименные методы модуля CGI.pm, имена которых записываются при помощи символов нижнего регистра. Если при этом возникают конфликты имен, в названия методов следует вводить символы верхнего регистра, как, например, в следующих случаях.
1. Название тэга <TR> совпадает с именем встроенной функции tr(). Имя соответствующего метода записывать в виде TR () или тг ().
2. Название тэга <PARAM> совпадает с именем собственного метода модуля CGI paramo. Для обозначения метода, соответствующего тэгу, использовать ИМЯ РАКАМ ().
3. Название тэга <SELECT> совпадает с именем встроенной функции select (). Для обозначения метода использовать имя Select ().
4. Название тэга <зив> совпадает с именем ключевого слова объявления функции sub. Для обозначения метода использовать имя sub ().
Тэгам, имеющим атрибуты, соответствуют методы, имеющие в качестве первого аргумента ссылку на анонимный хеш-массив. Ключами этого хеш-массива являются имена атрибутов тэга, а значениями - значения атрибутов.
Методы, соответствующие тэгам, и методы, предназначенные длячгенерирова-ния других элементов HTML-документа, возвращают строки, содержащие соответствующие элементы. Чтобы эти строки попали в создаваемый документ, их нужно вывести, как это делается в примере при помощи функции print.
В примере использованы следующие методы, соответствующие тэгам HTML.
Функция Н2 соответствует тэгу <Н2>. Она определяет, что ее аргумент является в документе заголовком второго уровня.
Функция Ьг соответствует тэгу <BR> и обозначает, что последующий текст размещается с начала новой строки.
Функция р соответствует тэгу <р> и обозначает начало абзаца.
Функция hr соответствует тэгу <т> и обозначает горизонтальную линию, разделяющую документ на части.
Функция em соответствует тэгу <ет> и обозначает, что ее аргумент в документе должен быть выделен курсивом.
Следующие функции используются для создания формы и ее элементов.
Функция start_form соответствует тэгу <FORM>. Она может иметь три параметра start_form(-method=>$method,
-action=>$action,
-encoding=>$encoding);
при помощи которых можно задать метод передачи формы Web-серверу (-method), программу, предназначенную для обработки формы (-action), и способ кодирования данных (-encoding). Все параметры являются необязательными. По умолчанию используются значения
method: POST;
action: данный сценарий;
encoding: application/x-www-form-urlencoded. П Функция end_form создает закрывающий тэг </FORM>.
Функция textfieid соответствует тэгу <INPUTE TYPE=TEXT>. Она имеет следующий синтаксис
textfieid (-name=>' field_name',
-default=>'starting value',
-size=>50,
-maxlength=>80);
Параметры соответствую^ атрибутам тэга. Обязательным является первый параметр. /
х. _ _ _^/
Функция password_fieid соответствует тэгу <INPUTE TYPE=PASSWORD>. Ее синтаксис:
password_field(-name=>'secret',
-value=>'starting value',
-size=>8,
-maxlength=>12);
Параметры имеют тот же смысл, что и одноименные атрибуты соответствующего тэга. Обязательным является первый параметр.
Функция radio_group служит для создания группы "радиокнопок" - элементов, задаваемых тэгом <INPUTE TYPE=RADIO>. Ее синтаксис имеет следующую форму
radio_group(-name=>'group_name',
-values=>['bim',Jbam','bom'],
-default=>'bom',
-linebreak=>'true',
-lab.els=>\%labels) ;
Первый аргумент является обязательным, соответствует одноименному атрибуту тэга. Второй аргумент тоже обязательный и задает значения элементов. Эти значения отображаются в качестве названий кнопок. Он должен быть ссылкой на массив. Остальные аргументы являются необязательными. Третий аргумент задает кнопку, которая выбрана по умолчанию. Если значение четвертого аргумента 'true1, каждая следующая кнопка группы размещается в начале новой строки. Пятым аргументом является ссылка на хеш-массив, который связывает значения, присвоенные кнопкам, с метками, которые отображаются в виде названий кнопок. Если аргумент не задан, то в качестве названий отображаются сами значения.
Функция checkbox_group служит для создания группы элементов-переключателей, задаваемых тэгом <INPUTE TYPE= CHECKBOX>.
checkbox_group(-name=>'group_name',
-values=>['bim','bam','bom'],
-default=>['bim','bom'],
-linebreak=>'true',
-labels=>\%labels);
Аргументы имеют тот же смысл, что и одноименные аргументы функции radio_group. Поскольку в группе переключателей можно одновременно выбрать несколько элементов, третий аргумент может быть или одиночным элементом, или ссылкой на массив, содержащий список значений, выбранных по умолчанию. Обязательными являются первый и второй аргументы.
Функция popup_menu служит для создания меню, задаваемого при помощи тэга <SELECT>. Имеет следующий синтаксис:
popup_menu(-name=>'menu_name',
-values=>['bim', 'bam','bom'],
-default=>'bomr,
-labels=>\%labels);
Первый аргумент задает имя меню. Второй аргумент является ссылкой на массив, содержащий список значений, присвоенных элементам меню. Первый и второй аргументы обязательны, остальные - нет. Третий аргумент задает элемент меню, выбранный по умолчанию. Четвертый1 аргумент является ссылкой на хеш-массив. Хеш-массив значению каждого элемента меню ставит в соответствие строку, которая будет отображаться в меню для этого элемента. Если четвертый аргумент отсутствует, то для каждого элемента меню отображается его значение, заданное вторым аргументом.
Функция textarea соответствует тэгу <TEXTAREA>, задающему в документе текстовое поле для ввода многострочного текста. Имеет следующий синтаксис
textarea(-name=>'region',
-default=>'starting value',
-rows=>10,
-columns=>50);
Первый параметр, задающий имя элемента формы <ТЕХТДКЕА>, является обязательным, остальные - нет. Второй параметр задает строку, отображаемую по умолчанию. Третий и четвертый параметры задают соответственно число строк и столбцов, отображаемых в текстовом поле.
Функция submit соответствует тэгу <INPUT TYPE=SUBMIT>, задающему кнопку передачи. Ее синтаксис:
print $query->submit(-name=>'button_name', -value=>'value');
Первый параметр является необязательным. Он задает имя кнопки, которое отображается в качестве ее названия. Нужен только для переопределения названия Submit и в тех случаях, когда надо различать несколько имеющихся кнопок передачи. Второй параметр тоже необязательный. Он задает значение, которое посылается в строке запроса при щелчке на этой кнопке.
Функция reset соответствует тэгу <INPUT TYPE=RESET>, задающему кнопку сброса. Может иметь параметр, переопределяющий название Reset, отображаемое по умолчанию.
Функция end_htmi завершает HTML-документ, добавляя тэги </BODY>
</HTML>.
Пример 15.8 содержит также код, который не связан с созданием формы Он состоит из одного условного оператора, в котором в качестве условия используется значение, возвращаемое функцией paramo. Эта функция используется также внутри блока условного оператора. Разберем для чего она применяется. При помощи функции paramo модуля CGI можно выполнить следующие действия.
Получение списка имен параметров, переданных сценарию.
Если сценарию переданы параметры в виде списка пар "имя=значение" функция paramo без аргументов возвращает список имен параметров сценария:
@names = param;
Получение значений именованных параметров.
Функция paramo с единственным аргументом - именем параметра, возвращает значение этого параметра. Если параметру соответствует несколько значений, функция param о возвращает список этих значений:'
@values = param('language');
в противном случае - одно значение:
$value = param('regname');
Задание значений параметров.
param(-name => 'language', -values => ['russian', 'english', 'french']);
Можно задавать значения параметров, используя вызов функции param о в форме с позиционными параметрами, но тогда нужно знать порядок следования этих параметров:
param ('language', 'russian', 'english', 'french');
При помощи функции param о можно устанавливать начальные значения элементов формы или изменять ранее установленные значения.
Часть сценария, предшествующая условному оператору, предназначена для создания формы из примера 15.1. Заключительная часть, состоящая из условного оператора, обрабатывает заполненную и отправленную Web-серверу форму. Это происходит потому, что по умолчанию приложением, обрабатывающим форму, является данный сценарий (см. описание start_form). Таким образом, в одном сценарии содержится код, и создающий форму, и ее обрабатывающий.
Сохраним код, приведенный в примере 15.8, в файле welcome.cgi. Этот файл можно поместить на Web-сервере в стандартный каталог cgi-bin, предназначенный для хранения CGI-сценариев. Предположим, что Web-сервер имеет Internet-адрес www.klf.ru. Если из удаленного браузера послать запрос по адресу http://www.klf.ru/cgi-bin/welcome.cgi, то Web-сервер, получив запрос, выполнит сценарий welcome, cgi. Сценарий "на лету" создаст HTML-документ, содержащий форму, и передаст его Web-серверу, который отправит документ браузеру. Браузер, получив документ, отобразит его.
После заполнения формы и нажатия кнопки ОК данные формы будут вновь отправлены Web-серверу, который передаст их для обработки все тому же сценарию welcome.cgi. Сценарий "на лету" создаст новый HTML-документ с учетом полученных данных и через сервер направит его браузеру. Браузер отобразит новый документ.
Сценарий welcome.cgi можно передать для выполнения интерпретатору peri, а результат вывести в файл, чтобы посмотреть, как вызовы функций модуля CGI преобразуются в тэги HTML-документа. Документ HTML, созданный сценарием welcome.cgi, имеет следующий вид.