Язык Perl и CGI-программирование


Основные понятия


Основу "всемирной паутины" WWW составляют Web-узлы. Это компьютеры, на которых выполняется специальная программа — Web-сервер, ожидающая запроса со стороны клиента на выдачу документа. Документы сохраняются на Web-узле, как правило, в формате HTML. Клиентом Web-сервера является программа-браузер, выполняющаяся на удаленном компьютере, которая осуществляет запрос к Web-серверу, принимает запрошенный документ и отображает его на экране.
Аббревиатура CGI (Common Gateway Interface) обозначает часть Web-сервера, которая может взаимодействовать с другими программами, выполняющимися на этом же Web-узле. В этом смысле она является шлюзом (gateway — шлюз) для передачи данных, полученных от клиента, программам обработки — таким, как СУБД, электронные таблицы и др. CGI включает общую среду (набор переменных) и протоколы для взаимодействия с этими программами.
Общая схема работы CGI состоит из следующих элементов.
1. Получение Web-сервером информации от клиента-браузера. Для передачи данных Web-серверу в языке HTML имеется средство, называемое форма. Форма задается в HTML-документе при помощи тэгов <FORM>. . .</FORM> и состоит из набора полей ввода, отображаемых браузером в виде графических элементов управления: селекторных кнопок, опций, строк ввода текста, управляющих кнопок и т. д.
2. Анализ и обработка полученной информации. Данные, извлеченные из HTML-формы, передаются для обработки CGI-программе. Они не всегда могут быть обработаны CGI-программой самостоятельно. Например, они могут содержать запрос к некоторой базе данных, которую CGI-программа читать "не умеет". В этом случае CGI-программа на основании полученной информации формирует запрос к компетентной программе, выполняющейся на том же компьютере. CGI-программа может быть написана на любом языке программирования, имеющем средства обмена данными между программами. В среде UNIX для этой цели наиболее часто используется язык Perl. Так как UNIX является наиболее популяр-
ной операционной системой для Web-серверов, то можно считать Perl наиболее популярным языком CGI-программирования. Программа на языке Perl представляет собой последовательность операторов, которые интерпретатор языка выполняет при каждом запуске без преобразования исходного текста программы в выполняемый двоичный код. По этой причине CGI-программы называют также CGI-сценариями или CGI-скрип-тами.

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

HTML-формы
HTML-формы предназначены для пересылки данных от удаленного пользователя к Web-серверу. С их помощью можно организовать простейший диалог между пользователем и сервером (например, регистрацию пользователя на сервере или выбор нужного документа из представленного списка). Формы поддерживаются всеми популярными браузерами.
Мы предполагаем, что читатель знаком с основами языка HTML и структурой HTML-документа. В этом разделе рассмотрены средства HTML, используемые для создания форм. В описании тэгов приведены только наиболее употребительные атрибуты и опущены атрибуты, специфические для отдельных браузеров.  
Тэг <FORM>
<FORM ACTION="URL" МЕТНОО=метод_передачи ЕНСТУРЕ=М1МЕ-!гип>
содержание_формы
</FORM>
В HTML-документе для задания формы используются тэги <FORM>. . .</FORM>, отмечающие, соответственно, начало и конец формы. Документ может содержать несколько форм, но они не могут быть вложены одна в другую. Тэг <FORM> имеет атрибуты ACTION, METHOD и ENCTYPE. Отдельные браузеры (Netscape, Internet Explorer) поддерживают дополнительные атрибуты помимо стандартных, например, CLASS, NAME, STYLE и др.
Атрибут ACTION — единственный обязательный. Его значением является адрес (URL) CGI-программы, которая будет обрабатывать информацию, извлеченную из данной формы.
Атрибут METHOD определяет метод пересылки данйых, содержащихся в форме, от браузера к Web-серверу. Он может принимать два значения; GET (по умолчанию) и POST.

