![]() (C) Зайцев Олег 1998-2001 |
Программирование на Delphi
|
| Система | Реестр | Графика | Сети | Мультимедиа | WEB | Разработка_компонент | Железо | Прочее |
Защита программных продуктовЭта статья содержит базовые советы по созданию CGI скриптов. Приведен ряд примеров и необходимая справочная информация. Внимание !! Сайт переехал - он теперь расположен по адресу http://z-oleg.com/delphi, размещенные там материалы переработаны и дополнены. На z-ol.chat.ru остается копия, однако обновляться она больше не будет |
|
|
|
(C) Зайцев Олег, 2001
Алгоритм работы с использованием интерфейса CGI
Рассмотрим алгоритм взаимодействия приложения клиента (для краткости будем
называть его браузером) с сервером при использовании интерфейса CGI:
Необходимый инструментарий разработчика
Как уже должно быть очевидно, для отладки и работы с CGI скриптами необходимо
Выбор языка программирования
Скажу пару слов на извечную тему - "на чем писать". Я не хочу ничего
рекомендовать, просто отмечу, что с точки зрения переносимости удобны C,
Perl, PHP; точки зрения быстродействия - C, Delphi; с точки зрения
отвратительной читаемости и отменного набора возможностей по манипулированию
строками - PERL и т.п.
У Delphi есть у данном случаен минус - программа на Delphi работает только под
Windows, что ограничивает его применение.
Переменные окружения, устанавливаемые 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 Документ не найден |
program test1;
uses
Windows, Sysutils;
begin
// Формирование заголовка
Writeln('Content-Type: text/html');
Writeln;
// Формирование самого документа
Writeln('<HTML><BODY>');
Writeln('Hello, world
');
Writeln('Current date/time is ' + DateTimeToStr(Now));
Writeln('</BODY></HTML>');
end.
Этот пример типовой, поэтому рассмотрим его подробно. Первая особенность - прагма
компилятора {$APPTYPE CONSOLE}, указывающая но то, что это консольное приложение.
Наличие этой пракмы обязательно, т.к. только у консольного приложения можно пользоваться
операторами writeln.
Пример 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','Не введено'),'
');
Writeln('Пароль :'+ CGIParamsParser.GetParamVal('passwd','Не введен'),'
');
Writeln('В радио-переключателе Radio1 выбран пункт с значением :'+ CGIParamsParser.GetParamVal('Radio1'),'
');
Writeln('</pre><hr>');
Writeln('</BODY></HTML');
end.
Исходные тексты примеров можно скачать здесь.
Использование данных материалов и их размещение на других сайтах возможно только с согласия автора.
Я советую посетить и другие сайты, посвященные программированию. Это легко сделать по кольцу:
Algorithm project
: Кольцо сайтов, посвященных программированию (подробнее о проекте WebRing...) [ Предыдущие 5 сайтов | Предыдуший | Следующий | Следующие 5 сайтов | Выбрать сайт случайным образом | Список всех сайтов ]