Cgi сценарий пример

Работа по теме: 8. +Серверные веб-прил. Глава: 8. Серверные веб-приложения. ВУЗ: МИРЭА.

Стандарт cgi. Сценарии. Сценарные языки: классификация по быстродействию. Язык Python. Язык Ruby. Технология asp. Интерфейс isapi.

Для
расширения возможностей клиент-серверного
взаимодействия в рамках протокола HTTP
помимо создания на клиентской стороне
расширений стандартных возможностей,
предоставляемых языками разметки и
браузерами, можно также разрабатывать
на стороне веб-сервера приложения,
плагины и сценарии, расширяющие
возможности самого веб-сервера.

Плагин
(plug-in) — независимо компилируемый
программный модуль, динамически
подключаемый к основной программе,
предназначенный для расширения или
использования ее возможностей. Обычно
выполняются в виде разделяемых библиотек.

Сценарий
(скрипт,
script) — программа, которая автоматизирует
некоторую задачу, которую пользователь
выполняет вручную используя интерфейсы
программы.

Стандарт cgi

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

Основной
набор таких правил — стандарт CGI (Common
Gateway Interface — интерфейс общего шлюза),
который определяет порядок запуска
программы на компьютере-сервере, способы
передачи программе параметров и доставки
результатов ее выполнения клиенту.
Программа, написанная по правилам CGI,
называется CGI-сценарием (script CGI), хотя
это не означает, что на сервере не может
выполняться двоичный файл.

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

Выполнение
любой программы (в том числе CGI-сценария)
можно условно разделить на пять этапов.

  1. Запуск
    программы.

  2. Инициализация
    и чтение выходных данных.

  3. Обработка
    данных.

  4. Вывод
    результатов выполнения.

  5. Завершение
    программы.

Различия
между CGI-сценарием и консольным приложением
касаются первого, второго и четвертого
этапов выполнения.

Каждый
раз, когда веб-сервер получает запрос
от клиента, он анализирует содержимое
запроса и возвращает соответствующий
ответ:

  • Если
    запрос содержит указание на файл,
    находящийся на жестком диске, то сервер
    возвращает в составе ответа этот файл;

  • Если
    запрос содержит указание на программу
    и необходимые для нее аргументы, то
    сервер исполняет программу и результат
    ее работы возвращает клиенту.

Cgi определяет:

  • каким
    образом информация о сервере и запросе
    клиента передается программе в форме
    аргументов и переменных окружения;

  • каким
    образом программа может передавать
    назад дополнительную информацию о
    результатах (например о типе данных) в
    форме заголовков ответа сервера.

В
подавляющем большинстве случаев запуск
CGI-сценария осуществляется щелчком на
кнопке Submit, сформированной с помощью
дескриптора <input tyре = «submit»>,
который находится на HTML-странице между
<form> и </form>. Не зная назначения
атрибутов action и method, невозможно понять,
как происходит вызов программы и передача
параметров.

Значением
атрибута action дескриптора <form> является
URL файла, содержащего код CGI-сценария.
Так, приведенное ниже выражение означает,
что файл с кодом CGI-сценария находится
на сервере www.myhp.edu в каталоге cgi-bin в файле
script.рl.

<form
action=»http://www.myhp.edu/cgi-bin/script.pl»
method=»post»>

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

Первый
способ

заключается в том, что при установке
веб-сервера один из каталогов специально
выделяется для хранения сценариев.
Обычно такой каталог получает имя
cgi-bin (или Scripts для веб-сервера IIS). В этом
случае, если клиент запрашивает файл
из каталога cgi-bin, сервер воспринимает
такой запрос как команду на запуск
сценария. Файлы из других каталогов
интерпретируются как HTML-документы.

Второй
способ

использует расширение файла. При
настройке сервера указывается, что
файлы с определенными расширениями
содержат коды сценариев.

Идентификация
по расширению используется относительно
редко. Чаще всего все сценарии помещаются
в cgi-bin, /Scripts или в другой каталог,
специально выделенный для их хранения.

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

Информация,
передаваемая клиенту, должна соответствовать
протоколу HTTP, т.е. состоять из заголовка
и тела ответа. Как правило, получив
данные от сценария, сервер самостоятельно
добавляет первую строку заголовка.

НТТР/1.0
200 OK

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

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

Content-type:
text/html

<html>

<hеаd>

<titlе>ответ
сценария</titlе>

</hеаd>

<body>

……………………

</body>

</html>

Обратите
внимание на пустую строку после выражения
Content-type: text/html. Она обязательно должна
присутствовать в ответе, в противном
случае клиент воспримет все последующие
данные как продолжение заголовка.

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

Для
вызова данного сценария достаточно
включить в веб-страницу следующий
фрагмент HTML-кода:

<form
method=»post»
action=»http://www.intuit.ru/cgi-bin/hello.exe»>

<input
type=»submit»>

</form>

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

В
большинстве случаев кроме кнопки Submit
форма содержит другие интерактивные
элементы, каждый из которых имеет имя
(атрибут NAME) и значение (атрибут VALUE, либо
последовательность символов, введенная
пользователем). Из имен элементов и их
значений формируется строка параметров,
которая имеет следующий формат.

имя=значение&имя=значение&
. . . &имя=значение

Каждый
параметр представляет собой имя
управляющего элемента и его значение,
разделенные знаком равенства, а несколько
таких пар объединяют строку с помощью
символа «&». Если в состав имени
или значения входит символ «&»
или «=», то подобные символы кодируются
последовательность знака процента «%»,
за которым следуют две шестнадцатеричные
цифры, определяющие код символа. Так,
например, последовательностью «%21»
кодируется восклицательный знак «!».
Как правило, при передаче параметров
трехсимвольными последовательностями
заменяются все знаки, кроме латинских
букв, цифр и символа пробела (последний
заменяется знаком «+»).

Таким
образом, перед использованием строки
параметров ее надо декодировать. Алгоритм
декодирования чрезвычайно прост и
включает в себя следующие действия:

  • Выделить
    из строки параметров пары имя = значение.

  • Выделить
    из каждой пары имя и значение.

  • В
    каждом имени и каждом значении заменить
    символы «+» пробелами.

  • Каждую
    последовательность из символа «%»
    и двух шестнадцатеричных и преобразовать
    в ASCII-символ.

Атрибут
method дескриптора <form> имеет либо
значение «GET», либо значение «POST».
Значения «GET» и «POST» определяют
два различных метода передачи параметров
сценарию:

  • Если
    атрибут method имеет значение «GET»,
    строка параметров передается вместе
    с URL вызываемого сценария. Разделителем
    между URL и строкой параметров является
    символ «?».

  • Если
    атрибут method имеет значение «POST»,
    строка параметров передается в теле
    HTTP-запроса.

Рассмотрим,
как должен вести себя CGI-сценарий, чтобы
правильно обработать данные в зависимости
от метода, использованного при передаче
данных, строка параметров доставляется
CGI-сценарию различными способами.

Если
атрибут METHOD дескриптора <FORM> имел
значение «GET», строка параметр
передается серверу в качестве значения
переменной окружения QUERY_STRING.

При
использовании метода POST данные
доставляются сценарию по-другому. Они
передаются через стандартный поток
ввода (STDIN). Чтобы сценарий смог определить,
сколько символов следует читать из
стандартного ввода, веб-сервер
устанавливает значение переменной
окружения CONTENT_LENGTH, равным длине строки
параметров.

Получив
управление, сценарий в первую очередь
должен выяснить, с помощью какого метода
выполнялась передача параметров. Эта
информация содержится в переменной
окружения REQUEST_METHOD.

Таким
образом, в простейшем случае, чтобы
выполнить обработку строки параметров,
достаточно знать назначение трех
переменных окружения: REQUEST_METHOD, QUERY_STRING
и CONTENT_LENGTH.

Пример
сценария на языке Perl, который возвращает
клиенту строку параметров, приведен
ниже. Сценарий определяет, какой метод
использовался для передачи данных,
читает строку параметров и передает ее
клиенту, предварительно дополнив
HTML-дескрипторами.

$method
= $ENV{‘REQUEST_METHOD’};

if
($method eq «GET»)

{
$pars = $ENV{‘QUERY_STRING’}; }

else

{
$length =$ENV{‘CONTENT_LENGTH’}; }

read
(STDIN, $pars, $ length);