При использовании метода GET данные формы пересылаются в составе URL запроса, к которому присоединяются после символа "?" в виде совокупности пар переменная=значение, разделенных символом "&". В этом случае первая строка запроса может иметь следующий вид:
После выделения данных из URL сервер присваивает их переменной среды QUERY_STRING, которая может быть использована CGI-программой.
При использовании метода POST данные формы пересылаются Web-серверу в теле запроса, после чего передаются сервером в CGI-программу через стандартный ввод.
Значением атрибута ENCTYPE является медиа-тип, определяющий формат кодирования данных при передаче их от браузера к серверу. Браузер кодирует данные, чтобы избежать их искажения в процессе передачи. Возможны два значения этого атрибута: appiication/x-wuw-form-uriencoded, используемое ПО умолчанию, И multipart/form-data.

Как видно из примера на рис. 15.1, форма отображается в окне браузера в виде набора стандартных элементов управления, используемых для заполнения полей формы значениями, которые затем передаются Web-серверу. Значение вводится в поле ввода пользователем или назначается по умолчанию. Для создания полей средствами языка HTML существуют специальные тэги:
<INPUT>, <SELECT>, <TEXTAREA>, которые могут употребляться только внутри
тэга <FORM>.  
Тэг <INPUT>
<INPUT ТУРЕ=тип_лоля_ввода ЫАМЕ=имя_лоля_ввода другие_атрибуты>
Наиболее употребительный тэг, с помощью которого можно генерировать внутри формы поля для ввода строки текста, пароля, имени файла, различные кнопки. Он имеет два обязательных атрибута: TYPE и NAME. Атрибут TYPE определяет тип поля: селекторная кнопка, кнопка передачи и др. Атрибут NAME определяет имя, присваиваемое полю. Оно не отображается браузером, а используется в качестве идентификатора значения, передаваемого Web-серверу. Остальные атрибуты меняются в зависимости от тира поля. Ниже приведено описание типов полей, создаваемых при помощи тэга <INPUT>, и порождаемых ими элементов ввода.

  • TYPE=TEXT Создает элемент для ввода строки текста. Дополнительные атрибуты:
  • MAXLENGTH=n Задает максимальное количество символов, разрешенных в текстовом поле. По умолчанию не ограничено,
  • SIZE=n Максимальное количество отображаемых символов.
  • УАЫ1Е=начальное_значение

Первоначальное значение текстового поля.

  • TYPE=PASSWORD

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

  • TYPE=FILE

Создает поле для ввода имени локального файла, сопровождаемое кнопкой Browse. Выбранный файл присоединяется к содержимому формы при пересылке на сервер. Имя файла можно ввести непосредственно, или, воспользовавшись кнопкой Browse, выбрать его из диалогового окна, отображающего список локальных файлов. Для корректной передачи присоединенного файла следует установить значения атрибутов формы равными ENCTYPE="multipart/form-data" И METHOD=POST. В Противном случае будет передана введенная строка, то есть маршрутное имя файла, а не его содержимое. Дополнительные атрибуты MAXLENGTH и SIZE имеют тот же смысл, что и для элементов типа TEXT и PASSWORD.

  • TYPE=CHECKBOX Создает элемент-переключатель, принимающий всего два значения (on/off, вкл./выкл., истина/ложь) и отображаемый в виде квадратной кнопки. Элементы-переключатели CHECKBOX можно объединить в группу, установив одинаковое значение атрибута NAME для всех ее элементов. Дополнительные атрибуты:
  • УАШЕ=строка Значение, которое будет передано серверу, если данная кнопка выбрана. Если кнопка не выбрана, значение не передается. Обязательный атрибут.
  • CHECKED Если указан атрибут CHECKED, элемент является выбранным по умолчанию.

Если переключатели образуют группу, то передаваемым значением является строка разделенных запятыми значений атрибута VALUE всех выбранных элементов.

  • TYPE=RADIO Создает элемент "радиокнопка", существующий только в составе группы подобных элементов, из которых может быть выбран только один. Все элементы группы должны иметь одинаковое значение атрибута NAME. Отображается в виде круглой кнопки. Дополнительные атрибуты:
  • VALUE=Cтpoкa Обязательный атрибут, значение которого передается серверу при выборе данной кнопки. Должен иметь уникальное значение для каждого члена группы.
  • CHECKED Устанавливает элемент выбранным по умолчанию. Один и только один элемент в группе должен иметь этот атрибут.
  • TYPE=SUBMIT Создает кнопку передачи, нажатие которой вызывает пересылку на сервер всего содержимого формы. По умолчанию отображается в виде прямоугольной кнопки с надписью Submit. Дополнительный атрибут
  • УАШЕ=яазвание_кнолки позволяет изменить надпись на кнопке. Атрибут NAME для данного элемента может быть опущен. В этом случае значение кнопки не включается в список параметров формы и не передается на сервер. Если атрибуты
  • NAME И VALUE Присутствуют, Например,

