|
Интерфейс CGI
|
|
Главная страница \ WEB программирование \ Интерфейс CGI |
Общие вопросы |
Алгоритм работы с использованием интерфейса CGI
Рассмотрим алгоритм взаимодействия приложения клиента (для краткости будем
называть его браузером) с сервером при использовании интерфейса CGI:
Необходимый инструментарий разработчика
Как уже должно быть очевидно, для отладки и работы с CGI скриптами необходимо
Выбор языка программирования
Скажу пару слов на извечную тему - "на чем писать". Я не хочу ничего
рекомендовать, просто отмечу, что с точки зрения переносимости удобны C, Java,
Perl, PHP; точки зрения быстродействия - C, Delphi; с точки зрения
отвратительной читаемости и отменного набора возможностей по манипулированию
строками - PERL и т.п.
У Delphi есть у данном случаен минус - программа на Delphi работает только под
Windows, что ограничивает его применение. С другой стророны, с появлением Kulix у разработчика
появилась возможность переносить приложения Delphi на платформу Linux с минимальными переделками
Теоретические основы CGI |
Переменные окружения, устанавливаемые WEB сервером.
Переменная | Назначение |
AUTH_TYPE | Используется для идентификации пользователя (при условии, что WEB сервер сконфигурирован так, что поддерживает идентификацию пользователя) |
CONTENT_LENGTH | Этот параметр содержит точный размер данных (в байтах), переданных в запросе. Пример: CONTENT_LENGTH = 14671 |
CONTENT_TYPE | Тип данных, переданных в запросе. Пример: CONTENT_TYPE = text/html. Если результат заполнения формы передается по методу POST, то CONTENT_TYPE как правило равно application/x-www-form-urlencoded |
GATEWEY_INTERFACE | Номер выпуска используемой спецификации CGI. Имеет формат CGI/nnn, где nnn - номер выпуска. Пример: CGI/1.1 |
PATH_INFO | Возвращает дополнительную информацию о пути. Так, например, если вызывается скрипт zaitsev.smolen.elektara.ru/cgi-bin/test1.exe/path1/path2/1.htm, то PATH_INFO будет равно /path1/path2/1.htm |
PATH_TRANSLATED | Путь, который пригоден для непосредственного использования в файловых операциях (учитывается то, что в запросе могут финурировать виртуальные директории и т.п.). |
QUERY_STRING | Очень важный и часто используемый параметр. Содержит строчку, переданную в качестве запроса при вызове CGI скрипта. К примеру, URL имеет вид zaitsev.smolen.elektara.ru/cgi-bin/test1.exe?name=Oleg+Zaitsev. Особенности: все пробелы заменяются знаком "+", параметры разделяются знаком "&" и имеют формат <имя пареметра> = <значение>. Все непечатные символы заменяются их кодами в формате %dd, где dd - код символа. В данном примере QUREY_STRING= name=Oleg+Zaitsev |
REMOTE_ADDR | IP адрес пользователя. Очень полезный параметр для фиксации в протоколе. В собственной сети большого предприятия компьютеры пользователей как правило имеют жестко закрепленные IP адреса, что может применяться для разграничения доступа. Пример: 192.20.97.28 |
REMOTE_HOST | Имя узла, с которого делается запрос. Пример: 172.20.97.28 или zaitsev |
REMOTE_IDENT | Имя удаленного пользователя. Имеет формат имя.хост, например, zaitsev.www.smolen.elektra.ru |
REMOTE_USER | То-же, что и REMOTE_IDENT, но содержит только имя. Пример: zaitsev |
REQUEST_METOD | Позволяет определить тип запроса (GET или POST). Должен обязательно анализироваться, т.к. определяет дальнейший способ обработки информации |
SCRIPT_NAME | Содержит путь к скрипту. Например, скрипт лежит на zaitsev.smolen.elektra.ru/cgi-bin/test1.exe, тогда SCRIPT_NAME = /cgi-bin/test1.exe |
SERVER_NAME | Имя (или IP адрес) домена |
SERVER_PORT | Номер порта, используемый браузером для связи с сервером. По умолчанию используется порт 80. |
SERVER_PROTOCOL | Содержит версию протокола HTTP, используемую в запросе (см. протокол HTTP). Пример: HTTP/3.2 |
SERVER_SOFTWARE | Произвольная строчка, несущая информацию о названии WEB сервера, его версии и т.п. Ряд серверов позволяет запретить формирование этой строчки или подставить в нее любые данные |
HTTP_*** | Переменные с именами HTTP_*** являются необязательными и содержат значения необязательных параметров HTTP запроса с именами ***. При формировании этих параметров есть особенность - все символы переноса "-" заменяются на "_". Примеры: HTTP_ACCEPT, HTTP _USER_AGENT, HTTP_REFERER |
Формат ответа CGI скрипта
Корректно написанный CGI скрипт должен сформировать ответ, содержащий заголовок.
Заголовок отделяется от последующих данных пустой строкой (т.е. CR+LF, что
достигается оператором writeln;). Если заголовок не содержит директив WEB
серверу, то он считает, что скрипт сам формирует заголовок HTTP ответа и
передает его клиенту как есть. В настоящее время определено три директивы:
Директива | Назначение |
Content-type | Тип ответа скрипта. Например, если скрипт возвращает HTML документ, то возвращается Content-type: text/html |
Location | Указатель на документ, который должен быть возвращен в качестве ответа скрипта. Например, скрипт хочет переадресовать пользователя на некоторый документ. Тогда он формирует заголовок Location: http://www.chat.ru/~z_ol |
Status | Возвращает код статуса в стандартном формате. Применяется, если необходимо указание статуса, например Status: 404 Документ не найден |
От теории к практике - примеры программирования |
Hello, world
Current date/time is 31.01.01 10:43:30 |
program test1; uses Windows, Sysutils; begin // Формирование заголовка Writeln('Content-Type: text/html'); Writeln; // Формирование самого документа Writeln('<HTML><BODY>'); Writeln('Hello, worldЭтот пример типовой, поэтому рассмотрим его подробно. Первая особенность - прагма компилятора {$APPTYPE CONSOLE}, указывающая но то, что это консольное приложение. Наличие этой пракмы обязательно, т.к. только у консольного приложения можно пользоваться операторами writeln.
'); Writeln('Current date/time is ' + DateTimeToStr(Now)); Writeln('</BODY></HTML>'); end.
Пример 2. Простейший CGI, формирующий динамическую HTML страничку со значениями
всех переменных окружения и параметров командной строки.
Этот пример интересен не только в качестве примера, но и в качестве тестера - с его
помощью легко посмотреть значения, передаваемые конкренным WEB сервером в различных случаях.
Исходный текст примера:
program test2; {$APPTYPE CONSOLE} uses Windows, Sysutils; // Получение переменной окружения по ее имени Function GetEnv(AName : string) : String; var Buf : array[0..16000] of char; begin GetEnvironmentVariableA(PChar(AName), Buf, SizeOf(buf)); Result := Buf; end; var i : integer; begin Writeln('Content-Type: text/html'); Writeln; Writeln('<HTML><BODY>'); Writeln('Пример 2. Вывод всех переменных окружения, используемых для передачи параметров'); Writeln('<hr>'); Writeln('<pre>'); Writeln('AUTH_TYPE = ', GetEnv('AUTH_TYPE')); Writeln('CONTENT_LENGTH = ', GetEnv('CONTENT_LENGTH')); Writeln('CONTENT_TYPE = ', GetEnv('CONTENT_TYPE')); Writeln('GATEWEY_INTERFACE = ', GetEnv('GATEWEY_INTERFACE')); Writeln('PATH_INFO = ', GetEnv('PATH_INFO')); Writeln('PATH_TRANSLATED = ', GetEnv('PATH_TRANSLATED')); Writeln('QUERY_STRING = ', GetEnv('QUERY_STRING')); Writeln('REMOTE_ADDR = ', GetEnv('REMOTE_ADDR')); Writeln('REMOTE_HOST = ', GetEnv('REMOTE_HOST')); Writeln('REMOTE_IDENT = ', GetEnv('REMOTE_IDENT')); Writeln('REMOTE_USER = ', GetEnv('REMOTE_USER')); Writeln('REQUEST_METOD = ', GetEnv('REQUEST_METOD')); Writeln('SCRIPT_NAME = ', GetEnv('SCRIPT_NAME')); Writeln('SERVER_NAME = ', GetEnv('SERVER_NAME')); Writeln('SERVER_PORT = ', GetEnv('SERVER_PORT')); Writeln('SERVER_PROTOCOL = ', GetEnv('SERVER_PROTOCOL')); Writeln('SERVER_SOFTWARE = ', GetEnv('SERVER_SOFTWARE')); Writeln('<hr>'); Writeln('Параметры командной строки '); // Вывод параметров командной строки for i:=1 to ParamCount do Writeln(' ',inttostr(i),' = ',ParamStr(i)); Writeln('</pre><hr>'); Writeln('Current date/time is '+DateTimeToStr(Now)); Writeln('</BODY></HTML>'); end.В данном примере имеется функция , демонстрирующая, как читать значение переменной окружения через Windows API. Рассмотрим пример передачи бинарных данных (картинки, архива ...). Главная особенность (о которой не следует забывать !!) - необходимость корректного указания MIME типа передаваемых данных. Например, для передачи GIF картинки тип будет image/gif и т.п. Про это часто забывают. Исходный текст примера:
program test3; {$APPTYPE CONSOLE} uses Windows, Sysutils; var i, NR, NW : integer; f : file; Buf : array[1..1024*5] of byte; begin Writeln('Content-Type: image/gif'); Writeln; AssignFile(f, 'Delphi.gif'); Reset(f,1); repeat BlockRead(f, Buf, SizeOf(Buf), NR); for i:=1 to NR do Write(Chr(Buf[i])); until NR=0; CloseFile(f); end.Легко заметить, что передача ведется дубовым способом - побайтным выводом данных в стандартный поток вывода оператором Write. Ну, на то он и пример.
Пример 4. Обработка заполненной формы - передача по методу GET
Итак, разработаем для примера простейшую форму:
<html> <body> <form action="/cgi/test4.exe" method=GET> Имя пользователя <input type=text name="Name"><br> Пароль <input type=password name="passwd"><br> <hr> Некий переключатель с именем Radio1 <input type=radio name="Radio1" value="1">Пункт1 <input type=radio name="Radio1" CHECKED value="2">Пункт2 <input type=radio name="Radio1" value="3">Пункт3 <p> input type=submit VALUE="Передать"> </FORM> </body> </html> </code>
Для обработки переданных параметров имеет смысл написать библиотеку функций, которая облегчит процесс разбора и перекодировки параметров. Эту библиотеку можно скачать здесь (размер 2 кб).Библиотека содержит две функции:
// Получение переменной окружения по ее имени Function GetEnv(AName : string) : String; // Декодировать параметр Function DecodeParam(AParam : string) : String;Первая читает значение указанной переменной окружения, а вторая декодирует значение параметра (учитывая всю специфику - замену пробела на +, непечатных символов на их шеснадцатеричные коды). Кроме того, в библиотеке описан класс
// Класс, разбирающий переданные параметры TCGIParamsParser = class private FParamStr: string; procedure SetParamStr(const Value: string); protected FParamList : TStrings; public Constructor Create; // Создание Destructor Destroy; // Разрушение // Получение параметра по имени. Если параметр не описан или не имеет знечения, // то оно может быть заменено на Function GetParamVal(AName : string;DefVal : string = ''): string; published // Строчка параметров. При присвоении автоматически производится разбор Property ParamStr: string read FParamStr write SetParamStr; end;Итак, сам пример 4:
program test4; {$APPTYPE CONSOLE} uses Windows, ZCGI; var CGIParamsParser : TCGIParamsParser; begin Writeln('Content-Type: text/html'); Writeln; Writeln('<HTML><BODY>'); Writeln('Пример 4. Работа по методу GET'); Writeln('<hr>'); // Создание класса и разбор строки параметров CGIParamsParser := TCGIParamsParser.Create; CGIParamsParser.ParamStr := GetEnv('QUERY_STRING'); // Вывод ответа Writeln('<pre>'); Writeln('Имя пользователя :'+ CGIParamsParser.GetParamVal('Name','Не введено'),'Исходные тексты примеров можно скачать здесь примеры CGI.
'); Writeln('Пароль :'+ CGIParamsParser.GetParamVal('passwd','Не введен'),'
'); Writeln('В радио-переключателе Radio1 выбран пункт с значением :'+ CGIParamsParser.GetParamVal('Radio1'),'
'); Writeln('</pre><hr>'); Writeln('</BODY></HTML'); end.