print
«Content-type: text/htmlnn»;

print
«<HTML><BODY>n»;

print
«<P>METHOD = «, $method;

print
«<P>String of parameters: <P>n»;

print
$pars;

print
«</HTML></BODY>n»;

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

REMOTE_ADDR
IP-адрес узла, с которого поступил запрос

REMOTE_HOST
Доменное имя узла, с которого поступил
запрос

SERVER_PORT
Номер порта, который использовался при
обращении к серверу

SERVER_SOFTWARE
Имя и версия сервера, посредством
которого был запущен сценарий

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

SERVER_PSOTOCOL
Название и версия протокола, с помощью
которого был передан запрос

HTTP_USER_AGENT
Клиентская программа, отправившая
запрос серверу

HTTP_REFERER
URL документа, отображаемого браузером
при вызове сценария

Соседние файлы в папке +Web-технол 1-36

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

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

Основу «всемирной паутины» WWW составляют Web-узлы. Это компьютеры, на которых выполняется специальная программа — Web-сервер, ожидающая запроса со стороны клиента на выдачу документа. Документы сохраняются на Web-узле, как правило, в формате HTML. Клиентом Web-сервера является программа-браузер, выполняющаяся на удаленном компьютере, которая осуществляет запрос к Web-серверу, принимает запрошенный документ и отображает его на экране.

Аббревиатура CGI (Common Gateway Interface) обозначает часть Web-сервера, которая может взаимодействовать с другими программами, выполняющимися на этом же Web-узле. В этом смысле она является шлюзом (gateway — шлюз) для передачи данных, полученных от клиента, программам обработки — таким, как СУБД, электронные таблицы и др. CGI включает общую среду (набор переменных) и протоколы для взаимодействия с этими программами.

Общая схема работы CGI состоит из следующих элементов.

1. Получение Web-сервером информации от клиента-браузера. Для передачи данных Web-серверу в языке HTML имеется средство, называемое форма. Форма задается в HTML-документе при помощи тэгов <FORM>. . .</FORM> и состоит из набора полей ввода, отображаемых браузером в виде графических элементов управления: селекторных кнопок, опций, строк ввода текста, управляющих кнопок и т. д. (рис. 15.1).

2. Анализ и обработка полученной информации. Данные, извлеченные из HTML-формы, передаются для обработки CGI-программе. Они не всегда могут быть обработаны CGI-программой самостоятельно. Например, они могут содержать запрос к некоторой базе данных, которую CGI-программа читать «не умеет». В этом случае CGI-программа на основании полученной информации формирует запрос к компетентной программе, выполняющейся на том же компьютере. CGI-программа может быть написана на любом языке программирования, имеющем средства обмена данными между программами. В среде UNIX для этой цели наиболее часто используется язык Perl. Так как UNIX является наиболее популярной операционной системой для Web-серверов, то можно считать Perl наиболее популярным языком CGI-программирования. Программа на языке Perl представляет собой последовательность операторов, которые интерпретатор языка выполняет при каждом запуске без преобразования исходного текста программы в выполняемый двоичный код. По этой причине CGI-программы называют также CGI-сценариями или CGI-скрип-тами.

3. Создание нового HTML-документа и пересылка его браузеру.

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

Пример отображения HTML-формы браузером

Рис 15.1. Пример отображения HTML-формы браузером

HTML-формы

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

(Различные аспекты передачи данных Web-серверу будут рассмотрены в разделе 15.3.)

Мы предполагаем, что читатель знаком с основами языка 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.

Замечание
Взаимодействие между клиентом-браузером и Web-сервером осуществляется по правилам, заданным протоколом HTTP, и состоит из запросов клиента и ответов сервера. Запрос разбивается на три части. В первой строке запроса содержится HTTP-команда, называемая методом, URL запрашиваемого файла и номер версии протокола HTTP. Вторая часть— заголовок запроса. Третья часть — тело запроса, собственно данные, посылаемые серверу. Метод сообщает серверу о цели запроса. В протоколе HTTP определены несколько методов. Для передачи данных формы в CGI-программу используются два метода: GET И POST.

При использовании метода GET данные формы пересылаются в составе URL запроса, к которому присоединяются после символа «?» в виде совокупности пар переменная=значение, разделенных символом «&». В этом случае первая строка запроса может иметь следующий вид:

После выделения данных из URL сервер присваивает их переменной среды QUERY_STRING, которая может быть использована CGI-программой.

При использовании метода POST данные формы пересылаются Web-серверу в теле запроса, после чего передаются сервером в CGI-программу через стандартный ввод.

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

Замечание
Одним из первых применений Internet была электронная почта, ориентированная на пересылку текстовых сообщений. Часто возникает необходимость вместе с текстом переслать данные в нетекстовом формате, например, упакованный zip-файл, рисунок в формате GIF, JPEG и т. д. Для того чтобы пересылать средствами электронной почты такие файлы без искажения, они кодируются в соответствии с некоторым стандартом. Стандарт MIME (Multipurpose Internet Mail Extensions, многоцелевые расширения электронной почты для Internet) определяет набор MIME-типов, соответствующих различным типам данных, и правила их пересылки по электронной почте. Для обозначения MIME-типа используется запись вида тип/подтип. Тип определяет общий тип данных, например, text, image, application (тип application обозначает специфический внутренний формат данных, используемый некоторой программой), а подтип — конкретный формат внутри типа данных, например, application/zip, image/gif, text/html. MIME-типы нашли применение в Web, где они называются также ме-dua-типами, для идентификации формата документов, передаваемых по протоколу HTTP. В HTML-форме атрибут ENCTYPE определяет медиа-тип, который используется для кодирования и пересылки специального типа данных — содержимого формы.

Как видно из примера на рис. 15.1, форма отображается в окне браузера в виде набора стандартных элементов управления, используемых для заполнения полей формы значениями, которые затем передаются Web-серверу. Значение вводится в поле ввода пользователем или назначается по умолчанию. Для создания полей средствами языка HTML существуют специальные тэги: <INPUT>, <SELECT>, <TEXTAREA>, которые могут употребляться только внутри тэга <FORM>.

Тэг <INPUT>

<INPUT ТУРЕ=тип_лоля_ввода ЫАМЕ=имя_лоля_ввода друте_атрибуты>

Наиболее употребительный тэг, с помощью которого можно генерировать внутри формы поля для ввода строки текста, пароля, имени файла, различные кнопки. Он имеет два обязательных атрибута: TYPE и NAME. Атрибут TYPE определяет тип поля: селекторная кнопка, кнопка передачи и др. Атрибут NAME определяет имя, присваиваемое полю. Оно не отображается браузером, а используется в качестве идентификатора значения, передаваемого Web-серверу. Остальные атрибуты меняются в зависимости от тира поля. Ниже приведено описание типов полей, создаваемых при помощи тэга <INPUT>, и порождаемых ими элементов ввода.

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

TYPE=PASSWORD

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

Замечание
Поле PASSWORD не обеспечивает безопасности введенного текста, так KaKjna сервер он передается в незашифрованном виде. /-«‘»

TYPE=FILE

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

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

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

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

Если атрибуты NAME И VALUE Присутствуют, Например,

<INPUT TYPE=SUBMIT NAME="submit_button" VALUE="OK">,

то в список параметров формы, передаваемых на сервер, включается параметр submit_button=»OK». Внутри формы могут существовать несколько кнопок передачи.

  • TYPE=RESET. Создает кнопку сброса, нажатие которой отменяет все сделанные изменения, восстанавливая значения полей формы на тот момент, когда она была загружена. По умолчанию отображается в виде прямоугольной кнопки с надписью Reset. Надпись можно изменить при помощи дополнительного атрибута
  • TYPE.=название_кнопки. Значение кнопки Reset никогда не пересылается на сервер, поэтому у нее отсутствует атрибут NAME.
  • TYPE=IMAGE. Создает элемент в виде графического изображения, действующий аналогично кнопке Submit.

Дополнительные атрибуты:

  • TYPE=тип_изображения. Задает ссылку (url) на файл с графическим изображением элемента.
  • TYPE=тип_въ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 TYPE=строка>содержимое_тэг'а</OPTION>