<INPUT TYPE=SUBMIT NAME="submit_button" VALUE="OK">,
то в список параметров формы, передаваемых на сервер, включается параметр submit_button="OK". Внутри формы могут существовать несколько кнопок передачи.

  • TYPE=RESET Создает кнопку сброса, нажатие которой отменяет все сделанные изменения, восстанавливая значения полей формы на тот момент, когда она была загружена. По умолчанию отображается в виде прямоугольной кнопки с надписью Reset. Надпись можно изменить при помощи дополнительного атрибута
  • ЧКШЕ.=название_кнопки Значение кнопки Reset никогда не пересылается на сервер, поэтому у нее отсутствует атрибут NAME.
  • TYPE=IMAGE Создает элемент в виде графического изображения, действующий аналогично кнопке Submit. Дополнительные атрибуты:
  • ЗКС=иг!_изображения Задает ссылку (url) на файл с графическим изображением элемента.
  • КЬТ.СП=тип_въ1равнивания Задает тип выравнивания изображения относительно текущей строки текста точно так же, как одноименный атрибут тэга <IMG>.

Если на изображении элемента щелкнуть мышью, то координаты указателя мыши в виде NAME.x=n&NAME.y=m включаются браузером в список параметров формы, посылаемых на сервер.

  • TYPE=HIDDEN Создает скрытый элемент, не отображаемый пользователю. Информация, хранящаяся в скрытом поле, всегда пересылается на сервер и не может быть изменена ни пользователем, ни браузером. Скрытое поле можно использовать, например, в следующем случае. Пользователь заполняет форму и отправляет ее серверу. Сервер посылает пользователю для заполнения вторую форму, которая частично использует информацию, содержащуюся в первой форме. Сервер не хранит историю диалога с пользователем. Он обрабатывает каждый запрос независимо и при получении второй формы не будет знать, как она связана с первой. Чтобы повторно не вводить уже введенную информацию, можно заставить CGI-програм-му, обрабатывающую первую форму, переносить необходимые данные в скрытые поля второй формы. Они не будут видимы пользователем и, в то же время, доступны серверу. Значение скрытого поля определяется атрибутом VALUE.  

Тэг <SELECT>
<SELECT NAME=HM*_n<afl# SIZE=n MULTIPLE>
элементы OPTION </SELECT>
Тэг <SELECT> предназначен для того, чтобы организовать внутри формы выбор из нескольких вариантов без применения элементов ввода типа CHECKBOX и RADIO. Дело в том, что если элементов выбора много, то представление их в виде переключателей и радиокнопок увеличивает размеры формы, делая ее труднообозримой. С помощью тэга <SELECT> варианты выбора более компактно представляются в окне браузера в виде элементов ниспадающего меню или списка прокрутки. Тэг имеет следующие атрибуты.

  • NAME= строка Обязательный атрибут. При выборе одного или нескольких элементов формируется список выбранных значений, который передается на сервер под именем NAME.
  • SIZE=n Устанавливает число одновременно видимых элементов выбора. Если п=1, то отображается ниспадающее менй, если п>1, то список прокрутки с п одновременно видимыми элементами.
  • MULTIPLE Означает, что из меню или списка можно выбрать одновременно несколько элементов. Если этот атрибут задан, то список выбора ведет себя как группа переключателей CHECKBOX, если не задан — как группа радиокнопок RADIO.

Элементы меню задаются внутри тэга <SELECT> при помощи тэга <OPTION>:
<OPTION SELECTED УАШЕ=строка>содержимое_тэга</ОРТION>
Закрывающий тэг </OPTION> не используется. Атрибут VALUE содержит значение, которое пересылается серверу, если данный элемент выбран из меню или списка. Если значение этого атрибута не задано, то по умолчанию оно устанавливается равным содержимому тэга <OPTION>. Например, элементы
<OPTION VALUE=Red>Red <OPTION>Red
имеют одно значение Red. В первом случае оно установлено явно при помощи атрибута VALUE, во втором — по умолчанию. Атрибут SELECTED изначально отображает элемент как выбранный.  
Тэг <TEXTAREA>
<TEXTAREA НАМЕ=имя ROWS=m COLS=n>
текст
</TEXTAREA>
Создает внутри формы поле для ввода многострочного текста, отображаемое в окне браузера в виде прямоугольной области с горизонтальной и вертикальной полосами прокрутки. Для пересылки на сервер каждая введенная строка дополняется символами %OD%OA (ASCII-символы "Возврат каретки" и "Перевод строки" с предшествующим символом %), полученные строки объединяются в одну строку, которая и отправляется на сервер под именем, задаваемым атрибутом NAME. Атрибуты:

  • NAME ' __ Необходимый атрибут, используемый для идентификации данных при пересылке на сервер.
  • COLS=n Задает число столбцов видимого текста.
  • ROWS=n Задает число строк видимого текста.

Между тэгами <textarea> и </textarea> можно поместить текст, который будет отображаться по умолчанию.
Пример формы
Ниже представлен пример формы, включающей набор характерных полей и HTML-код, использованный для ее создания.
<htmlxheadxtitle>пpимep формы</^д^1ех/пеаЗ>
<body>
<b2>Регистрационная страница Клуба любителей фантастики</b2>
Заполнив анкету, вы сможете пользоваться нашей электронной библиотекой.
<br>
<form method="get" action="/cgi-bin/registrar.cgi">
<pre>
Введите регистрационное имя: <input type="text" name="regnarae">
Введите пароль: <input type="password" name="passwordl" max-length=8>
Подтвердите пароль: <input type="password" name="password2" max-length=8>
</pre>
Ваш возраст:
<input type="radio" name="age" value="lt20" checked >До 20
<input type="radio" name="age" value="20_30">20-30
<input type="radio" name="age" value="30_50">30-50
<input type="radio" name="age" value="gt50">CTapiue 50
<brxbr>
На каких языках читаете:
<input type="checkbox" name="language" value="russian" checked>pycciorii
<input type="checkbox" name="language" ^а1ие="english">английский
<input type="checkbox" name="language" Уа1ие="£гепсЬ">французский
<input type="checkbox" name="language" value="germnan">немецкий
<brxbr>
Какой формат данных является для Вас предпочтительным
<br>
<select name="format" size=2 >
<option selected value="HTML">HTML
<option value="Plain text">Plain text
<option value="PostScript">PostScript
<option value="PDF">PDF </select> <brxbr>
Ваши любимые авторы: <br> <textarea name="wish" cols=40 rows=3>
</textarea> <brxbr>
<input type="submit" value="OK"> <input type="reset" уа!ие="0тменить">
</form>
</body> \
</html>
Данная форма содержит:

  • текстовое поле для ввода регистрационного имени пользователя;
  • текстовое поле для ввода пароля, отображаемого в окне символами *;
  • текстовое поле для подтверждения пароля, также отображаемого символами *;
  • группу радиокнопок для указания возраста пользователя (единственный выбор);
  • группу переключателей для указания языков, которыми владеет пользователь (множественный выбор);
  • список прокрутки для указания предпочтительного формата данных (выбор из ограниченного списка);
  • блок ввода многострочного текста для перечисления любимых авторов (неизвестное заранее количество строк);
  • кнопку передачи с меткой ОК (у этого элемента отсутствует атрибут NAME, он не нужен, так как в данном примере всего одна кнопка передачи, а, значит, CGI-программе нет необходимости определять, от какой именно кнопки поступила команда передачи данных);
  • кнопку сброса с меткой Отменить.