Закрывающий тэг </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>npHMep формы</^д^1ех/пеаЗ>
<body>
<Ь2>Регистрационная страница Клуба любителей фантастики</Ь2>
Заполнив анкету, вы сможете пользоваться нашей электронной библиотекой.
<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ие="епд!1зЬ">английский
<input type="checkbox" name="language" Уа1ие="£гепсЬ">французский
<input type="checkbox" name="language" value="gennan">HeMeuKHH
<brxbr>
Какой формат данных является для Вас предпочтительным
<Ьг>
<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. Дальнейшее прохождение данных выглядит следующим образом.

  • Информация кодируется и пересылается на Web-сервер, который передает ее для обработки CGI-программе.
  • CGI-программа обрабатывает полученные данные, возможно, обращаясь за помощью к другим программам, выполняющимся на том же компьютере, и генерирует новый «виртуальный» HTML-документ, либо определяет ссылку на уже имеющийся.
  • Новый 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, оказываются доступными посторонним.

Замечание
Метод GET позволяет передавать данные CGI-программе вообще без использования форм. Информацию, содержащуюся в приведенном выше URL, можно передать при помощи следующей гиперссылки, помещенной в HTML-документ: <А HREF=»http://www.domain/cgi-bin/registrar.cgi?regname= 4>bobspasswordl=rumata&password2=rimata&age=lt20slanguage=russian& 4>format=HTML&wish=%F6%C5%CC%Dl%DA%CE%D9 «>СС1-программа</А>, заменив в этом фрагменте символ «&» его символьным примитивом &#38 или &amp для правильной интерпретации браузером.

К сожалению, эта информацдагявдяется статической. Форма же позволяет менять данные.

Строка запроса — не единственный способ передачи данных через URL. Другим способом является дополнительная информация о пути (extra path information), представляющая собой часть URL, расположенную после имени CGt-программы. Сервер выделяет эту часть и сохраняет ее в переменной среды PATH_INFO. CGI-программа может затем использовать эту переменную для извлечения данных. Например, URL

http://www.domain/cgi-bin/registrar.cgi/4>regname = bobspasswordl
= rumataspassword2 = ramata&age = lt20&language
= 4>russian&format = HTML&wish = %F6%C5%CC%Dl%DA%CE%D9

содержит уже знакомые нам данные, но не в виде строки запроса, а в виде дополнительной информации о пути. При получении запроса с таким URL сервер сохранит данные в переменной среды

PATH_INFO = /regname = bobspasswordl = rumataspassword2 = rumata&age = lblt20&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 данные формы пересылаются серверу в теле запроса. Если в примере 15.1 вместо метода GET использовать метод 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, к тому же, является более надежным при пересылке конфиденциальной информации.

CGL-сценарии

Назначение 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/htmlnn";
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-серверов можно найти в соответствующей литературе и документации на конкретный сервер.

Web-страница, сформированная программой hello.cgi

Рис 15.2. Web-страница, сформированная программой hello.cgi

Переменные среды 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-программа на языке Perl имеет доступ к переменным среды через специальный предопределенный хеш-массив %ENV, к элементам которого можно обратиться по ключу, совпадающему с именем переменной среды. Ниже приведены пример CGI-сценария, формирующего HTML-документ с информацией о всех установленных переменных среды, и отображение этого документа в окне браузера.

#!/usr/bin/perl
print "Content-type:text/htmlnn";
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";

Информация о переменных среды CGI

Рис 15.3. Информация о переменных среды CGI

Обработка данных формы

Данные формы поступают в 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 представляет собой последовательность символов, определяющих формат представления пакуемых данных:

  • а/А Текстовая строка, заполненная нулями/пробелами
  • ь/в Двоичная строка, значения расположены в порядке возрастания/ убывания
  • с/с Обычное символьное значение/ Символьное значение без знака
  • 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;

Значение переменной $х во всех случаях равно «perl». Функция

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/htmlnn"; $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/htmlnn"; } .
# Подпрограмма декодирования данных форьы 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//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} .= "$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.htmlnn"; }
#Проверка правильности ввода пароля
elsif ($FORM{'passwordl'} eq $FORM{'password2'}}{
print "Location: /confirmation.htmlnn";
open (OUTF, "»users");
print OUTF $file_rec, Hn";
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} .= "$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 ('Пример формы) , 1

Замечание
Функции модуля CGI.pm являются методами класса CGI. Для того чтобы их можно было вызывать и как функции, и как методы, синтаксис не требует в качестве обязательного первого параметра указывать объект класса CGI. Поэтому в качестве функций к ним можно обращаться обычным образом, а как к объектам — только используя форму $object->method ().

Модуль как мы отметили выше, содержит большой набор методов, и в наши планы не входит их подробное изучение. Документация, входящая в состав самого модуля, достаточно подробно описывает его компоненты. Чтобы получить представление о работе модуля 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',20_30'=>'20-30', 4> '30_50'=>'30-50','gt50'=>'старше 50'}), I
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('wish))), 1
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 Found 1);

Функция start_htmi () создает HTML-заголовок и начальную часть документа, включая открывающий тэг <вооу>. При помощи параметров функции можно задать дополнительные тэги внутри тэга <HEAD>, а также значения атрибутов. Все параметры являются необязательными. В примере функция start_htmio вызвана с одним позиционным параметром, определяющим название документа.

Модуль CGI содержит методы (функции) для поддержки многих тэгов HTML2, HTML3, HTML4 и расширений, используемых в браузерах Netscape. Тэгам соответствуют одноименные методы модуля CGI.pm, имена которых записываются при помощи символов нижнего регистра. Если при этом возникают конфликты имен, в названия методов следует вводить символы верхнего регистра, как, например, в следующих случаях.

  • Название тэга <TR> совпадает с именем встроенной функции tr(). Имя соответствующего метода записывать в виде TR () или тг ().
  • Название тэга <PARAM> совпадает с именем собственного метода модуля CGI paramo. Для обозначения метода, соответствующего тэгу, использовать ИМЯ РАКАМ ().
  • Название тэга <SELECT> совпадает с именем встроенной функции select (). Для обозначения метода использовать имя Select ().
  • Название тэга <зив> совпадает с именем ключевого слова объявления функции 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);

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

  • Функция 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=>'bom, r
-labels=>%labels);

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

  • Функция 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-адрес Если из удаленного браузера послать запрос по адресу то Web-сервер, получив запрос, выполнит сценарий welcome, cgi. Сценарий «на лету» создаст HTML-документ, содержащий форму, и передаст его Web-серверу, который отправит документ браузеру. Браузер, получив документ, отобразит его. www.klf.ru, http://www.klf.ru/cgi-bin/welcome.cgi.

После заполнения формы и нажатия кнопки данные формы будут вновь отправлены Web-серверу, который передаст их для обработки все тому же сценарию welcome.cgi. Сценарий «на лету» создаст новый HTML-документ с учетом полученных данных и через сервер направит его браузеру. Браузер отобразит новый документ.

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

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> <HTMLXHEADXTITLE>npMMep <J>opMbi</TITLE> </HEADXBODY>
<Н2>Регистрационная страница Клуба любителей фантастики</Н2>
Заполнив анкету, вы сможете пользоваться нашей электронной библиотекой,
<BR>
<FORM METHOD="POST" ENCTYPE="application/x-www-form-urlencoded">
Введите регистрационное имя:<1ЫРит TYPE="text" NAME="regname" ><P>
Введите пароль: <INPUT TYPE="password" NAME="passwordl" MAXLENGTH=8><P>
Подтвердите пароль: <INPUT TYPE="password" NAME="password2" MAXLENGTH=8>
<Р>Ваш возраст<Р>
<INPUT TYPE="radio" NAME="age" VALUE="lt20" СНЕСКЕО>до 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">CTapnie 50
<BRXBR>Ha каких язьпсах читаете:
<INPUT TYPE="checkbox" NAME="language" УАШЕ="русский" СНЕСКЕП>русский
<INPUT TYPE="checkbox" NAME="language" УАШЕ="английский">английский
<INPUT TYPE="checkbox" NAME="language" УАьиЕ="французский">фанцузский р
<INPUT TYPE="checkbox" NAME="language" VALDE="немецкий"Жемецкий
<BRXBR>
Какой формат данных является для Вас предпочтительным
<BRXSELECT NAME="type">
<OPTION VALUE="Plain text">Plain text
<OPTION VALUE="PostScript">PostScript
<OPTION VALUE="PDF">PDF
</SELECT>
<BRXBR>
Ваши любимые авторы:
<BRXTEXTAREA NAME="wish" ROWS=3 COLS=40X/TEXTAREA>
<BR>
<INPUT TYPE="sxobmit" NAME="OK" VALUE="OK">
<INPUT TYPE="reset" VALUE="Отменить">
<INPUT TYPE="hidden" NAME=".cgifields" VALUE="language">
<INPUT TYPE="hidden" NAME=".cgifields" VALUE="age">
</FORM>
<HRX/BODYX/HTML>

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