Итак, пользователь заполнил форму и щелкнул кнопку передачи Submit. Дальнейшее прохождение данных выглядит следующим образом.
1. Информация кодируется и пересылается на Web-сервер, который передает ее для обработки CGI-программе.
2. CGI-программа обрабатывает полученные данные, возможно, обращаясь за помощью к другим программам, выполняющимся на том же компьютере, и генерирует новый "виртуальный" HTML-документ, либо определяет ссылку на уже имеющийся.
3. Новый HTML-документ или ссылка передаются CGI-Программой Web-серверу для возврата клиенту.
Рассмотрим эти шаги более подробно.
Передача информации CGI-программе
Как мы уже знаем, существуют два метода кодирования информации, содержащейся в форме: стандартный метод application/x-www-form-urlencoded, используемый по умолчанию, и дополнительный multipart/form-data. Второй метод нужен только в том случае, если к содержимому формы присоединяется локальный файл, выбранный при помощи элемента формы < INPUT TYPE=FILE>. В остальных случаях следует использовать метод кодирования по умолчанию.
Схема кодирования application/x-www-form-urlencoded одинакова для обоих методов пересылки GET и POST и заключается в следующем.
Для каждого элемента формы, имеющего имя, заданное атрибутом МИМЕ, формируется пара "name=value", где value — значение элемента, введенное пользователем или назначенное по умолчанию. Если значение отсутствует, соответствующая пара имеет вид "пате—'. Для радиокнопок и переключателей используются значения только выбранных элементов. Если элемент выбран, а значение атрибута VALUE не определено, по умолчанию используется значение "ON".
Все пары объединяются в строку, в качестве разделителя служит символ &. Так как имена и значения представляют собой обычный текст, то они могут содержать символы, недопустимые в составе URL (метод GET пересылает данные как часть URL). Такие символы заменяются последовательностью, состоящей из символа % и их шестнадцатеричного ASCII-кода. Символ пробела может заменяться не только кодом %20, но и знаком + (плюс). Признак конца строки, встречающийся в поле TEXTAREA, заменяется кодом %OD%OA. Такое кодирование называется URL-кодированием.
Закодированная информация пересылается серверу одним из методов GET или POST. Основное отличие заключается в том, как метод передает информацию CGI-программе.
При использовании метода GET данные формы пересылаются серверу в составе URL запроса, к которому добавляются после символа ? (вспомним, что запрос — это формализованный способ обращения браузера к Web-серверу). Тело запроса в этом случае является пустым. Для формы из примера 15.1 запрос выглядит следующим образом:
GET /cgi-bin/registrar.cgi? regname = bobSpasswordl = rumataSpassword2 = rumata&age = lt20&language = russian&format = HTML&wish = %F6%C5%CC%Dl%DA%CE%D9 HTTP/1.0
(заголовки запроса, сообщающие серверу, информацию о клиенте) <пусто> (тело запроса). Часть URL после символа "?" называется строкой запроса. Web-сервер, получив запрос, присвоит переменной среды QUERY_STRING значение строки запроса и вызовет CGI-программу, обозначенную в первой части URL до символа "?": /cgi-bin/registrar.cgi. CGI-Программа registrar.cgi сможет затем обратиться к переменной QUERY_STRING для обработки закодированных в ней данных.
Обратите внимание на то, что данные, введенные в поле типа PASSWORD, передаются открытым текстом без шифрования. При передаче данных методом GET они в составе URL помещаются в файл регистрации доступа access.log, обычно открытый для чтения всем пользователям. Таким образом "секретные" данные, введенные в поле типа PASSWORD, оказываются доступными посторонним.

К сожалению, эта информацдагявдяется статической. Форма же позволяет менять данные.
Строка запроса — не единственный способ передачи данных через URL. Другим способом является дополнительная информация о пути (extra path information), представляющая собой часть URL, расположенную после имени CGt-программы. Сервер выделяет эту часть и сохраняет ее в переменной среды PATH_INFO. CGI-программа может затем использовать эту переменную для извлечения данных. Например, URL
http://www.domain/cgi-bin/registrar.cgi/
4>regname = bobspasswordl = rumataspassword2 = r\amata&age = lt20&language = 4>russian&format = HTML&wish = %F6%C5%CC%Dl%DA%CE%D9
содержит уже знакомые нам данные, но не в виде строки запроса, а в виде дополнительной информации о пути. При получении запроса с таким URL сервер сохранит данные в переменной среды
PATH_INFO = /regname = bobspasswordl = rumataspassword2 = rumata&age = l blt20&language = russian&format = HTML&wish = %F6%C5%CC%Dl%DA%CE%D9"
Название объясняется тем, что обычно этим способом передается информация о местоположении какого-либо файла (extra path information). Например, URL
http://www.domain/cgi-bin/registrar.cgi/texts/jdk_doc.txt
содержит дополнительную информацию PATH_iNFO=/texts/jdk_doc.txt" о местонахождении файла jdk_doc.txt относительно корневого каталога дерева документов. Другая переменная среды PATHJTRANSLATED содержит информацию об абсолютном местоположении файла в файловой системе, например,
PATH_TRANSLATED="/hcme/httpd/docs/texts/jdk_doc.txt"
а переменная DOCUMENT_ROOT содержит путь к корневому каталогу дерева документов, В нашем случае DOCUMENT_ROOT="/home/httpd/docs/".
При использовании метода POST данные формы пересылаются серверу в теле запроса.
<form method="post" action="/cgi-bin/registrar.cgi">,
то запрос клиента будет иметь следующий вид:
POST /cgi-bin/registrar.cgi HTTP/1.1
(заголовки запроса, сообщающие серверу информацию о клиенте)
Content-length: 126
regname=bob&passwordl=rumata&password2=ruinata&age=lt20&language=russian& 4>format=HTML&wish=%F6%C5%CC%Dl%DA%CE%D9
В этом фрагменте среди прочих заголовков выделен заголовок content-length, сообщающий серверу количество байт, переданных в теле запроса. Это значение сервер присваивает переменной среды CONTENT_LENGTH, а данные посылает в стандартный ввод CGI-программы.
Методы GET и POST имеют свои достоинства и недостатки. Метод GET обеспечивает лучшую производительность при пересылке форм, состоящих из небольшого набора коротких полей. При пересылке большого объема данных следует использовать метод POST, так как браузер или сервер могут накладывать ограничения на размер данных, передаваемых в составе URL, и отбрасывать часть данных, выходящую за границу. Метод POST, к тому же, является более надежным при пересылке конфиденциальной информации.
 CGI-сценарии
Назначение 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

Заголовки отделяются от содержимого документа пустой строкой.
Напишем простейший 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.

Переменные среды CGI
В зависимости от метода данные формы передаются в CGI-программу или через стандартный ввод (POST), или через переменную среды QUERY_STRING (GET). Помимо этих данных CGI-программе доступна и другая информация, поступившая от клиента в заголовках запроса или предоставленная Web-сервером. Эта информация сохраняется в переменных среды UNIX. С некоторыми из них мы уже познакомились ранее. В табл. 15.1 перечислены переменные, обычно используемые в CGI.

Таблица 15.1. Переменные среды CGI