Вопросы для самоконтроля

  1. Что обозначает аббревиатура CGI?
  2. Что такое Web-сервер?
  3. Что такое клиент Web-сервера?
  4. Для чего нужна HTML-форма?
  5. Какую первую строку должна выводить CGI-программа?
  6. Какие методы передачи данных формы Web-серверу вы знаете? В чем их особенности?
  7. Перечислите элементы управления, которые может содержать форма.
  8. Как кодируется информация, передаваемая CGI-программе?
  9. В чем заключается специфика CGI-сценариев?
  10. Каким образом CGI-программа имеет доступ к переменным среды CGI?
  11. Какая информация содержится в переменной среды QUERY_STRING?
  12. Для чего предназначен модуль CGI.pm?

Упражнения

1. Запишите текст примера 15.8 в обозначениях, использующих объектно-ориентированный интерфейс с модулем CGI.

2. Используя модуль CGI, дополните пример 15.8 кодом, осуществляющим проверку введенных данных, как это сделано в примере 15.76.

3. Ниже приведены тексты трех примеров, входящих в отдельный дистрибутив модуля CGI.pm. Разберите, что делают эти сценарии.

  • a) clickable_image.cgi

#!/usr/bin/perl
use CGI;
$query = new CGI;
print $query->header;
print $query->start__html("A Clickable Image");
print «END;
<H1>A Clickable Image</Hl>
</A> /
END |
print "Sorry, this isn't very exciting!n";
print $query->startform;
print $query->image_button('picture',"./wilogo.gif"); print "Give me a: ", $query->popup_rnenu ('letter',
['A', 'B', 'C', 'D', 'E', 'W]),"n"; print "<P>Magnification: ", $query->radio_group('magnification',
['IX,'2X',MX','20X']),"n"; print "<HR>n"; if ($query->param) 1{
print "<P>Magnification, <EM>",$query->param('magnification'),
"</EM>n";
print "<P>Selected Letter, <EM>",$query->param( 'letter'),"</EM>n"; ($x,$y) = ($query->param('picture.x'),$query->param('picture.y')) ; print "<P>Selected Position <EM>($x,$y)</EM>n";
}
print $query->end_html;

  • 6) quadraphobia.cgi

#!/usr/bin/perl use CGI qw/:standard/; print header,
start_html('QuadraPhobia'),
hi('QuadraPhobia'),
start_form(),
image_button(-name=>'square',
-src=>'red_square.gif,
-width=>200,
-height=>200,
-align=>MIDDLE),
end_form(); if (paramO) {
($x,$y) = (param('square.x'),param('square.у'));
$pos = 'top-left' if $x < 100 && $y < 100;
$pos = 'top-right' if $x >= 100 && $y < 100;
$pos = 'bottom-left' if $x < 100 && $y >= 100;
$pos = 'bottom-right' if $x >= 100 && $y >= 100;
print b("You clicked on the $pos part of the square."); }
print p,a({href=>'../source.html'},"Code examples"); print end_html ();

  • в) popup.cgi

#!/usr/local/bin/perl
use CGI;
$query = new CGI;
print $query->header;
print $query->start_html('Popup Window');
if (!$query->param) {
print "<Hl>Ask your Question</Hl>n";
print $query->startform(-target=>'_new');
print "What's your name? ",$query->textfield('name'); .
print "<P>What's the corobination?<P>",
$query->checkbox_group(-name=>'words',
-values=>['eenie','meenie','minie','moe'],
-defaults=>['eenie','moe']);
print "<P>What's your favorite color? ", $query->popup_menu(-name=>'color',
-values=>['red','green','blue','chartreuse']), "<P>";
print $query->submit; print $query->endform;
} else {
print "<Hl>And the Answer is...</Hl>n";
print "Your name is <EM>",$query->param(name),"</EM>n";
print "<P>The keywords are: <EM>",
join(", ",$query->param(words)),"</EM>n"; print "<P>Your favorite color is <EM>",
$query->param{color),"</EM>n";
}
print qq{<PXA HREF="cgi_docs.html">Go to the documentation</A>};
print $query->end_html;

Материал из Intuit

Перейти к: навигация, поиск

Разработка Web-приложений

Аннотация: Одна из главных сфер применения языка Python — web-приложения — представляется в этой лекции на конкретных примерах. Кроме того, делается акцент на типичных слабых местах безопасности web-приложений.

Содержание

  • 1 НЕТ ДАННЫХ
  • 2 CGI-сценарии
    • 2.1 Модуль cgi
    • 2.2 Что после CGI?
  • 3 Среды разработки
    • 3.1 Zope и его объектная модель
    • 3.2 Заключение

НЕТ ДАННЫХ

Под web-приложением будет пониматься программа, основной интерфейс пользователя которой работает в стандартном WWW-браузере под управлением HTML и XML-документов. Для улучшение качества интерфейса пользователя часто применяют JavaScript, однако это несколько снижает универсальность интерфейса. Следует заметить, что интерфейс можно построить на Java- или Flash-апплетах, однако, такие приложения сложно назвать web-приложениями, так как Java или Flash могут использовать собственные протоколы для общения с сервером, а не стандартный для WWW протокол HTTP.

При создании web-приложений стараются отделить Форму (внешний вид, стиль), Содержание и Логику обработки данных. Современные технологии построения web-сайтов дают возможность подойти достаточно близко к этому идеалу. Тем не менее, даже без применения многоуровневых приложений можно придерживаться стиля, позволяющего изменять любой из этих аспектов, не затрагивая (или почти не затрагивая) двух других. Рассуждения на эту тему будут продолжены в разделе, посвященном средам разработки.

CGI-сценарии

Классический путь создания приложений для WWW — написание CGI-сценариев (иногда говорят — скриптов). CGI (Common Gateway Interface, общий шлюзовой интерфейс) — это стандарт, регламентирующий взаимодействие сервера с внешними приложениями. В случае с WWW, web-сервер может направить запрос на генерацию страницы по определенному сценарию. Этот сценарий, получив на вход данные от web-сервера (тот, в свою очередь, мог получить их от пользователя), генерирует готовый объект (изображение, аудиоданные, таблицу стилей и т.п.).

При вызове сценария Web-сервер передает ему информацию через стандартный ввод, переменные окружения и, для ISINDEX, через аргументы командной строки (они доступны через sys.argv).

Два основных метода передачи данных из заполненной в браузере формы Web-серверу (и CGI-сценарию) — GET и POST. В зависимости от метода данные передаются по-разному. В первом случае они кодируются и помещаются прямо в URL, например: http://host/cgi-bin/a.cgi?a=1&b=3. Сценарий получает их в переменной окружения с именем QUERY_STRING. В случае метода POST они передаются на стандартный ввод.

Для корректной работы сценарии помещаются в предназначенный для этого каталог на web-сервере (обычно он называется cgi-bin) или, если это разрешено конфигурацией сервера, в любом месте среди документов HTML. Сценарий должен иметь признак исполняемости. В системе Unix его можно установить с помощью команды chmod a+x.

Следующий простейший сценарий выводит значения из словаря os.environ и позволяет увидеть, что же было ему передано:

Пример  
 
 #!/usr/bin/python
 
 import os
 print """Content-Type: text/plain
 
 %s""" % os.environ
 

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

Основные переменные окружения, достаточные для создания сценариев:

Пример  
 
 QUERY_STRING
 

Строка запроса.

Пример  
 
 REMOTE_ADDR
 

IP-адрес клиента.

Пример  
 
 REMOTE_USER
 

Имя клиента (если он был идентифицирован).

Пример  
 
 SCRIPT_NAME
 

Имя сценария.

Пример  
 
 SCRIPT_FILENAME
 

Имя файла со сценарием.

Пример  
 
 SERVER_NAME
 

Имя сервера.

Пример  
 
 HTTP_USER_AGENT
 

Название броузера клиента.

Пример  
 
 REQUEST_URI
 

Строка запроса (URI).

Пример  
 
 HTTP_ACCEPT_LANGUAGE
 

Желательный язык документа.

Вот что может содержать словарь os.environ в CGI-сценарии:

Пример  
 
 {
 'DOCUMENT_ROOT': '/var/www/html', 
 'SERVER_ADDR': '127.0.0.1', 
 'SERVER_PORT': '80', 
 'GATEWAY_INTERFACE': 'CGI/1.1', 
 'HTTP_ACCEPT_LANGUAGE': 'en-us, en;q=0.50', 
 'REMOTE_ADDR': '127.0.0.1', 
 'SERVER_NAME': 'rnd.onego.ru',
 'HTTP_CONNECTION': 'close', 
 'HTTP_USER_AGENT': 'Mozilla/5.0 (X11; U; Linux i586; en-US; 
 rv:1.0.1) Gecko/20021003', 
 'HTTP_ACCEPT_CHARSET': 'ISO-8859-1, utf-8;q=0.66, *;q=0.66', 
 'HTTP_ACCEPT': 'text/xml,application/xml,application/xhtml+xml,
 text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,
 image/gif;q=0.2,text/css,*/*;q=0.1',
 'REQUEST_URI': '/cgi-bin/test.py?a=1', 
 'PATH': '/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin',
 'QUERY_STRING': 'a=1&b=2', 
 'SCRIPT_FILENAME': '/var/www/cgi-bin/test.py', 
 'HTTP_KEEP_ALIVE': '300', 
 'HTTP_HOST': 'localhost', 
 'REQUEST_METHOD': 'GET', 
 'SERVER_SIGNATURE': 'Apache/1.3.23 Server at rnd.onego.ru Port 80', 
 'SCRIPT_NAME': '/cgi-bin/test.py', 
 'SERVER_ADMIN': 'root@localhost', 
 'SERVER_SOFTWARE': 'Apache/1.3.23 (Unix)  (Red-Hat/Linux) 
 mod_python/2.7.8 Python/1.5.2 PHP/4.1.2',
 'SERVER_PROTOCOL': 'HTTP/1.0', 
 'REMOTE_PORT': '39251'
 }
 

Следующий CGI-сценарий выдает черный квадрат (в нем используется модуль Image для обработки изображений):

Пример  
 
 #!/usr/bin/python
 
 import sys
 print """Content-Type: image/jpeg
 """
 
 import Image
 i = Image.new("RGB", (10,10))
 i.im.draw_rectangle((0,0,10,10), 1)
 i.save(sys.stdout, "jpeg")
 

Модуль cgi

В Python имеется поддержка CGI в виде модуля cgi. Следующий пример показывает некоторые из его возможностей:

Пример  
 
 #!/usr/bin/python
 # -*- coding: cp1251 -*-
 import cgi, os
 
 # анализ запроса
 f = cgi.FieldStorage()
 if f.has_key("a"):
   a = f["a"].value
 else:
   a = "0"
 
 # обработка запроса
 b = str(int(a)+1)
 mytext = open(os.environ["SCRIPT_FILENAME"]).read()
 mytext_html = cgi.escape(mytext)
 
 # формирование ответа
 print """Content-Type: text/html
 
 <html><head><title>Решение примера: %(b)s = %(a)s + 1</title></head>
 <body>
 %(b)s
 <table width="80%%"><tr><td>
 <form action="me.cgi" method="GET">
 <input type="text" name="a" value="0" size="6">
 <input type="submit" name="b" value="Обработать">
 </form></td></tr></table>
 <pre>
 %(mytext_html)s
 </pre>
 </body></html>""" % vars()
 

В этом примере к заданному в форме числу прибавляется 1. Кроме того, выводится исходный код самого сценария. Следует заметить, что для экранирования символов >, <, & использована функция cgi.escape(). Для формирования Web-страницы применена операция форматирования. В качестве словаря для выполнения подстановок использован словарь vars() со всеми локальными переменными. Знаки процента пришлось удвоить, чтобы они не интерпретировались командой форматирования. Стоит обратить внимание на то, как получено значение от пользователя. Объект FieldStorage «почти» словарь, с тем исключением, что для получения обычного значения нужно дополнительно посмотреть атрибут value. Дело в том, что в сценарий могут передаваться не только текстовые значения, но и файлы, а также множественные значения с одним и тем же именем.

Осторожно!

При обработке входных значений CGI-сценариев нужно внимательно и скрупулезно проверять допустимые значения. Лучше считать, что клиент может передать на вход все, что угодно. Из этого всего необходимо выбрать и проверить только то, что ожидает сценарий.

Например, не следует подставлять полученные от пользователя данные в путь к файлу, в качестве аргументов к функции eval() и ей подобных; параметров командной строки; частей в SQL-запросах к базе данных. Также не стоит вставлять полученные данные напрямую в формируемые страницы, если эти страницы будет видеть не только клиент, заказавший URL (например, такая ситуация обычна в web-чатах, форумах, гостевых книгах), и даже в том случае, если единственный читатель этой информации — администратор сайта. Тот, кто смотрит страницы с непроверенным HTML-кодом, поступившим напрямую от пользователя, рискуют обработать в своем браузере зловредный код, использующий брешь в его защите.

Даже если CGI-сценарий используется исключительно другими сценариями через запрос на URL, нужно проверять входные значения столь же тщательно, как если бы данные вводил пользователь. (Так как недоброжелатель может подать на web-сервер любые значения).

В примере выше проверка на допустимость произведена при вызове функции int(): если было бы задано нечисловое значение, сценарий аварийно завершился, а пользователь увидел Internal Server Error.

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

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

В приведенных примерах имена появлялись в строке запроса только один раз. Некоторые формы порождают несколько значений для одного имени. Получить все значения можно с помощью метода getlist():

Пример  
 
 lst = form.getlist("fld")
 

Список lst будет содержать столько значений, сколько полей с именем fld получено из web-формы (он может быть и пустым, если ни одно поле с заданным именем не было заполнено).

В некоторых случаях необходимо передать на сервер файлы (сделать upload). Следующий пример и комментарий к нему помогут разобраться с этой задачей:

Пример  
 
 #!/usr/bin/env python
 import cgi
 
 form = cgi.FieldStorage()
 file_contents = ""
 if form.has_key("filename"):
   fileitem = form["filename"]
   if fileitem.file:  
     file_contents = """<P>Содержимое переданного файла:
     <PRE>%s</PRE>""" % fileitem.file.read()
 
 print """Content-Type: text/html
 
 <HTML><HEAD><TITLE>Загрузка файла</TITLE></HEAD>
 <BODY><H1>Загрузка файла</H1>
 <P><FORM ENCTYPE="multipart/form-data" 
 ACTION="getfile.cgi" METHOD="POST">
 <br>Файл: <INPUT TYPE="file" NAME="filename">
 <br><INPUT TYPE="submit" NAME="button" VALUE="Передать файл">
 </FORM>
 %s
 </BODY></HTML>""" % file_contents
 

В начале следует рассмотреть web-форму, которая приведена в конце сценария: именно она будет выводиться пользователю при обращении по CGI-сценарию. Форма имеет поле типа file, которое в web-броузере представляется полоской ввода и кнопкой «Browse». Нажимая на кнопку «Browse», пользователь выбирает файл, доступный в ОС на его компьютере. После этого он может нажать кнопку «Передать файл» для передачи файла на сервер.

Для отладки CGI-сценария можно использовать модуль cgitb. При возникновении ошибки этот модуль выдаст красочную HTML-страницу с указанием места возбуждения исключения. В начале отлаживаемого сценария нужно поставить

Пример  
 
 import cgitb
 cgitb.enable(1)
 

Или, если не нужно показывать ошибки в браузере:

Пример  
 
   import cgitb
   cgitb.enable(0, logdir="/tmp")
 

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


Что после CGI?

К сожалению, строительство интерактивного и посещаемого сайта на основе CGI имеет свои ограничения, главным образом, связанные с производительностью. Ведь для каждого запроса нужно вызвать как минимум один сценарий (а значит — запустить интерпретатор Python), из него, возможно, сделать соединение с базой данных и т.д. Время запуска интерпретатора Python достаточно невелико, тем не менее, на занятом сервере оно может оказывать сильное влияние на загрузку процессора.

Желательно, чтобы интерпретатор уже находился в оперативной памяти, и были доступны соединения с базой данных.

Такие технологии существуют и обычно опираются на модули, встраиваемые в web-сервер.

Для ускорения работы CGI используются различные схемы, например, FastCGI или PCGI (Persistent CGI). В данной лекции предлагается к рассмотрению специальным модуль для web-сервера Apache, называемый mod_python.

Пусть модуль установлен на web-сервере в соответствии с инструкциями, данными в его документации.

Модуль mod_python позволяет сценарию-обработчику вклиниваться в процесс обработки HTTP-запроса сервером Apache на любом этапе, для чего сценарий должен иметь определенным образом названные функции.

Сначала нужно выделить каталог, в котором будет работать сценарий-обработчик. Пусть это каталог /var/www/html/mywebdir. Для того чтобы web-сервер знал, что в этом каталоге необходимо применять mod_python, следует добавить в файл конфигурации Apache следующие строки:

Пример  
 
 <Directory "/var/www/html/mywebdir">
   AddHandler python-program .py
   PythonHandler mprocess
 </Directory>
 

После этого необходимо перезапустить web-сервер и, если все прошло без ошибок, можно приступать к написанию обработчика mprocess.py. Этот сценарий будет реагировать на любой запрос вида http://localhost/*.py.

Следующий сценарий mprocess.py выведет в браузере страницу со словами Hello, world!:

Пример  
 
 from mod_python import apache
 
 def handler(req):
   req.content_type = "text/html"
   req.send_http_header()
   req.write("""<HTML><HEAD><TITLE>Hello, world!</TITLE></HEAD>
   <BODY>Hello, world!</BODY></HTML>""")
   return apache.OK
 

Отличия сценария-обработчика от CGI-сценария:

  • Сценарий-обработчик не запускается при каждом HTTP-запросе: он уже находится в памяти, и из него вызываются необходимые функции-обработчики (в приведенном примере такая функция всего одна — handler()). Каждый процесс-потомок web-сервера может иметь свою копию сценария и интерпретатора Python.
  • Как следствие п.1 различные HTTP-запросы делят одни и те же глобальные переменные. Например, таким образом можно инициализировать соединение с базой данных и применять его во всех запросах (хотя в некоторых случаях потребуются блокировки, исключающие одновременное использование соединения разными потоками (нитями) управления).
  • Обработчик задействуется при обращении к любому «файлу» с расширением py, тогда как CGI-сценарий обычно запускается при обращении по конкретному имени.
  • В сценарии-обработчике нельзя рассчитывать на то, что он увидит модули, расположенные в том же каталоге. Возможно, придется добавить некоторые каталоги в sys.path.
  • Текущий рабочий каталог (его можно узнать с помощью функции os.getcwd()) также не находится в одном каталоге с обработчиком.
  • #!-строка в первой строке сценария не определяет версию интерпретатора Python. Работает версия, для которой был скомпилирован mod_python.
  • Все необходимые параметры передаются в обработчик в виде Request-объекта. Возвращаемые значения также передаются через этот объект.
  • Web-сервер замечает, что сценарий-обработчик изменился, но не заметит изменений в импортируемых в него модулях. Команда touchmprocess.py обновит дату изменения файла сценария.
  • Отображение os.environ в обработчике может быть обрезанным. Кроме того, вызываемые из сценария-обработчика другие программы его не наследуют, как это происходит при работе с CGI-сценариями. Переменные можно получить другим путем: req.add_common_vars(); params = req.subprocess_env.
  • Так как сценарий-обработчик не является «одноразовым», как CGI-сценарий, из-за ошибок программирования (как самого сценария, так и других компонентов) могут возникать утечки памяти (программа не освобождает ставшую ненужной память). Следует установить значение параметра MaxRequestsPerChild (максимальное число запросов, обрабатываемое одним процессом-потомком) больше нуля.

Другой возможный обработчик — сценарий идентификации:

Пример  
 
 def authenhandler(req):
   password = req.get_basic_auth_pw()
   user = req.connection.user
   if user == "user1" and password == "secret":
       return apache.OK
   else:
       return apache.HTTP_UNAUTHORIZED
 

Эту функцию следует добавить в модуль mprocess.py, который был рассмотрен ранее. Кроме того, нужно дополнить конфигурацию, назначив обработчик для запросов идентификации (PythonAuthenHandler), а также обычные для Apache директивы AuthType, AuthName, require, определяющие способ авторизации:

Пример  
 
 <Directory "/var/www/html/mywebdir">
   AddHandler python-program .py
   PythonHandler mprocess
   PythonAuthenHandler mprocess
   AuthType Basic
   AuthName "My page"
   require valid-user
 </Directory>
 

Разумеется, это — всего лишь пример. В реальности идентификация может быть устроена намного сложнее.

Другие возможные обработчики (по документации к mod_python можно уточнить, в какие моменты обработки запроса они вызываются):

Пример  
 
 PythonPostReadRequestHandler
 

Обработка полученного запроса сразу после его получения.

Пример  
 
 PythonTransHandler
 

Позволяет изменить URI запроса (в том числе имя виртуального сервера).

Пример  
 
 PythonHeaderParserHandler
 

Обработка полей запроса.

Пример  
 
 PythonAccessHandler
 

Обработка ограничений доступа (например, по IP-адресу).

Пример  
 
 PythonAuthenHandler
 

Идентификация пользователя.

Пример  
 
 PythonTypeHandler
 

Определение и/или настройка типа документа, языка и т.д.

Пример  
 
 PythonFixupHandler
 

Изменение полей непосредственно перед вызовом обработчиков содержимого.

Пример  
 
 PythonHandler
 

Основной обработчик запроса.

Пример  
 
 PythonInitHandler
 

PythonPostReadRequestHandler или PythonHeaderParserHandler в зависимости от нахождения в конфигурации web-сервера.

Пример  
 
 PythonLogHandler
 

Управление записью в логи.

Пример  
 
 PythonCleanupHandler
 

Обработчик, вызываемый непосредственно перед уничтожением Request-объекта.

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

С помощью mod_python можно строить web-сайты с динамическим содержимым и контролировать некоторые аспекты работы web-сервера Apache через Python-сценарии.


Среды разработки

Для создания Web-приложений применяются и более сложные средства, чем web-сервер с расположенными на нем статическими документами и CGI-сценариями. В зависимости от назначения такие программные системы называются серверами web-приложений, системами управления содержимым (CMS, Content Management System), системы web-публикации и средствами для создания WWW-порталов. Причем CMS-система может быть выполнена как web-приложение, а средства для создания порталов могут базироваться на системах web-публикации, для которых CMS-система является подсистемой. Поэтому, выбирая систему для конкретных нужд, стоит уточнить, какие функции она должна выполнять.

Язык Python, хотя и уступает PHP по количеству созданных на нем web-систем, имеет несколько достаточно популярных приложений. Самым ярким примером средства для создания сервера web-приложений является Zope (произносится «зоп») (см. [1]) (Z Object Publishing Environment, среда публикации объектов). Zope имеет встроенный web-сервер, но может работать и с другими Web-серверами, например, Apache. На основе Zope можно строить web-порталы, например, с помощью Plone/Zope, но можно разрабатывать и собственные web-приложения. При этом Zope позволяет разделить Форму, Содержание и Логику до такой степени, что Содержанием могут заниматься одни люди (менеджеры по содержимому), Формой — другие (web-дизайнеры), а Логикой — третьи (программисты). В случае с Zope Логику можно задать с помощью языка Python (или, как вариант, Perl), Форма может быть создана в графических или специализированных web-редакторах, а работа с содержимым организована через Web-формы самого Zope.

Zope и его объектная модель

В рамках этой лекции невозможно детально рассмотреть такой инструмент как Zope, поэтому стоит лишь заметить, что он достаточно интересен не только в качестве среды разработки web-приложений, но и с точки зрения подходов. Например, уникальная объектно-ориентированная модель Zope позволяет довольно гибко описывать требуемое приложение.

Zope включает в себя следующие возможности:

  • Web-сервер. Zope может работать с Web-серверами через CGI или использовать свой встроенный Web-сервер (ZServer).
  • Среда разработчика выполнена как Web-приложение. Zope позволяет создавать Web-приложения через Web-интерфейс.
  • Поддержка сценариев. Zope поддерживает несколько языков сценариев: Python, Perl и собственный DTML (Document Template Markup Language, язык разметки шаблона документа).
  • База данных объектов. Zope использует в своей работе устойчивые объекты, хранимые в специальной базе данных (ZODB). Имеется достаточно простой интерфейс для управления этой базой данных.
  • Интеграция с реляционными базами данных. Zope может хранить свои объекты и другие данные в реляционных СУБД: Oracle, PostgreSQL, MySQL, Sybase и т.п.

В ряду других подобных систем Zope на первый взгляд кажется странным и неприступным, однако тем, кто с ним «на ты», он открывает большие возможности.

Разработчики Zope исходили из лежащей в основе WWW объектной модели, в которой загрузку документа по URI можно сравнить с отправкой сообщения объекту. Объекты Zope разложены по папкам (folders), к которым привязаны политики доступа для пользователей, имеющих определенные роли. В качестве объектов могут выступать документы, изображения, мультимедиа-файлы, адаптеры к базам данных и т.п.

Документы Zope можно писать на языке DTML — дополнении HTML с синтаксисом для включения значений подобно SSI (Server-Side Include). Например, для вставки переменной с названием документа можно использовать

Пример  
 
 <!- #var document_title ->
 

Следует заметить, что объекты Zope могут иметь свои атрибуты, а также методы, в частности, написанные на языке Python. Переменные же могут появляться как из заданных пользователем значений, так и из других источников данных (например, из базы данных посредством выполнения выборки функцией SELECT).

Сейчас для описания документа Zope все чаще применяется ZPT (Zope Page Templates, шаблоны страниц Zope), которые в свою очередь используют TAL (Template Attribute Language, язык шаблонных атрибутов). Он позволяет заменять, повторять или пропускать элементы документа описываемого шаблоном документа. «Операторами» языка TAL являются XML-атрибуты из пространства имен TAL. Пространство имен сегодня описывается следующим идентификатором:

Пример  
 
 xmlns:tal="http://xml.zope.org/namespaces/tal&quot;
 

Оператор TAL имеет имя и значение (что выражается именем и значением атрибута). Внутри значения обычно записано TAL-выражение, синтаксис которого описывается другим языком — TALES (Template Attribute Language Expression Syntax, синтаксис выражений TAL).

Таким образом, ZPT наполняет содержимым шаблоны, интерпретируя атрибуты TAL. Например, чтобы Zope подставил название документа (тег TITLE), шаблон может иметь следующий код:

Пример  
 
 <title tal:content="here/title">Doc Title</title>
 

Стоит заметить, что приведенный код сойдет за код на HTML, то есть, Web-дизайнер может на любом этапе работы над проектом редактировать шаблон в HTML-редакторе (при условии, что тот сохраняет незнакомые атрибуты из пространства имен tal). В этом примере here/titleявляется выражением TALES. Текст Doc Title служит ориентиром для web-дизайнера и заменяется значением выражения here/title, то есть, будет взято свойство title документа Zope.

Примечание:

В Zope объекты имеют свойства. Набор свойств зависит от типа объекта, но может быть расширен в индивидуальном порядке. Свойство id присутствует всегда, свойство title обычно тоже указывается.

В качестве более сложного примера можно рассмотреть организацию повтора внутри шаблона (для опробования этого примера в Zope нужно добавить объект Page Template):

Пример  
 
 <ul>
    <li tal:define="s modules/string" 
        tal:repeat="el python:s.digits"> 
    <a href="DUMMY"
       tal:attributes="href string:/digit/$el"
       tal:content="el">SELECTION</a>
   </li> 
 </ul>
 

Этот шаблон породит следующий результат:

Пример  
 
 <ul>
    <li><a href="/digit/0">0</a></li>
    <li><a href="/digit/1">1</a></li>
    <li><a href="/digit/2">2</a></li>
    <li><a href="/digit/3">3</a></li>
    <li><a href="/digit/4">4</a></li>
    <li><a href="/digit/5">5</a></li>
    <li><a href="/digit/6">6</a></li>
    <li><a href="/digit/7">7</a></li>
    <li><a href="/digit/8">8</a></li>
    <li><a href="/digit/9">9</a></li>
 </ul>
 

Здесь нужно обратить внимание на два основных момента:

  • в шаблоне можно использовать выражения Python (в данном примере переменная s определена как модуль Python) и переменную-счетчик цикла el, которая проходит итерации по строке string.digits.
  • с помощью TAL можно задавать не только содержимое элемента, но и атрибута тега (в данном примере использовался атрибут href).

Детали можно узнать по документации. Стоит лишь заметить, что итерация может происходить по самым разным источникам данных: содержимому текущей папки, выборке из базы данных или, как в приведенном примере, по объекту Python.

Любой программист знает, что программирование тем эффективнее, чем лучше удалось «расставить скобки», выведя «общий множитель за скобки». Другими словами, хорошие программисты должны быть достаточно «ленивы», чтобы найти оптимальную декомпозицию решаемой задачи. При проектировании динамического web-сайта Zope позволяет разместить «множители» и «скобки» так, чтобы достигнуть максимального повторного использования кода (как разметки, так и сценариев). Помогает этому уникальный подход к построению взаимоотношений между объектами: заимствование (acquisition).

Пусть некоторый объект (документ, изображение, сценарий, подключение к базе данных и т.п.) расположен в папке Example. Теперь объекты этой папки доступны по имени из любых нижележащих папок. Даже политики безопасности заимствуются более глубоко вложенными папками от папок, которые ближе к корню. Заимствование является очень важной концепцией Zope, без понимания которой Zope сложно грамотно применять, и наоборот, ее понимание позволяет экономить силы и время, повторно используя объекты в разработке.

Самое интересное, что заимствовать объекты можно также из параллельных папок. Пусть, например, рядом с папкой Example находится папка Zigzag, в которой лежит нужный объект (его наименование note). При этом в папке Example программиста интересует объект index_html, внутри которого вызывается note. Обычный путь к объекту index_html будет происходить по URI вроде http://zopeserver/Example/. А вот если нужно использовать note из Zigzag (и в папке Example его нет), то URI будет: http://zopeserver/Zigzag/Example/. Таким образом, указание пути в Zope отличается от традиционного пути, скажем, в Unix: в пути могут присутствовать «зигзаги» через параллельные папки, дающие возможность заимствовать объекты из этих папок. Таким образом, можно сделать конкретную страницу, комбинируя один или несколько независимых аспектов.

Заключение

В этой лекции были рассмотрены различные подходы к использованию Python в web-приложениях. Самый простой способ реализации web-приложения — использование CGI-сценариев. Более сложным является использование специальных модулей для web-сервера, таких как mod_python. Наконец, существуют технологии вроде Zope, которые предоставляют специализированные сервисы, позволяющие создавать web-приложения.

.. seealso::

    * https://ru.wikipedia.org/wiki/CGI
    * http://www.ietf.org/rfc/rfc3875.txt
    * `Лекции ОмГТУ, кафедра АСОИУ <http://www.4stud.info/web-programming/cgi.html>`_
    * http://webpython.codepoint.net/cgi_tutorial

CGI (от англ. Common Gateway Interface — «общий интерфейс шлюза») — стандарт интерфейса, используемого для связи внешней программы с веб-сервером. Программу, которая работает по такому интерфейсу совместно с веб-сервером, принято называть шлюзом, хотя многие предпочитают названия «скрипт» (сценарий) или «CGI-программа».

Поскольку гипертекст статичен по своей природе, веб-страница не может непосредственно взаимодействовать с пользователем. До появления JavaScript, не было иной возможности отреагировать на действия пользователя, кроме как передать введенные им данные на веб-сервер для дальнейшей обработки. В случае CGI эта обработка осуществляется с помощью внешних программ и скриптов, обращение к которым выполняется через стандартизованный (см. RFC 3875: CGI Version 1.1) интерфейс — общий шлюз.

Упрощенная модель, иллюстрирующая работу CGI:

/_static/5.web.server/cgi.*

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

Как работает CGI?

Обобщенный алгоритм работы через CGI можно представить в следующем виде:

  1. Клиент запрашивает CGI-приложение по его URI.
  2. Веб-сервер принимает запрос и устанавливает переменные окружения, через них приложению передаются данные и служебная информация.
  3. Веб-сервер перенаправляет запросы через стандартный поток ввода (stdin) на вход вызываемой программы.
  4. CGI-приложение выполняет все необходимые операции и формирует результаты в виде HTML.
  5. Сформированный гипертекст возвращается веб-серверу через стандартный поток вывода (stdout). Сообщения об ошибках передаются через stderr.
  6. Веб-сервер передает результаты запроса клиенту.

Области применения CGI

Наиболее частая задача, для решения которой применяется CGI — создание интерактивных страниц, содержание которых зависит от действий пользователя. Типичными примерами таких веб-страниц является форма регистрации на сайте или форма для отправки комментария. Другая область применения CGI, остающаяся за кулисами взаимодействия с пользователем, связана со сбором и обработкой информации о клиенте: установка и чтение «печенюшек»-cookies; получение данных о браузере и операционной системе; подсчет количества посещений веб-страницы; мониторинг веб-трафика и т.п.

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

Warning

CGI — это не язык программирования! Это простой протокол, позволяющий веб-серверу передавать данные через stdin и читать их из stdout. Поэтому в качестве CGI-обработчика может использоваться любая серверная программа, способная работать со стандартными потоками ввода-вывода.

Примеры

Пример на Python:

#!/usr/bin/python
print("""Content-Type: text/plain

Hello, world!""")

В этом коде строка #!/usr/bin/python указывает полный путь к интерпретатору Python.

Пример на Си:

#include <stdio.h>
int main(void) {
  printf("Content-Type: text/plainnn");
  printf("Hello, world!nn");
  return 0;
}

Строка Content-type: text/htmlnn — http-заголовок, задающий тип содержимого (mime-type). Удвоенный символ разрыва строки (nn) — обязателен, он отделяет заголовки от тела сообщения.

Все скрипты, как правило, помещают в каталог cgi (или cgi-bin) сервера, но это необязательно: скрипт может располагаться где угодно, но при этом большинство веб-серверов требуют специальной настройки. В веб-сервере Apache, например, такая настройка может производиться при помощи общего файла настроек httpd.conf или с помощью файла .htaccess в том каталоге, где содержится этот скрипт. Также скрипты должны иметь права на исполнение (chmod +x hello.py).

Переменные окружения

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

Пример вывода переменных окружения CGI-скрипта:

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/2.environment.py
   :language: python
   :linenos:

/_static/5.web.server/cgi_env.png

Преимущества CGI

  • Процесс CGI скрипта не зависит от Веб-сервера и, в случае падения, никак не отразится на работе последнего
  • Может быть написан на любом языке программирования
  • Поддерживается большинством Веб-серверов

Недостатки CGI

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

Альтернативы

  • FastCGI — дальнейшее развитие технологии CGI. Поддерживается
    многими Веб-серверами, например Nginx.
  • Веб-сервера, в которые уже встроена поддержка дополнительных
    стандартов и протоколов, таких как WSGI (Gunicorn, waitress, uwsgi)
  • Веб-сервер, функционал которого расширяется через модули, например,
    Apache (mod_wsgi, mod_php, mod_fastcgi)

Практика

.. seealso::

   * https://docs.python.org/2/library/cgihttpserver.html
   * https://docs.python.org/3/library/http.server.html

Для запуска CGI сервера необходимо перейти в директорию sourcecode
и выполнить команду:

python -m CGIHTTPServer 8000

или

python3 -m http.server --cgi 8000

или cgiserver.py

.. literalinclude:: /../sourcecode/5.web.server/cgiserver.py
   :language: python
   :linenos:

Теперь CGI-скрипты доступны на 8000 порту,
например по адресу http://localhost:8000/cgi-bin/1.hello.py

  • Пример CGI скриптов на Python
  • Пример CGI скриптов на C++

Note

Для компиляции кода на C++ необходимо установить библиотеку cgicc:

sudo apt-get install libcgicc5-dev

Пример компиляции:

g++ -o 3.get.post.cgi 3.get.post.cpp -lcgicc

Hello World!

Note

  • http://localhost:8000/cgi-bin/1.hello.cgi
  • http://localhost:8000/cgi-bin/1.hello.go.cgi
  • http://localhost:8000/cgi-bin/1.hello.py
  • http://localhost:8000/cgi-bin/1.hello.rb

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/1.hello.py
   :language: python
   :linenos:

Ruby

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/1.hello.rb
   :language: ruby
   :linenos:

C++

Для компиляции: make 1_hello

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/1.hello.cpp
   :language: cpp
   :linenos:

Go

Для компиляции: make 1_hello_go

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/1.hello.go
   :language: go
   :linenos:

Вывод переменных окружения

Note

  • http://localhost:8000/cgi-bin/2.environment.cgi
  • http://localhost:8000/cgi-bin/2.environment.py
  • http://localhost:8000/cgi-bin/2.environment.rb

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/2.environment.py
   :language: python
   :linenos:

Ruby

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/2.environment.rb
   :language: ruby
   :linenos:

C++

Для компиляции: make 2_environment

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/2.environment.cpp
   :language: cpp
   :linenos:

GET и POST запросы

Note

  • http://localhost:8000/cgi-bin/3.get.post.cgi?first_name=Lev&last_name=Tolstoy
  • http://localhost:8000/cgi-bin/3.get.post.py?first_name=Lev&last_name=Tolstoy
  • http://localhost:8000/cgi-bin/3.get.post.rb?first_name=Lev&last_name=Tolstoy
  • GET (action=»http://localhost:8000/cgi-bin/3.get.post.cgi» method=»get»)

    .. todo:: Add LaTeX support
    
    

    First Name:
    Last Name:

  • POST (action=»http://localhost:8000/cgi-bin/3.get.post.cgi» method=»post»)

    .. todo:: Add LaTeX support
    
    

    First Name:
    Last Name:

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/3.get.post.py
   :language: python
   :linenos:

Ruby

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/3.get.post.rb
   :language: ruby
   :linenos:

C++

Для компиляции: make 3_get_post

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/3.get.post.cpp
   :language: cpp
   :linenos:

Checkbox

.. todo:: Add LaTeX support

Maths
Physics

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/4.checkbox.py
   :language: python
   :linenos:

C++

Для компиляции: make 4_checkbox

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/4.checkbox.cpp
   :language: cpp
   :linenos:

Radio

.. todo:: Add LaTeX support

Maths
Physics

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/5.radio.py
   :language: python
   :linenos:

C++

Для компиляции: make 5_radio

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/5.radio.cpp
   :language: cpp
   :linenos:

TextArea

.. todo:: Add LaTeX support

Type your text here…

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/6.textarea.py
   :language: python
   :linenos:

C++

Для компиляции: make 6_textarea

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/6.textarea.cpp
   :language: cpp
   :linenos:

Drop Down Box

.. todo:: Add LaTeX support

Maths
Physics

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/7.dropdown.py
   :language: python
   :linenos:

C++

Для компиляции: make 7_dropdown

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/7.dropdown.cpp
   :language: cpp
   :linenos:

Печать Cookie

Note

  • http://localhost:8000/cgi-bin/8.getcookie.cgi
  • http://localhost:8000/cgi-bin/8.getcookie.py

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/8.getcookie.py
   :language: python
   :linenos:

C++

Для компиляции: make 8_getcookie

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/8.getcookie.cpp
   :language: cpp
   :linenos:

Установка Cookie

Note

  • http://localhost:8000/cgi-bin/9.setcookie.cgi
  • http://localhost:8000/cgi-bin/9.setcookie.py

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/9.setcookie.py
   :language: python
   :linenos:

C++

Для компиляции: make 9_setcookie

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/9.setcookie.cpp
   :language: cpp
   :linenos:

Загрузка файлов

.. todo:: Add LaTeX support

File:

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/10.fileupload.py
   :language: python
   :linenos:

C++

Для компиляции: make 10_fileupload

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/10.fileupload.cpp
   :language: cpp
   :linenos:

Отладка

.. seealso::

   * http://pymotw.com/2/cgitb/
   * https://docs.python.org/2/library/cgitb.html

Python

.. literalinclude:: /../sourcecode/5.web.server/cgi-bin/test.py
   :language: python
   :linenos:
   :emphasize-lines: 24

/_static/5.web.server/cgitb.png

Понравилась статья? Поделить с друзьями:
  • Castle flipper праздник удался
  • Carol singing праздник перевод
  • Carnival праздник дата
  • Canterville ghost сценарий
  • Calgary stampede праздник