Имена переменных среды CGI на разных Web-серверах могут различаться. Следует обратиться к документации на соответствующий сервер.
CGI-программа на языке Peri имеет доступ к переменным среды через специальный предопределенный хеш-массив %ENV, к элементам которого можно обратиться по ключу, совпадающему с именем переменной среды. Ниже приведены пример CGI-сценария, формирующего HTML-документ с информацией о всех установленных переменных среды, и отображение этого документа в окне браузера.
#!/usr/bin/perl
print "Content-type:text/html\n\n";
print "<html>\n";
print "<head><title>Преременные cpeды</titlex/head>\n";
print "<bоdу><h2>Переменные среды</h2>\n";
print "<hrxpre>\n";
foreach $name (sort(keys %ENV))
{
print "$name: $ENV($name}\n";
}
print "<hrx/pre>\n";
print "</bodyx/html>\n";
Обработка данных формы
Данные формы поступают в 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. Аргумент template представляет собой последовательность символов, определяющих формат представления пакуемых данных:
а/A Текстовая строка, заполненная нулями/пробелами
b/B Двоичная строка, значения расположены в порядке возрастания/ убывания
с/с Обычное символьное значение/ Символьное значение без знака
f/d Значение в формате с плавающей точкой одинарной/двойной точности
b/n Шестнадцатеричная строка, младший/старший полубайт первый
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.  
Пример создания собственного 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;
Проверка заполнения обязательных полей
if (!$FORM{'regname'} I I !$FORM{'passwordl'}) { print «goback
<html>
<head><title>Невведенные данные </title></head>
<body><h2>Извините, Вы пропустили обязательные данные</п2>
<br>
<а href=" http://www.klf.ru/welcome.Мл11 ">Попробуйте еше раз, пожалуйста</а>
</body>
</html> goback ;}
#Проверка правильности ввода пароля elsif ($FORM{'passwordl 1 } eq $FORM{'password2'}){ print«confirmation <htrml>
<head><title>no3flpaBnHeM!</titlex/head> <Ьойу><h2>Поздравляем! </h2><br>
Ваша регистрация прошла успешно. Вы можете пользоваться нашей библиотекой. Спасибо за внимание.
</body>
</html>
confirmation
;}
else {
print«new_form
<htmlxhead><title>Oira6Ka при вводе napc«w</titlex/head>
<ЬойуХпЗ>Введенные Вами значения пароля не совпадают
<br><form 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-документ, предлагающий повторить попытку, который и посылается клиенту.
При выводе этого документа в операции print использована конструкция "документ здесь". Она позволяет использовать внутри себя символы, которые при заключении в обычные двойные кавычки необходимо маскировать символом "\", например, сами, двойные кавычки ", символы "@", ••$", "%".
Условие elsif ($FORM{ 'passwordl 1 } eq $FORM{ 'password2 ' }} предназначено для проверки совпадения двух копий введенного пользователем пароля. Если значения совпадают, то пользователю посылается сообщение, подтверждающее успешную регистрацию.

В противном случае формируется HTML-документ, предлагающий ввести пароль повторно (рис. 15.6). Этот новый документ содержит форму, в состав которой входят два видимых поля типа "password" — для ввода и подтверждения пароля, и скрытые поля типа "hidden" — для сохранения остальных данных, введенных при заполнении исходной формы. Каждое скрытое поле новой формы наследует у соответствующего поля исходной формы атрибуты name и value. Если эти данные не сохранить, то их придется вводить заново, принуждая пользователя повторно выполнять уже сделанную работу. Информация, сохраненная в скрытых полях, невидима пользователю и недоступна для изменения.
Культура 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>
<head><title>Ошибка при вводе пароля</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
<br><br>
<input type="submit" value="OK">
<input type="reset" value="Отменить">
</form>
</body>
</html> EndOfHTML
} exit
3. В исходном варианте сценария в качестве ответов сервера при получении неполных данных и для подтверждения регистрации пользователя формируются виртуальные HTML-документы. В этом нет необходимости, так как они содержат только статическую информацию. Соответствующие фрагменты сценария преобразуем в HTML-код ротовых документов, которые сохраним в отдельных файлах. В основном сценарии в качестве ответа сервера возвращаются ссылки на эти документы.
Файл confirmation.html содержит документ, посылаемый клиенту в качестве сообщения об успешной регистрации:
<html>
<head><title>Пoздpaвляeм!</title></head> <body><h2>Пoздpaвляeм! </h2><br>
Ваша регистрация прошла успешно. Вы можете пользоваться нашей библиотекой. ..- -
<br>
Спасибо за внимание.
</body>
</html>
Файл goback.html содержит документ, посылаемый клиенту при получении неполных данных:
<html>
<head><title>Heпoлныe данные</^^1ех/пеай>
<body><h2>HsBHHMTe, Вы пропустили обязательные данные</h2>
<br>
<а href=" http://www.klf.ru/welcome.Ылп1 ">Попробуйте еще раз, пожалуйста </а>
</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, то без проверки в ассоциативный массив %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.  
Модуль 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 ('Пример формы' ) ,

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика