|
СодержаниеИтак ...приступим... Переменные среды CGI Прекрасный язык Perl Заголовки запросов и ответов Права Доступа Генерация ответа Обработка Форм Изображения ismap Анимация Несколько советов по отладке Краткое лирическое отступление насчет CGIИтак что такое CGI - скрипты и вообще подобные вещи. Начнем с того что ваш браузер (когда вы набрали URL) соединяется по протоколу HTTP с указаным сервером и просит у него нужный файл, примерно так:GET /~suas/cgi-bin/guestbbok.cgi HTTP/1.0-Вот это самое главное в запросе Ну тут дальше идет посылаемая браузером информация о себе и о том что более подробно ему надо.(Например Accept: */*) Ну и если запрошен простой файл например .html то если если такой файл есть, То сервер отошлет браузеру ответ: HTTP/1.0 200 Okay Content-Type: text/htmlДалее после пустой строки(она нужна чтоб отделить заголовок от тела) идет информация из самого URL'а <HTML><BODY>... Вот в принципе и весь WWW. А что если Нужно внести в этот унылый процесс что-нибудь по настоящему интерактивное , динамическое, прекрасное и великолепное? Чтож есть ответ и на этот вопрос. Просто что если в запрашиваемом URL указать специальную программу (CGI, программа Common Gateway Inteface - Общего Шлюзового Интерфейса) и то что эта прога выдаст то и отправить браузеру. Сервер запускает .cgi программу и она, например, обработав данные формы, заносит вас куда-нибудь в свою базу данных, а вам сообщит что вы большой молодец :). Ну надеюсь я вас заинтриговал? Итак, приступим.Краткие сведения о том что надо знать чтоб писать CGI скрипты: Ну вопервых надо знать что такое интернет и как он работает (а вы знаете? ;))) ) Ну и чуть-чуть умения прграмировать(это самое главное)Давайте вместе писанем какой нибудь простенький скриптик а потом я вам расскажу где сдесь собака порылась.... Ну сначала в своем домашнем каталоге создайте директорию cgi-bin: cd public_html mkdir cgi-bin chmod 0777 cgi-bin Последняя строчка будет очень важна. Возьмите редактор и наберите: #!/usr/bin/perl #first.cgi print "Content-Type: text/html\n\n"; print "<HTML><BODY>"; print "<H1>Hello you!!!</H1>"; print "</BODY></HTML>"; Сохраните его в директории cgi-bin под именем first.cgi .Ну как сохранили? А теперь сделайте его исполняемым(ведь это программа): chmod +x first.cgi Ну вот,подходим к торжественному моменту.... наберите в строке браузера http://www.uic.nnov.ru/~твой_логин/cgi-bin/first.cgi и посмотрите чо будет. Будет одно из двух ,либо скрипт заработает и вы увидите сгенерированую им страничку (поздравляю,в нашем полку прибыло!) либо Internal Server Error -тогда не расстраивайтесь,вы что-то сделали не так. Вам тогда пригодится пособие по ловле блох. Ну вопервых проверку синтаксиса можно осуществить следующим образом: perl -с first.cgi Perl вам сразу выдаст либо сообщения об ошибках(ну бывает,точку с запятой пропустили, скобочки или кавычки забыли закрыть...) это сразу по ходу дела поправимо. Более грубая с логической точки зрения это пропустить вывод пустой строки, которая отделяет заголовок от тела: print "Content-Type: text/html\n\n"; #Все Правильно print "Content-Type: text/html\n"; #ОШИБКА!!! Разберем скрипт: Первая строка #!/usr/bin/perl Просто указывает где в системе расположен Perl. Вторая это просто коментарий -вы можете тыкать чо угодно после знака # Затем идет print "Content-Type: text/html\n\n"; Это заголовок указывающий тип содержимого все что скрипт печатает в свой стандартный вывод STDOUT идет на обработку к серверу. Пустая строка отделяет заголовок от тела,которое в нашем случае представляет собой <HTML><BODY> <H1>Hello you!!!</H1> </BODY></HTML> Сервер обработает ответ скрипта и на базе него сформирует и пошлет браузеру ответ.(Сервер обычно не изменяет тела сообщения,он только дополняет заголовок нужными для протокола HTTP полями) Ну вот азы уже освоены,все не так трудно и удручающе как могло показаться на первый раз Вы теперь можете сами потренироваться в написании таких вот простеньких скриптов чтоб набить руку. Переменные среды CGIПредыдущий скрипт не содержал ничего особенно замечательного,так просто вываливал HTMLый текст который благополучно и отбражался на екране браузера.Но По настоящему мощь придает CGI возможность обработки параметров, которые переданы скрипту.например вы можете набрать http://www.somehost.ru/somedir/cgi-bin/my_cgi.cgi?param=value то есть вы хотите чтоб скрипт my_cgi.cgi обработал для вас параметер param со значением value (ну это например) или когда вы заполнили запрос в форме (в например yahoo или altavista).Ну это с точки зрения пользователя... А на сервере при запуске CGI-скрипта сервер формирует среду окружения в которой скрипт может найти всю доступную информацию о HTTP-соединении и о запросе. Вот эти переменные: REQUEST_METHOD Это одно из самых главных поле используемое для определения метода запроса HTTP Протокол HTTP использует методы GET и POST для запроса к серверу.Они отличаются тем что при методе GET запрос является как-бы частью URL т.е. http://www..../myscript.cgi?request а при методе POST данные передаются в теле HTTP-запроса (при GET тело запроса пусто) и следовательно для CGI тоже есть различие при GET запрос идет в переменную QUERY_STRING а при POST подается на STDIN скрипта.QUERY_STRING Это строка запроса при методе GET. Вам всем известно что запрос из формы кодируется браузером поскольку не все символы разрешены в URL некоторые имеют специальное назначение. Теперь о методе urlencode: неплохо бы чисто формально напомнить,что все пробелы заменяются в URL на знак '+', а все специальные и непечатныеCONTENT_LENGTH Длина в байтах тела запроса.При методе запроса POST необходимо считать со стандартного входа STDIN CONTENT_LENGTH байт,а потом производить их обработку.Обычно методом POST пользуются для передачи форм,содержащихCONTENT_TYPE Тип тела запроса(для форм кодированых выше указаным образом он application/x-www-form-urlencoded)GATEWAY_INTERFACE Версия протокола CGI.REMOTE_ADDR IP-Адрес удаленого хоста,делающего данный запрос.REMOTE_HOST Если запрашивающий хост имеет доменноеSCRIPT_NAME Имя скрипта,исполизованое в запросе.Для получения реального пути на сервере используйте SCRIPT_FILENAMESCRIPT_FILENAME Имя файла скрипта на сервере.SERVER_NAME Имя серера ,чаще всего доменное как www.microsoft.com ,но в редких случаях за неимением такового может быть IP-адресом как 157.151.74.254SERVER_PORT TCP-Порт сервераSERVER_PROTOCOL Версия протокола сервера.SERVER_SOFTWARE Програмное обеспечение сервера.AUTH_TYPE, REMOTE_USER Эти переменные определены в том случае,когда запрошеный ресурс требует аутентификации пользователя.Переменные заголовка HTTP-запроса. За исключением тех строк из заголовка HTTP-запроса которые были включены в другие переменные,сервер приделывает строкам префикс HTTP_ и заменяет знаки '-' на '_': HTTP_ACCEPT Давая запрос на сервер браузерHTTP_USER_AGENT Браузер обычно посылает на сервер и Ну,начнем применять на практике усвоеные уроки. #!/usr/bin/perl #vars.cgi sub urldecode{ #очень полезная функция декодирования local($val)=@_; #запроса,будет почти в каждой вашей CGI-программе $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; } print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>CGI-Variables</TITLE></HEAD>\n"; print "<BODY>\n"; print "Enter here something:<ISINDEX><BR>\n"; print "Your request is:$ENV{'REQUEST_STRING'}<BR>\n"; print "Decoded request is:urldecode($ENV{'REQUEST_STRING'})<BR>\n"; print "<HR>\n"; print "Variables:<BR>\n"; print "<I><B>REQUEST_METHOD</B></I>=$ENV{'REQUEST_METHOD'}<BR>\n"; print "<I><B>QUERY_STRING</B></I>=$ENV{'QUERY_STRING'}<BR>\n"; print "<I><B>CONTENT_LENGTH</B></I>=$ENV{'CONTENT_LENGTH'}<BR>\n"; print "<I><B>CONTENT_TYPE</B></I>=$ENV{'CONTENT_TYPE'}<BR>\n"; print "<I><B>GATEWAY_INTERFACE</B></I>=$ENV{'GATEWAY_INTERFACE'}<BR>\n"; print "<I><B>REMOTE_ADDR</B></I>=$ENV{'REMOTE_ADDR'}<BR>\n"; print "<I><B>REMOTE_HOST</B></I>=$ENV{'REMOTE_HOST'}<BR>\n"; print "<I><B>SCRIPT_NAME</B></I>=$ENV{'SCRIPT_NAME'}<BR>\n"; print "<I><B>SCRIPT_FILENAME</B></I>=$ENV{'SCRIPT_FILENAME'}<BR>\n"; print "<I><B>SERVER_NAME</B></I>=$ENV{'SERVER_NAME'}<BR>\n"; print "<I><B>SERVER_PORT</B></I>=$ENV{'SERVER_PORT'}<BR>\n"; print "<I><B>SERVER_PROTOCOL</B></I>=$ENV{'SERVER_PROTOCOL'}<BR>\n"; print "<I><B>SERVER_SOFTWARE</B></I>=$ENV{'SERVER_SOFTWARE'}<BR>\n"; print "<I><B>HTTP_ACCEPT</B></I>=$ENV{'HTTP_ACCEPT'}<BR>\n"; print "<I><B>HTTP_USER_AGENT</B></I>=$ENV{'HTTP_USER_AGENT'}<BR>\n"; print "<HR>\n"; print "All enviroment:<BR>\n"; foreach $env_var (keys %ENV){ print "<I>$env_var=$ENV{$env_var}</I><BR>\n"; } print "</BODY></HTML>\n";Так как все ваши .cgi -файлы должны быть исполняемыми то чтоб облегчить себе жизнь заведите себе в директории cgi-bin командный файл mkcgi ,содержащий chmod +x *.cgiи сделайте его в свою очередь исполняемым chmod +x mkcgi -он сильно упростит вам жизнь. Ну а теперь запускайте скрипт...... Изучив информацию,выдаваемую данным скриптом вы сможете лучше ориентироваться в переменных окружения CGI. Прекрасный язык PerlВы наверное обратили свое внимание что CGI скрипты пишутся обычно на языке Perl (Practical Extraction and Report Language)- очень удобном языке,впитавшем из других все лучшие черты.Может у вас возникнуть сомнение :Ну вот!Изучать новый язык программирования!? Спешу вас успокоить,изучение Perl не будет в тягость (я сужу по своему опыту!). Вы даже сами не заметите как выучите его.Если вы хоть когда-нибудь программировали скажем на C и использовали утилиту grep для поиска регулярных выражений в тексте,то вам будет еще легче.Мое изучение Perl началось с того что я скачал Perl под Windows (фирмы ActiveWare) и изучения той HTMLой документации которая к нему прилагалась хватило чтоб этот язык стал моим любимым.... Все в нем сделано для удобства программиста (в отличии например от Java;( ) Начнем с переменных,они в Perl бывают 3х типов скаларные,списковые(массивы) и хэши(ассоциативные массивы). Для указания компилятору(да и для немалого удобства программиста) перед именем скалярной переменной стоит знак '$' перед массивом '@',перед хешем '%'. т.е. например $scalar_var,@array_var,%hash_var Скалярные переменные могут быть как числовые так и строковые,но это не надо указывать Perl сам по контексту в зависимости от операций может привести одно к другому. Например: "123"+"4" будет 127 (или "127") так как операция '+' действует над числами а вот если применить операцию конкатенации строк '.' то строковое "test" . 1 будет "test1" Ну а вот операции над скалярными переменными:
$x='qwerty'; print 'my var is $x'; #выведет my var is $x print "my var is $x"; #выведет my var is qwertyСписки: Спискочные переменные начинаются с символа '@' конструируются следующим образом @List1=(1,2,5,70); @List2=(12,23,@List1); #12,23,1,2,5,70 @Rgb=($r,$g,$b);Также можно список использовать как lvalue: @List=(1,2,3..8,15); ($x,$y,$z)=@List; #$x=1,$y=2,$z=3 ($x,$y,$z,@list2)=@List; #$x=1,$y=2,$z=3,@list2=(4,5,6,7,8,15); ($r,$g,$b)=@Rgb;Можно обращаться к нескольким,выбраным элементам массива(срезу массива): @list=(1..10); @list[2,3,5,9]=(100,200,300,400); #@list=(1,100,200,4,300,6,7,8,400,10) @list[1,10]=@list[10,1];#меняет местами элементыОбратится к скаларному значению -элементу массива можно $имя_массива[индекс], сдесь обратите внимание на знак '$'- мы ведь обращаемся к скаляру-элементу. Теперь немного о хешах: хеш это такой массив который состоит из пар ключ-значение, весь хеш обозначается %хеш ,к отдельным элементам доступ $хеш{скалярное выражение} конструируется хеш так: $my_hash{1}="doom"; $my_hash{'quake'}="www.idsoftware.com"; $my_hash{1+2}=100;Хеш может быть также сконструирован из массива с четным числом элементов где пары превращаются в ключ-значение %hash=(1,20,2,100);#аналогично $hash{1}=20;$hash{2}=100;удаление из хеша -операция delete: delete $hash{1};есть функции выдающие ключи и значения соответственно. %hash=(1,20,2,100,3,'doom'); @k=keys %hash; #@k=(1,2,3); @v=values %hash;#@v=(20,100,'doom');Операторы: Набор операторов в Perl Очень широк,многие из них прямые аналоги имеющихся в других языках,например if,for,while;но есть и значительные улучшения имеюшихся и конечно новые... Тот же самый оператор if имеет две формы (как когда удобнее): if(условие)оператор; оператор if условие;В пару к оператору if имеется оператор unless : означающий if с отрицанием: unless(($method eq 'GET')||($method eq 'POST')){print "Unsupported method";} print "Ok" unless $x < $y;Также в пару while существует until синтаксис оператора for полностью аналогичен C: for($i=0;$i<10;$i++){ print $i; }новшеством(и приятным) является foreach позволяющий пройтись по всем элементам массива,присваивая по очереди его элементы какой-то переменной, его синтаксис такой: foreach $переменная (@массив){ блок операторов; } или foreach (@массив){ операторы; }Последний пример особенно важен для упрощения вашего тяжкого труда програмиста и демонтстрирует интересную особенность Perl-переменную по умолчанию $_: в оргомном количестве операторов и функций при опускании аргумента она подразумевается по умолчанию. Она также по умолчанию сопоставляется с регулярным выражением: следующий пример @Data=<STDIN>; foreach(@Data){ chomp; print if /^From:/; } аналогичен такому: @Data=<STDIN>; foreach $_ (@Data){ chomp($_); print $_ if $_ =~ /^From:/;как видите затраты труда значительно сокращаются,благодаря этому маленькому трюку. Регулярные выражения. регулярное выражение записывается между двух слэшей /рег_выр/ if(/abc/){ print '$_ содержит abc\n'; }это самый простой пример применения регулярного выражения а теперь посложнее вот тут в табличке (из того что я помню наизусть):
print "Are you sure?:"; $answer=<STDIN>; if($answer=~/Y/i){ #че-нибудь сделаем... } Полезные функции. В Perl очень много различных функций ,как говорится на все случаи жизни,все о них я конечно не опишу,но обо многих. Начну с тех,которые больше относятся к операторам. Операция замены s/рег.выражение/строка/ игнорировать регистр - опция i глобальная(по всей строке) замена -опция g; Пример: $x="This is test"; $x=~s/ /_/g; print $x; #This_is_testОчень полезная опция у s/// e -она означает что вторая строка не строка а выражение, результат которого и будет подставлен. Например,у вас есть файл в котором все записи о возрасте через год надо менять open OLD,"oldfile.txt" || die "Cannot open oldfile.txt $!\n"; open NEW,">newfile.txt" || die "Cannot open newfile.txt $!\n"; foreach(или более показательным примером послужит функция urldecode,которая будет встречатся в каждой вашей программе,обрабатывающей формы: sub urldecode{ local($val)=@_; $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; }Также важным удобством в Perl являются операции для работы с файлами для выполнения схожих функций в других языках приходиться проделывать огромную массу работы. Аргументами могут быть как Файловые переменные,так и строки,представляющие имя файла.
функция open открывает файл open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,"имя файла"; #открыть файл для чтения open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,">имя файла"; #для записи open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,">>имя файла";#для записи в конец open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,"+<имя файла";#для чтения и записи open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,"|комманда"; #направить информацию на вход программы open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,"комманда|"; #считать информацию с выхода программы open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,"|комманда|"; #и то и другое вместеЧто какается открытия файлов,то вам как програмистам все очевидно, но с коммандами тоже все здорово,что пояснит хороший пример(из практики): open MAIL,"|mail paaa@uic.nnov.ru";#Пошлем информацию по почте print MAIL "Hello\n"; print MAIL "...\n"; print MAIL "...\n"; close MAIL;когда вы открыли файл вы можете считать из него строку в скалярную переменную Вот так:$str=<FILE> избавиться от символа новой строки на конце поможет функция chomp, ведь этот символ может помешаться например в имени файла или при выводе на экран print "Введите имя файла:"; $fname=<STDIN>; chomp($fname); open F,$fname || die "Cannot open $fname $!\n"; .....Если также подставить списочную переменную,то получим список строк файла от текущей строки и до конца print "Что искать:"; $search=<STDIN>; chomp($search); @L=<F>; foreach(@L){ print if /$search/; } а можно и так: print "Что искать:"; $search=<STDIN>; chomp($search); foreach(<F>){ print if /$search/; }бинарный файл можно читать и писать функциями sysread и syswrite: sysread ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,$скалярная_перемменая,сколько_байт syswrite ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,$скалярная_перемменая,сколько_байт функции split и join: @Список=split /рег.выр/,$скаляр; $скаляр=join строка,@Список; #Разбить строку слов,разделенных пробелами в список вы можете @WordList=split / /,$String; #После обработки снова обьединить $String=join ' ',@WordList;Встроеные функции Perl можно вызывать со скобками или без (как вам удобно), скобки программисты указывают или для красоты,или чаще,что устранить возможную неоднозначность в выраженнии: printf "x=%d",$x; printf ("x=%d",$x);#аналогичноНадеюсь что я вас позабавил примерами функций ;). Примеры применения Perl для различных нужд... Следующая программа переводит текстовый файл в формат HTML (вспомните сколько хлопот вам доставит отлов во всем файле '<', '>' и '&' чтоб заменить их на &tl; , > и & а как неплохо чтоб автоматически все http://www.... превратились в <A href="http://www...." >http://www....</A>) #!/usr/bin/perl #txt2html die "Usage: txt2html Infile OutFile\n" unless(@ARGV); open IN,"$ARGV[0]" || die "Cannot open $ARGV[0] $! \n"; open OUT,">$ARGV[1]" || die "Cannot open $ARGV[1] $! \n"; while(<IN>){ s/&/&/g; s/</</g; s/>/>/g; s/\n/<BR>\n/g; s/(http:\/\/\S+)/<A href="$1">$1<\/A>/g; print OUT $_; } close IN; close OUT;Более подробную информацию о Perl вы можете получить по адресам: http://www.perl.com http://www.metronet.com/0/perlinfo/perl5/manual/perl.html http://www.ActiveWare.com/ Заголовки запросов и ответовДаже если вы и знаете кое-что о HTTP все равно не лишне будет вспомнить о том как это все работает тем более на эту информацию придется ориентироваться при написании CGI скриптов. Этапы соедирения. Первый этап это когда HTTP -клиент(браузер) соединяется с сервером.для этого он использует протокол TCP/IP соединение происходит к известному клиенту TCP-порту (80 -номер порта HTTP) (другие сервисы сидят на других портах ,например FTP и SMTP на 21 и 25) Вторым этапом идет запрос клиента:клиент передает заголовок запроса и возможно(в зависимости от метода) тело сообщения запроса.В заголовке обязательно указывается метод ,URI,и версия HTTP,и может быть еще несколько необязательных полей Третий этап -ответ сервера,который опять таки состоит из заголовка,в котором сервер указывает версию HTTP и код статуса, который может говорить о успешном или неуспешном результате и его причинах.Далее идет тело ответа. Четвертым этапом происходит разрыв TCP/IP соединения. HTTP -запрос. Запрос состоит из Строки запроса(она обязательна) и остальных полей. Синтаксис строки :МЕТОД <SP> URI <SP> HTTP/версия <CRLF> где <SP> -пробел ,<CRLF> -переход на новую строку Методы HTTP. GET Самый часто применяемый метод,в протоколе HTTP/0.9 был единственным методом,и применяется для извлечения информации по заданому URI Может быть условным если в заголовке указано поле If-Modified-Since: HEAD Почти идентичен GET но отличается тем что сервер не возвращает тело обьекта а только его заголовок (метаинформацию) программы могут применять его для проверки гиперссылок на правильность,доступность и изменения. POST передает данные для обработки их программой ,указаной в URIсдесь обязательно указывается поле Content-Length: Сушествуют и другие ,реже применяемые методы,например PUT -для сохранения передавемых данных в указаном URI и DELETE для удаления ресурса. Поля заголовка запроса. После строки запроса идут поля заголовка запроса. Поля общего(general-header) заголовка (он общий как для запросов так и для ответов): Date: Указывает дату запроса,например: Date: Sun, 20 Nov 1994 08:12:31 GMT MIME-version: Указывает версию MIME (по умолчанию 1.0) MIME-version: 1.0 Pragma: Содержит указания для таких промежуточных агентов как прокси и шлюзы, Pragma: no-cache Поля относящиеся к запросу(Request-Header): Authorization: Содержит информацию аутентификации Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== From: Браузер может посылать адрес пользователя серверу From: quake@doom.ru If-Modified-Since: используется при методе GET ресурс возвращается ,если он был изменен с указаного момента, может использоваться при кешировании. If-Modified-Since:Mon 15 Jul 1997 00:15:24 GMT Referer: Содержит URL предшествующего ресурса. Referer: http://www.uic.nnov.ru/~paaa/index.html User-Agent: Програмное обеспечение клиента. User-Agent: Mozilla/3.0 Заголовок информации сообщения (Entity-Header) применяется как в запросах так и в ответах (при этом некоторые поля только в ответах): Allow: (в ответе сервера) Список методов,поддерживаемых ресурсом. Allow: GET, HEAD Content-Encoding: идентифицирует метод кодировки,которым был закодирован ресурс Content-Encoding: x-gzip Content-Length: Длина тела сообщения Content-Length: 102 Content-Type: Содержит тип ресурса(MIME),для текстовых еще и кодировку символов(необязательно) Content-Type: text/html; charset=windows-1251 Expires: (в ответе сервера) Дата окончания действия ресурса,применяется в кешировании для запрета кеширования устаревших ресурсов (в ответе) Expires: Tue, 24 Sep 1998 23:00:15 GMT Last-Modified: (в ответе сервера) Время последнего обновления ресурса Last-Modified: Tue, 23 sep 1998 13:48:40 GMT Другие поля: Поля Accept: указывают серверу выдавать только указаные форматы данных,которые клиент может распознать. Accept: text/html Accept: text/plain Accept: image/gif Примеры запросов: Простейший запрос: GET /index.html HTTP/1.0 Посложнее: GET /somedir/somedoc.html HTTP/1.0 User-Agent: Mozilla/2.0 Accept: text/html Accept: text/plain Accept: image/gif Передача данных CGI- скрипту через метод GET GET /~paaa/cgi-bin/test.cgi?name=Dmitry&organization=%D3%ED%E8%E2%E5%F0%F1%E8%F2%E5%F2+%CD%E8%E6%ED%E5%E3%EE+%CD%EE%E2%E3%EE%F0%EE%E4%E0&Name=&email=&comment= HTTP/1.0 User-Agent: Mozila/2.0 Accept: text/html Accept: image/gif Используя метод POST данные передаются в теле сообщения запроса: GET /~paaa/cgi-bin/test.cgi HTTP/1.0 User-Agent: Mozila/2.0 Accept: text/html Accept: image/gif Content-Type: application/x-www-form-urlencoded Content-Length: 131 name=Lesha &organization=%D3%ED%E8%E2%E5%F0%F1%E8%F2%E5%F2+%CD%E8%E6%ED%E5%E3%EE+%CD%EE%E2%E3%EE%F0%EE%E4%E0&Name= &email= &comment=Ответ HTTP-сервера. Ответ идет от сервера.Состоит он из строки состояния и затем поля ответа Общий заголовок(General-Header) и заголовок тела сообщения (Entity-Header),которые уже описаны при обсуждении запроса. и еще идет заголовок ответа(Response-Header). Строка состояния имеет следующий формат: HTTP/version <SP> Status-Code <SP> Status-Phrase где HTTP/version версия,Status-Code -3х значный код,и Status-Phrase текстовая фраза, поясняющая код ,пример: HTTP/1.0 200 Ok ,200 -код означающий успешную обработку запроса,что и поясняет "Ok" Заголовок ответа состоит из полей: Location: Содержит URI ресурса,может быть использован для переключения клиента в другое место, если например ресурс был перемещен в другое место или на другой сервер. Location: http://www.uic.nnov.ru/newlocation/index.html Server: Информация о програмном обеспечении сервера Server: Apache/1.1 WWW-Autenticate: Параметры аутентификации. WWW-Autenticate: Basic realm="doomsday" Коды ответов HTTP.
Несколько примеров: HTTP/1.0 200 Ok Date: Wed, 25 Sep 1998 23:00:00 GMT Server: Apache/1.1 MIME-version: 1.0 Last-Modified: Mon 15 Nov 1996 15:20:12 GMT Content-Type: text/html Content-Length: 2000 <HTML><HEAD><TITLE>Hello</TITLE></HEAD> <BODY bgcolor="green" text="yellow"> ...... </HTML> А вот такое сервер выдаст в неудачном случае: HTTP/1.0 404 Not FoundCGI-заголовок. В том случае когда запрашиваемый URI есть CGI-скрипт сервер базируясь на данных запроса создает среду переменных CGI и передает управление скрипту скрипт должен выдать CGI-заголовок,после которого и идет тело ответа,сгенерированое скриптом. Заголовок (CGI-Header) состоит из полей: Content-Type: Должно обязательно присутствовать,если есть тело. Content-Type: text/html Location: Содержит URL ресурса на который скрипт перенаправляет запрос.Как правило,если присутствует это поле больше ничего не указывается. Location: http://www.idsoftware.com/index.html Status: Позволяет CGI скрипту вернуть статус обработки,если это поле не задано,то сервер подразумевает Status: 404 Not found На базе этой информации сервер и формирует окончательный заголовок,который и передается клиенту. Примеры: Обычно такое выдает скрипт: Content-Type: text/html <HTML><HEAD>....... Но иногда такое(когда он служит для перенаправления): Location: http://www.mustdie.ru/ А вот пример возврата статуса: Content-Type: image/gif Status: 190 Its seems great like a playing doom! WOW! GIF89a........nph-скрипты. Иногда возникает необходимость чтобы CGI -скрипт сам отвечал напрямую клиенту, минуя разбор заголовка.Это во-первых уменьшает нагрузку на сервер,и во вторых, что самое главное такой прямой ответ клиенту позволяет скрипту полностью контролировать транзакцию.Для этого существуют nph-скрипты(Not Parse Header) ,имя скрипта должно начинаться с префикса "nph-" ,Например "nph-animate.cgi" .Такие скрипты сами формируют HTTP-ответ клиенту,что полезно при анимации: #!/usr/bin/perl #nph-animate.cgi $times = 20; #Заготовте несколько небольних gif-файлов для этой программы @files = qw(img0.gif img1.gif img2.gif img3.gif); select (STDOUT); $|=1; #autoflush mode on #Generate header print "HTTP/1.0 200 Okay\n"; print "Content-Type: multipart/x-mixed-replace;boundary=myboundary\n\n"; print "--myboundary\n"; for ($num=1;$num<=$times;$num++) { foreach $file (@files) { print "Content-Type: image/gif\n\n"; open(PIC,"$file"); print <PIC>; close(PIC); print "\n--myboundary\n"; sleep(3); } } print "\n--myboundary--\n";Этот пример вам выдаст анимацию ,составленую из нескольких .gif -файлов.Если же вы получили вместо анимации сообщение об ошибках,то вам следует,может быть перейти к следующей главе, которая поведает вам о правах доступа- того,без чего Unix не был бы Unixом. Права ДоступаЯ бы ни за что не написал этот раздел,если бы он не был так важен.Сидя в пределах своей домашней директории и занимаясь только тем,что качаете с Инета всякую херню,вы возможно и не задавались некоторыми вопросами....а зря.......... Ведь немного надо,чтоб попортить нервы начинающему CGI -програмисту. Одна из таких вещей это права доступа...... Начнем с того ,что в системе Unix каждый пользователь имеет свой идентификатор- число,уникально идентифицирующее его в этой системе.(Мой логин paaa а ему соответсвует число 1818).Это число внутреннее для операционной системы,для пользования оно представлено как логин,который и соответствует пользователю. Только не надо думать о пользователе,как о конкретном человеке сидящим за клавиатурой, пользователем может быть и какой-нибудь процесс.Важно отметить что пользователь-это определенная область прав доступа,которая ему соответствует.(Вы например не можете удалить файлы из каталога другого пользователя). Это и дает возможность стабильной работы всей системы. Итак есть идентификатор пользователя.Также имеется идентификатор группы. Группа служит для выделения пользователей по группам. Например у пользователей группы users (Обычные пользователи) не такие права как у группы wheels (административная группа). Каждый процесс который вами запущен(Будь то Netscape,терминал,или текстовый редактор)получают ваши идентификаторы пользователя и группы. таким образом исполняются от вашего имени. Теперь рассмотрим повнимательней файловую систему.В Unix с файлом связано много характеристик. Во-первых в системе нет "ничьих" файлов ,все файлы имеют владельца-пользователя и владельца-группу. Любой файл который вы создаете автоматически получает ваш идентификатор.По этому система очень легко отслеживает, чьи это файлы и каталоги. Следующее новшество по сравнению с DOS это права доступа к файлу.Их может сменить только тот пользователь которому принадлежит файл,или супервизор.(Это в отличии от DOS где каждая дрянь типа вируса может снять атрибут readonly) Права доступа задаются обычно числом в восьмеричном коде и разбиты на 3 части по 3 бита: Каждая часть задает права доступа для конкретной группы:
chmod [u|g|o]{+|-}{r|w|x} file chmod number file ,где u-user,g-group,o-other,r-read,w-write,x-execute;--удалить,+-установить Примеры: chmod +r file.txt #разрешает всем право на чтения файла chmod u+w file.txt #устанавливает для владельца файла право на запись в него chmod +x gbook.cgi #право на исполнение для всех,как для ползователя,группы,и для других chmod 0777 cgi-bin #Разрешает самые широкие права доступа для cgi-bin Приоткрытии файла программой,операционная система сравнивает идентификатор пользователя с идентификатором пользователя владельца файла, если они равны,то действуют права пользователя,если не равны то сравниваются идентификаторы группы,если и они не равны,то действуют права доступа для остальных остальных.В том случае если у процесса нет достаточных прав,система возвращает ошибку. Следует заметить ,что для супервизора root права доступа не проверяются. Можно выполнить скрипт,только если есть права на его исполнение. Вот почему следует давать chmod +x *.cgi иначе ваши скрипты станут просто недоступными. Но и это еще не все..... Ваш скрипт может обращатся к вашим файлам (например ведет базу данных гостевой книги). Все выглядит нормально,но только ничего не работает,файл в который вы намеревались писать, не открывается,знакомая проблема ;(( ?.Так вот чтобы вы не мучались в догадках Ваш скрипт не может получить доступ к вашим файлам,потому что он выполняется не вами (не с вашим идентификатором), а от имени nobody (непривелигированый пользователь).Это мера предосторожности направлена на то, чтоб скрипты ,взбесившись из-за неправильно переданых параметров(или вообще от глюков) не могли ничего повредить ценного и важного на сервере. Поэтому к тем файлам,к которым скрипт по смыслу должен обращатся нужно присвоить самые широкие права доступа 0777 Например в случае гостевой книги chmod 0777 guestbook.dat Если также важно чтоб скрипты могли заводить новые файлы в cgi-bin то надо дать также права на это chmod 0777 cgi-bin Если вы видите что ваш скрипт не может обратится к какому-то файлу,то это в 99% случаев из-за вашей забывчивости.!!! На самый крайний случай воспользуйтесь setuid-скриптами (к этому делу ,если вы на это решились,отнеситесь ОЧЕНЬ серьезно,так как целые тома по безопасности в Unix посвящены именно setuid-скриптам). Хочу сразу предупредить ,сам я таких не писал,да и вам не особенно советую.Но для общего как говорится развития,имейте в виду следующую информацыю. Кроме указания прав доступа,существуют специальные биты у файла.Это биты установки пользователя и группы. Когда процесс выполняется(простой процесс) то его реальный и эффективный идентификаторы пользователей совпадают,идентификаторы групп тоже. На самом деле значение имеют как раз эффективные значения пользователя и группы,они учавствуют в сравнении прав доступа. Нельзя ли их как-то изменить,когда уж совсем нужда заставит? Можно! .На этот вопрос дают ответ программы с установленым битом пользователя.Когда система запускает такую программу,она присваивает новому процессу не идентификатор того пользователя,что запустил ее, а идентификатор пользователя-владельца исполняемого файла. Самый классический пример setuid-программ это программа passwd ,предназначеная для смены пароля пользователя. Такие данные как пароль и прочие характеристики пользователей хранятся в специальном файле,который имеет огромное значение при входе в систему. Так как это системный файл,то открыть к нему доступ на запись всем-значит подвергнуть ВСЮ систему риску,ведь любое неправильное изменение его повлечет катастрофические последствия(в конце концов бывает просто хулиганство). Поэтому доступ к этому файлу закрыт для всех пользователей.А что если надо сменить пароль? Запускаем программу passwd ,если глянуть на ее аттрибуты ,то видно что она принадлежит root -супервизору, и еще имеет установленый бит setuid. Так корректно обходится эта проблема. Если вы все-же решили попытаться ,то знайте ,что сделать программу setuid можно коммандой : chmod +s myprogramm И как всгда Примерчик напоследок: Эта программа выдает содержимое вашей директории public_html в том случае,если она доступна для чтения,и для каждого файла указывает ,можно ли его читать,писать и исполнять. Попробуйте ее сделать setuid и посмотрите как изменится результат. #!/usr/bin/perl #listmydir.cgi print "Content-Type: text/html\n\n"; if(!(-r '..')){ print ".. is not allowed for reading ;)))))\n"; } else{ @list=glob('../*'); foreach(@list){ print "<A href=\"$_\">$_</A>"; print " readable" if -r; print " writable" if -w; print " executable" if -x; print "<BR>\n"; } } Генерация ответаБольшую часть того что нужно знать о генерации ответа,я сказал в разделе Заголовки запросов и ответов.Нет,не угадали! Я не буду сдесь говорить о всяком дизайне того что вы выдаете.Этому вы успели напрактиковатся на HTML -страничках. Я поговорю о MIME (Multipurpose Internet Mail Extension).И о разных браузерах. Стандарт MIME появился в электронной почте (e-mail) потому что остро стала проблемма пересылки по e-mail различных данных в различных форматах.Так как HTTP тоже работает с различными типами данных то поэтому тоже использует MIME для своих нужд. Типы MIME состоят из Типа и подтипа (например text/plain,где text-указывает на наличие текстового содержимого,а plain-уточняет его как простой текст) приведеный ниже список (он далеко не полн,типов MIME огромное количество) описывает некоторые часто встречающиеся типы.: text/html text/plain text/richtext image/gif image/jpeg image/tiff audio/basic audio/32kadpcm audio/ video/mpeg video/quicktime multipart/mixed multipart/alternate multipart/ application/octet-stream application/msword application/postscript message/digest Информация о MIME больше возможно пригодится вам в том случае если вы собираетесь работать из ваших скриптов с электронной почтой,но и для WWW она не повредит. Онобенно знание Content-Type: Content-Type: Состоит из типа и подтипа типы могут быть как стандартные так и экспериментальные начинающиеся с префикса 'x-': text Текстовые данные.Первым подтипом который включен сюда это plain,что значит простой текст. сюда же включен самый ходовой формат html .У типа text как и у многих типов могут быть параметры,главным из них является charset он как раз и указывает на раскладку символов, которая применена в тексте, так что если вы хотите указать браузеру какую раскладку применять, то просто укажите charset: Content-Type: text/plain; charset=us-ascii Content-Type: text/html; charset=iso-8859-1 Content-Type: text/html; charset=koi8-r multipart Данные которые могут состоять из нескольких частей,различных типов данных.Поэтому параметром multipart служит boundary, позволяюший указать разделитель.Каждый фрагмент в многочастевом сообщении имеет свой Content-Type: (Он может быть также multipart,т.е. допускаются вложеные multipart,главное чтоб boundary были разными).В электронной почте применяется больше multipart/mixed (основной подтип) и multipart/alternative (Он отличается тем что показывается одна из альтернатив,например сообщение шлется в простом и HTMLом форматах,и почтовая программа показывает либо часть,которую она способна отобразить). В WWW -програмировании распостранен x-mixed-replace ,который означает что следующая часть должна заменить предыдущую после подгрузки, что применяется для анимации(см.Пример с анимацией). Теперь о разделителе,его надо выбирать так,чтоб он не встретился где-то в данных (т.е. что-то вроде "diUr344rnmvforgefvrg923rghyj2").Когда вы задали разделитель,например boundary="boundary" то когда закончилась одна часть,вы должны выдать строку --boundary,последняя часть --boundary--,причем эти разделители должны быть на отдельной строке,а не сливаться с текстом: Пример: MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="w23renff491nc4rth56u34-9449" --w23renff491nc4rth56u34-9449 Content-Type: text/plain; charset="koi8-r" Hello,World!! --w23renff491nc4rth56u34-9449 Content-Type: text/html; charset="us-ascii" <H1>Hello,Word!!</H1> <HR> <FONT size=+1 color=red>Hello people!</FONT> --w23renff491nc4rth56u34-9449--message Представляет инкапсулированое почтовое сообщение.Используется в e-mail ,а не в WWW. image Некоторое Графическое изображение.(чаще всего image/gif и image/jpeg) audio Аудиоданные. video Видеоданные. application бинарные данные какого-нибудь приложения.В том случае если данное приложение может быть запущено,Браузер запускает его.Например при поступлении данных application/msword Браузер спросит,нужно ли запустить Word для просмотра досумента.При отсутствии нужного приложения браузер спросит в каком файле сохранить данные.Подтип octet-stream как раз и означает поток байт информации,который и используется по умолчанию.(К сожалению не все так гладко,известен глюк в Netscape Navigator'е который вместо того чтоб сохранить application/octet-stream пытается его показать как text/plain что если это сгенерировано из CGI,ни к чему хорошему не приводит ;(() Что касается application ,то Вы можете тут смело извращатся,используя x- типы данных, Например application/x-fuck-to-netscape-navigator. ;))))) Часто используемый параметр name позволяет указать имя файла.Например: Content-Type: application/msword; name="readme.doc" Что полезно при полученнии файлов через HTTP,причем этот параметр может применятся и для других типов таких image или audio ,Например: Content-Transfer-Encoding: Применяется больше в системе электронной почты и обозначает метод кодирования, которым были закодированы данные при передаче сообщения.Например: 7bit 8bit quoted-printable base64 binary x-типы MIME-Version: Указывает версию MIME . Теперь поговорим о разных браузерах вы знаете что браузеры бывают разные,разных версий на разных платформах, поддерживают и не разные тэги и глюки у них тоже разные.....;((( . Это могло попортить много нервов WEB-дизайнерам и конечно же нам ,CGI-програмистам. Профессионально написаный сайт от просто хорошего отличается тем что хорошо выглядит Не только на экране того браузера,которым пользуется сам его автор,а на других тоже. Если вы используете JavaScript для своих страничек,то вы уже наверно использовали (или хотя бы вам в голову приходила мысль использовать)свойства navigator.AppName navigator.AppCodeName navigator.appVersion navigator.userAgent: <SCRIPT language="JavaScript"> if(navigator.AppName=="Netscape"){ /*Сделать чо-нибудь специфичное для Netscape*/ } else if(navigator.AppName=="Microsoft Internet Explorer"){ /*Сделать чо-нибудь специфичное для Explorer*/ } else{ /*Не делаем специфичных вещей-хрен его знает с каким браузером мы имеем дело*/ } </SCRIPT> или <SCRIPT language="JavaScript"> if((navigator.AppName=="Netscape")&&(parseFloat(navigator.appVersion)<3.0)){ document.writeln("Пользуетесь слишком старым браузером"); } </SCRIPT>Ну не волнуйтесь вы так ,мы CGI-программисты не в самых худших условиях на этот счет. Вспомните о том что браузер сам при запросе посылает вам данные о себе и о своей версии. И делает он это для того,чтобы эту информацию можно было учесть. В запросе он указывает User-Agent: которое и попадает на сервере в переменную среды HTTP_USER_AGENT ,которую и можно использовать. Например если в ней содержится Mozilla/3.01Gold (Win95;I) то значит вы имеете дело с Netscape (Mozilla-кодовое название Netscape Navigator'а),версии 3.01Gold и далее после имени и версии может следовать необязательная информация ,например как в приведеном примере о платформе Win95 и о том является ли версия U -для США (USA) или I -международной(International). Напомню,что такая информация необязательна.(То есть если браузер послал информацию User-Agent: то гарантировано расчитывать вы можете только на Название/Версия). Ну вот я слишком много развел демагогии,пора переходить к практическим примерам. Допустим ваш скрипт генерирует какие-то тэги,которые слишком старые браузеры не поддерживают,причем без них не обойдешся,они составляют всю 'изюминку' сайта. #!/usr/bin/perl #oldbrowser.cgi print "Content-Type: text/html\n\n"; if(defined ($ENV{'HTTP_USER_AGENT'})){ $browser=$ENV{'HTTP_USER_AGENT'}; ($vers)=($browser=~/\/(\d+\.\d+)/); if(($browser=~/mozilla/i)&&($vers<=2.0)){ print "<HTML><HEAD><TITLE>Too old!</TITLE></HEAD>"; print "<BODY bgcolor=\"red\" text=\"black\">"; print "<CENTER><H1>Ваш Netscape Слишком старый для этого сайта"; print "(старость не радость)</H1></CENTER>"; print "</BODY></HTML>"; exit; } if(($browser=~/msie/i)&&($vers<=3.0)){ print "<HTML><HEAD><TITLE>Too old!</TITLE></HEAD>"; print "<BODY bgcolor=\"red\" text=\"black\">"; print "<CENTER><H1>Ваш Explorer устарел"; print "(не пора ли сделать апгрейт хотя бы до 4.0 версии)</H1></CENTER>"; print "</BODY></HTML>"; exit; } } print "<HTML><HEAD>.........";Ну уже почувствовали,насколько это здорово.А вот еще примерчик.Это из разряда того, что тэги бывают разные.Например в Explorer есть тэг BGSOUND предназначеный для проигрывания музыки на страничке.(В Netscape этого тега нет,и поэтому для втыкания музыки приходится использовать подключаемые модули plugin).Мутится с этими Плугинами Вам в облом,а хочется побаловать человека хорошей музыкой,если браузер позволяет. ... ... if($ENV{'HTTP_USER_AGENT'}=~/msie/i){ print "<BGSOUND src=\"jmj00.mid\">"; } elsif($ENV{'HTTP_USER_AGENT'}=~/mozilla/i){ #Оставлю сдесь коментарий,что воткну что-нибудь типа музыки,для Netscap'а , #Когда мне не будет так в облом это делать....... }Ну вот вы уже можете управлять этим процессом.Только не забывайте,что если вы не получили информацию о клиенте(так может быть,если например ваш скрипт вызвал какая-нибудь поисковая машина) то не в этом случае не надо делать никаких предположений,а просто пусть ваш скрипт продолжает делать то что должен был делать. Как всегда Примерчик на последок.Этот примерчик позволит выбирать из списка файлов. и загружать что пользователь хочет. #!/usr/bin/perl #download.cgi sub urldecode{ local($val)=@_; $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; } @Filelist=qw(index.html readme.txt jmj00.mid gunshot.wav foto.gif); @Sel_list=(); if($ENV{'REQUEST_METHOD'} eq 'GET'){$query=$ENV{'QUERY_STRING'};} elsif($ENV{'REQUEST_METHOD'} eq 'POST'){sysread(STDIN,$query,$ENV{'CONTENT_LENGTH'});} if($query eq ''){ #Если никаких данных не подано на обработку,то сгенерируем форму, #которую и предложим заполнить пользователю. print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>File Downloading</TITLE></HEAD>"; print "<BODY bgcolor=\"white\">"; print "Выберите файлы которые вы хотите загрузить:<BR>"; print "<FORM METHOD=\"POST\">"; print "<SELECT NAME=\"file\" size=4 multiple>"; foreach(@Filelist){ print "<OPTION value=\"$_\">$_"; } print "</SELECT><BR>"; print "<INPUT TYPE=\"Submit\" value=\"Download!\">"; print "</FORM>"; print "</BODY></HTML>" } else{ @formfields=split /&/,$query; foreach(@formfields){ if(/^file=(.*)/){push(@Sel_list,urldecode($1));} } unless(@Sel_list){ print "Content-Type: text/html\n\n"; print "<HTML><BODY><CENTER><H1>Вы должны выбрать что-то из списка"; print "</H1></CENTER></BODY></HTML>"; } else{ print "Content-Type: multipart/mixed;boundary=\"bhy3e23r4t34tnehtpo7678nneu4232y213vdg\"\n\n"; print "--bhy3e23r4t34tnehtpo7678nneu4232y213vdg\n"; foreach(@Sel_list){ print "Content-Type: application/x-qwerty; name=\"$_\"\n\n"; open F,"$_"; print <F>; close F; print "\n--bhy3e23r4t34tnehtpo7678nneu4232y213vdg\n"; } print "Content-Type: text/html\n\n"; print "<HTML><H1>Thats all folks!</H1></HTML>"; print "\n--bhy3e23r4t34tnehtpo7678nneu4232y213vdg--\n"; } } Обработка ФормНу вот ,вы уже знаете достаточно,кое в чем уже успели приобрести опыт, пришло время перейти к очень важной теме - обработке форм. При всей простоте (кажушейся) это едва ли не самое главное предназначение всего стандарта CGI . Куда бы вы не зашли на любой уважающий себя сайт,везде вы встретите формы, которые вам предложат заполнить.В этом деле можно положится только на CGI, так как Java и JavaScript ,выполняющиеся на страничке у клиента не имеют доступа к серверу,на котором находится сайт. Коротко вспомним о том что происходит при рассматриваемом процессе поближе,так сказать на трезвую голову ;). Итак браузер требует у сервера определенный URL (это может быть как простой документ,так и сгенерированый CGI) в этом документе может содержаться форма.Отображая такой документ браузер также выводит элементы формы (кнопки, поля ввода, поля ввода пароля, переключатели, радио-кнопки, списки, текстовые области,скрытые поля). И со всем этим добром пользователь может взаимодействовать.К форме естественно имеет доступ и встроеный язык программирования JavaScript -он может как использовать форму для своих нужд,не передавая CGI,так и помогать пользователю в заполнении формы. После того,как пользователь заполнил форму он нажимат кнопку Submit которая говорит, что форму надо отправить на сервер. Браузер собирает все имена и значения элементов формы ,кодирует их методом urlencode и в зависимости от указаного в тэге FORM метода вызывает GET или POST с указаным URL,передавая ему данные. На сервере CGI-скрипту это попадает (в зависимости от метода) либо в переменную QUERY_STRING либо на STDIN.Скрипт может проверить данные ,занести их в какую нибудь базу данных,может как yahoo выполнить какой-нибудь поиск, может что-нибудь вычислить......да мало ли что он может,все зависит только от нашей фантазии..... В конце концов скрипт выдает браузеру ответ,который он и отображает.В этом ответе может содержаться все что вашей душе угодно от сообщения об удачном или неправильном запросе до таких ответов,по сравнению с которыми yahoo и altavista подвиснут от зависти, главное чтоб вам и тем кто посещает ваш сайт это нравилось.;) Ну а теперь немного о синтаксисе элементов форм ,их описании и самое главное особенностях при обработке CGI-скриптом. Итак немного экскурс в HTML: FORM <FORM action="http://......cgi" method="GET"|"POST" enctype="encodingType" name="formName" target="windowName" onSubmit="Handler"> </FORM>Атрибуты: action как раз и задает тот URL,который будет и обрабатывать форму, если он опущен,то текущий URL документа(а он-то может быть сгенерирован нашим скриптом). method задает метод GET или POST enctype обычно не задается,для форм он application/x-www-form-urlencoded -по умолчанию, и поддерживается всеми CGI скриптами.Но если вы уж очень хотите чтобы браузер послал вам данные в другом формате (например text/plain) то можете указать этот тип кодировки,только потом не жалуйтесь,что ваш скрипт не может разделить поля,или вообще начинает глючить когда пользователь ввел какой-то спецсимвол. name Задается для JavaScript,чтоб обращатся к форме по имени,а не по номеру. Для CGI не играет ни какой роли,так как внутреннее для браузера. target Может Определять в какой фрейм отправить полученую информацию.Имеет значение во фреймосодержащих документах.Прозрачен для CGI обработки данных. onSubmit Определяет JavaScript -обработчик активизации формы.Применяется для проверки JavaScript'ом правильности заполнения.Опять таки прозрачен для CGI. Пример типичной формы: <FORM action="http://www.uic.nnov.ru/~paaa/cgi-bin/test.cgi" method="POST"> .........Поля формы......... </FORM>Форма может содержать элементы.Элементы имеют имена,которые используются для кодирования пар имя=значение.Некоторые Элементы не передаются CGI,а используются JavaScript для управления,например кнопки.Некоторые поля передаются только в тех случаях, когда в них что-то выбрано,например списки и переключатели.Остальные поля передаются всегда, даже когда они пустые. Например: <FORM action="http://www.doom/cgi-bin/test.cgi"> Your Name:<INPUT name="Name"><BR> E-Mail:<INPUT name="Email"><BR> Are you doomer:<INPUT type="checkbox" name="doomer" value="Yes"> <INPUT type="submit" value="Send Form!"> </FORM>Допустим вы ввели имя lesha и адрес paaa@uic.nnov.ru,при этом выбрали переключатель После нажатия кнопки будет отправлен вот такой запрос: http://www.doom/cgi-bin/test.cgi?Name=lesha&Email=paaa@uic.nnov.ru&doomer=Yes Если же вы не выбрали переключатель,то запрос будет таким: http://www.doom/cgi-bin/test.cgi?Name=lesha&Email=paaa@uic.nnov.ru ,как видите элемент doomer не вошел в строку запроса Теперь попробуйте оставить поля редактирования пустыми: http://www.doom/cgi-bin/test.cgi?Name=&Email= Эти элементы (Name и Email) присутствуют и сообщают что они пустые. Кнопка(button) <INPUT type="button" name="buttname" value="Текст На Кнопке" onClick="Handler"> В форме изображается кнопка,при нажатии которой вызывается JavaScript-обработчик заданый атрибутом onClick ,атрибут name служит для JavaScript-именования кнопки а не для передачи CGI.Так как значение кнопки не передается CGI, value задает Текст,изображаемый на кнопке.
<INPUT type="submit" name="submitName" value="Отправить Форму" onClick="Handler"> Кнопка,предназначеная для передачи формы.Опять же,сама не передается,а служит только для управления. текст на ней задается атрибутом value.
<INPUT type="reset" name="resetName" value="Очистить" onClick="Handler"> Кнопка очистки формы.При ее нажатиивсем измененым элементам возвращается значение по умолчанию.
<INPUT [type="text"] name="textName" value="textValue" size=число [обработчики]> Применяется очень часто,поэтому тип "text" служит для INPUT по умолчанию,его не надо каждый раз указывать.Имя поля,задаваемое name является обязательным для CGI (в отличии от JavaScript,где элементы формы можно индексировать по номерам,а имена для удобства и читабельности кода служат).Можно задать значение по умолчанию атрибутом value,которое будет после загрузки докумета.атрибут size позволяет задать размер поля.Также может содержать обработчики onBlur,onChange,onFocus,onSelect.
<TEXTAREA name="textareaName" rows="число" cols="число" wrap="hard"|"soft"> TextToEdit </TEXTAREA>Область многострочного редактирования.Размеры в строках и столбцах задаются атрибутами rows и cols.Значения атрибута wrap "hard" и "soft" -означают соответственно мягкую или жесткую разбивку на строки (в большинстве случаев ето не существенно). На что следует действительно обратить внимание так это на символ,используемый для указания перехода на новую строку. В Windows это '\r\n' а в Unix '\n',так что если это для вас существенно,то приводите преобразование,например так: $my_text =~ s/\r\n/\n/g;
<INPUT type="password" name="passName" size=число value="passValue"> Очень похоже на поле ввода,отличается тем что вместо символов в нем отображаются символы '*'.Служит для ввода пользователем пароля.
<INPUT type="hidden" name="hiddName" value="hidValue"> Поле не отображаемое на экране.Но оно имеет имя и значение и следовательно передается в форму. Служит для того (и очень часто програмисты его применяют) чтоб передавать скрипту какую нибудь информацию.Например,если ваш скрипт обрабатывает несколько форм разных типов,то в скрытом поле каждой формы можно указать с какой формой конкретно вы имеете дело. Так как это ваша внутренняя кухня то нечего пользователю мозолить глаза этой информацией.
<INPUT type="checkbox" name="checkboxname" value="checkboxValue" [checked] onClick="Handler">Text В отличии от кнопки,атрибут value сдесь не задает на надпись на переключателе,а его значение(внутреннее).Поэтому если надо что-то подписать,пишите рядом в ним. Может быть сразу выбраным если указан атрибут checked .Если value не указано то значение по умолчанию "on" .Передается только в том случае,когда выбран.
<INPUT type="radio" name="radioName" value="radioVal1" [checked] onClick="Handler">Text В отличие от checkbox может быть несколько радиокнопок с одинаковым параметром name ,но с разными value,из них передается только та,что выбрана.Одна из них может быть изначально выбрана по умолчанию checked.Например:
<SELECT name="SelectName" size=число [multiple] [обработчики] > <OPTION value="optionValue1" [selected]>Опция 1 <OPTION value="optionValue2" [selected]>Опция 2 <OPTION value="optionValue3" [selected]>Опция 3 ..... <OPTION value="optionValueN" [selected]>Опция N </SELECT>Задает список,позволяющий выбрать одну (или несколько) опций из списка. Если атрибут multiple не указан,то создается простой выпадающий список,в котором можно выбрать только одну из опций.Его значение всегда передается,т.к. всегда хоть одно выбрано. Если указан атрибут multiple,то во первых можно указать размер видимой части списка атрибутом size (Если опций больше появится скролинг).Во вторых передаются только выбраные опции ,т.е.Он может передатся несколько раз ?SelectName=opt1&SelectName=opt2&SelectName=opt9 если выбраны скажем несколько опций.А может и не разу,если ничего не выбрано из списка. Можно задавать обработчики onBlur,onChange,onFocus.
Для CGI-програмиста конечно JavaScript -это иной мир,вы можете спокойно пропустить этот абзац,если вы не знаете JavaScript,так как написаное в нем к CGI не относится, а скорей к самим формам и дизайну сайта.Я скажу пару слов о том как JavaScript может оказать посильную помощь в проверке правильности заполнения форм.Все проверки конечно можно и нужно делать на сервере,но когда имеешь дело с рассеяным пользователем, то для него заполнение простой формы превратится в мучение.Поясню на примере,в форме есть какие-то обязательные поля,например имя.Если пользователь забыл его указать то скрипт скажет ему об этом в сообщении он исправит это, допустим что-нибудь еще не так ввел .... Только на передачу данных по сети может уходить масса времени.А на обработку на локальной машине-доли секуды. Вот Например как это можно применить JavaScript для предварительного контроля правильности. Допустим простейшая форма содержит имя и возраст.Имя не должно быть пустым, а возраст должен состоять из цифр. <HTML><HEAD> <SCRIPT language="JavaScript"> <!-- function IsNumber(data){ var NumStr="0123456789"; var ch;var count; for(var i=0;i<data.length;i++){ ch=data.substring(i,i+1); if(NumStr.indexOf(ch)!=-1)count++; } if(counter==data.length)return true; else return false; } function IsEmpty(data){ if(data.length==0)return true; else return false; } function IsFormOk(f){ if(IsEmpty(f.Name.value)){ alert('Имя не должно быть пустой строкой'); return false; } if(!IsNumber(f.Age.value)){ alert('Возраст должен состоять из цифр'); return false; } return true; } //--></SCRIPT></HEAD> <BODY> <FORM action="http://www.test.ru/cgi-bin/test.cgi" onSubmit="IsFormOk(this.form)"> Your Name:<INPUT name="Name"><BR> Your age:<INPUT name="Age"><BR> <INPUT type="submit" value="Послать Данные"> </FORM> </BODY></HTML>Ну вот ,на этом можно закончить это краткое введение в HTMLые формы. Итак,У нас на входе скрипта данные формы,закодированые методом urlencode Положеные в Переменную QUERY_STRING или подаваемые на STDIN.Мы должны вопервых их получить. if($ENV{'REQUEST_METHOD'} eq 'GET'){#Анализируем метод,GET или POST $query=$ENV{'QUERY_STRING'}; } elsif($ENV{'REQUEST_METHOD'} eq 'POST'){ sysread STDIN,$query,$ENV{'CONTENT_LENGTH'}; }Вот,мы уже считали наш запрос в переменную $query.Теперь пришло самое время ее обработать. Мы знаем что поля разделены символом '&' значит используем его в качестве разделителя функции split: @formfields=split /&/,$query;Вот разделили,а теперь организуем цикл foreach по полученым полям @formfields foreach(@formfields){ if(/^Name=(.*)/){$name=urldecode($1);} if(/^Age=(.*)/){$age=urldecode($1);} }Сдесь выражение в регулярном выражении в круглых скобках (.*) после знака '=',запоминается в скалярную переменную $1 ,которая затем и декодируется нашей старой и знакомой функцией urldecode (я предупреждал,что она будет почти в каждой вашей CGI-программе) sub urldecode{ #очень полезная функция декодирования local($val)=@_; #запроса,будет почти в каждой вашей CGI-программе $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; }Так мы проходим по всем полям,которые нам переданы.Это стандартный подход,он годится в качестве шаблона.У вас может возникнуть вопрос,а что делать если вам переданы данные от списка у которого задана возможность выбора нескольких элементов и данные поступают в таком виде: Sel=opt1&Sel=opt2&Sel=opt9. Тут тоже нет никаких проблем,просто запихиваем эти поступающие значения в массив. foreach(@formfields){ ..... if(/^Sel=(.*)/){push @Sel,urldecode($1);} ..... }И потом спокойно оперируем с Полученым Массивом @Sel. На этом можно так сказать заканчивается шаблонная часть скрипта и начинается содержательная, которая зависит только от вашей фантазии..... Вы можете сколько угодно анализировать полученые значения,обращатся при этом к различным файлам .Если вы к этому приложите фантазию,то кто знает что получится.... А Пока Ради примера я вам напишу скрипт,который ведет социологическое исследование насчет курения и отношения к нему.Может он слишком массивен для данного пособия, но зато он наглядно показывает как достаточно простыми средствами можно проводить социологические исследования. <HTML><!-- HTML файл с формой,можете повесить его себе на сайт! -> <HEAD><TITLE>Социологический опрос насчет курения</TITLE></HEAD> <BODY> <CENTER><H1>Социологический опрос насчет курения</H1></CENTER> <FORM action="cgi-bin/smoketest.cgi"> <TABLE> <TR><TD>Ваш возраст:</TD><TD><INPUT name="age"></TD></TR> <TR><TD>Вы курите(Y/N):</TD> <TD><INPUT type="radio" name="smoke" value="Yes" checked>Да <INPUT type="radio" name="smoke" value="No">Нет</TD></TR> <TR><TD>Как вы относитесь если рядом кто-то курит?</TD> <TD><SELECT name="sm_near"> <OPTION value="0">Резко негативно <OPTION value="1">Негативно <OPTION value="2" selected>Мне все равно <OPTION value="3">Позитивно <OPTION value="4">Резко позитивно </SELECT> </TD></TR> <TR><TD>Сколько вы выкуриваете в день?</TD> <TD><SELECT name="sm_day"> <OPTION value="0">Ни сколько <OPTION value="1">1 сигарету <OPTION value="2">2 сигареты <OPTION value="5">около 5 <OPTION value="0.5pac">полпачки <OPTION value="pac">пачку <OPTION value="2pac">2 пачки <OPTION value="more">больше </SELECT> </TD></TR> <TR><TD>Как давно вы начали курить?</TD> <TD><SELECT name="sm_stage"> <OPTION value="noatall">Не начинал <OPTION value="onetime">Бросил <OPTION value="0.5year">Полгода <OPTION value="1year">Год <OPTION value="2year">2 Года <OPTION value="5year">5 Лет <OPTION value="more">Больше </SELECT> </TD></TR> <TR><TD>Считаете ли вы это опасным для своего здоровья?</TD> <TD><SELECT name="sm_danger"> <OPTION value="0">Очень Опасно <OPTION value="1">Думаю,что да <OPTION value="2" selected>Не знаю <OPTION value="3">Может самую малость <OPTION value="4">Нет,Безопасно. </SELECT> </TD></TR> <TR><TD>Хотите ли вы бросить?</TD> <TD><SELECT name="sm_nosmoke"> <OPTION value="0">Уже бросаю <OPTION value="1">Думаю бросить <OPTION value="2" selected>Иногда <OPTION value="3">Очень Редко <OPTION value="4">Никогда. </SELECT> </TD></TR> <TR><TD><INPUT type="submit" value="Послать Данные"></TD> <TD><INPUT type="reset" value="Очистить Форму"></TD></TR> </TABLE> </FORM> </BODY></HTML>А вот скрипт для его обработки: #!/usr/bin/perl #smoketest.cgi $datafile="smoke.dat"; sub urldecode{ local($val)=@_; $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; } sub print_err{ print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>Error!!</TITLE></HEAD>"; print "<BODY><CENTER><H1>@_</H1>"; print "</BODY></HTML>"; exit; } if($ENV{'REQUEST_METHOD'} eq 'GET'){$query=$ENV{'QUERY_STRING'};} elsif($ENV{'REQUEST_METHOD'} eq 'POST') {sysread STDIN,$query,$ENV{'CONTENT_LENGTH'};} if($query ne ''){ @formfields=split /&/,$query; foreach(@formfields){ if(/^age=(.*)/){$age=urldecode($1);} if(/^smoke=(.*)/){$smoke=urldecode($1);} if(/^sm_near=(.*)/){$sm_near=urldecode($1);} if(/^sm_day=(.*)/){$sm_day=urldecode($1);} if(/^sm_stage=(.*)/){$sm_stage=urldecode($1);} if(/^sm_danger=(.*)/){$sm_danger=urldecode($1);} if(/^sm_nosmoke=(.*)/){$sm_nosmoke=urldecode($1);} } if((!$age)||($age=~/\D/)){ print "Content-Type: text/html\n\n"; print "<HTML><BODY><H2>Возраст введен неправильно,должен состоять из цифр.</H2>"; print "<FORM><INPUT type=\"button\" value=\"Вернуться назад к Анкете\""; print "onClick=\"history.back();\"></FORM>"; print "</BODY></HTML>"; } $anket_str=join '\t',($age,$smoke,$sm_near,$sm_day,$sm_stage,$sm_danger,$sm_nosmoke); open DATA,">>$datafile" || print_err("Cannot open $datafile $!"); print DATA "$anket_str\n"; close DATA; } open DATA,"$datafile" || print_err("Cannot open $datafile $!"); @AllData=<DATA>; close DATA; $total=$#AllData; foreach(@AllData){ ($age,$smoke,$sm_near,$sm_day,$sm_stage,$sm_danger,$sm_nosmoke)=split /\t/,$_; $smok_total++ if ($smoke eq 'Yes'); $nosmok_total++ if ($smoke eq 'No'); if($age<16){$age16_total++; if($smoke eq 'Yes'){$age16_sm++;}else{$age16_nosm++;} } if(($age>16)&&($age<=18)){$age16_18_total++; if($smoke eq 'Yes'){$age16_18_sm++;}else{$age16_18_nosm++;} } if(($age>18)&&($age<=20)){$age18_20_total++; if($smoke eq 'Yes'){$age18_20_sm++;}else{$age18_20_nosm++;} } if($age>20){$age20_total++; if($smoke eq 'Yes'){$age20_sm++;}else{$age20_nosm++;} } if($sm_near eq '0'){$near0++;} if($sm_near eq '1'){$near1++;} if($sm_near eq '2'){$near2++;} if($sm_near eq '3'){$near3++;} if($sm_near eq '4'){$near4++;} if($sm_day eq '0'){$day0++;} if($sm_day eq '1'){$day1++;} if($sm_day eq '2'){$day2++;} if($sm_day eq '5'){$day5++;} if($sm_day eq '0.5pac'){$dayhalfpac++;} if($sm_day eq 'pac'){$daypac++;} if($sm_day eq '2pac'){$day2pac++;} if($sm_day eq 'more'){$daymore++;} if($sm_stage eq 'noatall'){$stagenoatall++;} if($sm_stage eq 'onetime'){$statgeonetime++;} if($sm_stage eq '0.5year'){$stagehalfyear++;} if($sm_stage eq '1year'){$stage1year++;} if($sm_stage eq '2year'){$stage2year++;} if($sm_stage eq '5year'){$stage5year++;} if($sm_stage eq 'more'){$stagemore++;} if($sm_danger eq '0'){$danger0++;} if($sm_danger eq '1'){$danger1++;} if($sm_danger eq '2'){$danger2++;} if($sm_danger eq '3'){$danger3++;} if($sm_danger eq '4'){$danger4++;} if($sm_nosmoke eq '0'){$stopsmoke0++;} if($sm_nosmoke eq '1'){$stopsmoke1++;} if($sm_nosmoke eq '2'){$stopsmoke2++;} if($sm_nosmoke eq '3'){$stopsmoke3++;} if($sm_nosmoke eq '4'){$stopsmoke4++;} } ######### print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>Результаты обработки данных</TITLE></HEAD>"; print "<BODY bgcolor=\"yellow\">"; unless($total){print "<H1>Еще нет данных</H1></BODY></HTML>";exit;} print "<CENTER><H1>Результаты обработки данных</H1></CENTER>"; print "<BR>\n"; print "Обработано анкет: $total<BR>\n"; print "Общие данные Всего:<BR>\n"; print "Курящие:$smok_total (".(($smok_total/$total)*100) ."%)<BR>\n"; print "Некурящие:$nosmok_total (".(($nosmok_total/$total)*100)."%)<BR>\n"; print "<TABLE>\n"; print "<TR><TD colspan=4>Возрастные группы:(<16,16..18,18..20,>20)</TD></TR>\n"; print "<TR><TD>Возраст</TD><TD>Курящие</TD><TD>Некурящие</TD><TD>Всего</TD></TR>\n"; print "<TR><TD><16:</TD><TD>$age16_sm</TD><TD>$age16_nosm</TD><TD>$age16_total</TD></TR>\n"; print "<TR><TD>16..18:</TD><TD>$age16_18_sm</TD><TD>$age16_18_nosm</TD><TD>$age16_18_total</TD></TR>\n"; print "<TR><TD>18..20:</TD><TD>$age18_20_sm</TD><TD>$age18_20_nosm</TD><TD>$age18_20_total</TD></TR>\n"; print "<TR><TD>>20:</TD><TD>$age20_sm</TD><TD>$age20_nosm</TD><TD>$age20_total</TD></TR>"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>Отношение когда кто-то курит рядом:(%)</TD></TR>\n"; print "<TR><TD>Резко негативно</TD><TD>".(($near0/$total)*100)."</TD></TR>\n"; print "<TR><TD>Негативно </TD><TD>".(($near1/$total)*100)."</TD></TR>\n"; print "<TR><TD>Мне все равно </TD><TD>".(($near2/$total)*100)."</TD></TR>\n"; print "<TR><TD>Позитивно </TD><TD>".(($near3/$total)*100)."</TD></TR>\n"; print "<TR><TD>Резко позитивно</TD><TD>".(($near4/$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>В среднем выкуривают:(%)</TD></TR>\n"; print "<TR><TD>не курят: </TD><TD>".(($day0/$total)*100)."</TD></TR>\n"; print "<TR><TD>1 сигарету:</TD><TD>".(($day1/$total)*100)."</TD></TR>\n"; print "<TR><TD>2 сигареты:</TD><TD>".(($day2/$total)*100)."</TD></TR>\n"; print "<TR><TD>5 сигарет: </TD><TD>".(($day5/$total)*100)."</TD></TR>\n"; print "<TR><TD>полпачки: </TD><TD>".(($dayhalfpac/$total)*100)."</TD></TR>\n"; print "<TR><TD>пачку: </TD><TD>".(($daypac/$total)*100)."</TD></TR>\n"; print "<TR><TD>2 пачки: </TD><TD>".(($day2pac/$total)*100)."</TD></TR>\n"; print "<TR><TD>больше: </TD><TD>".(($daymore/$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>Стаж курения:(%)</TD></TR>\n"; print "<TR><TD>Не начинал</TD><TD>".(($stagenoatall /$total)*100)."</TD></TR>\n"; print "<TR><TD>Бросил </TD><TD>".(($statgeonetime/$total)*100)."</TD></TR>\n"; print "<TR><TD>Полгода </TD><TD>".(($stagehalfyear/$total)*100)."</TD></TR>\n"; print "<TR><TD>Год </TD><TD>".(($stage1year /$total)*100)."</TD></TR>\n"; print "<TR><TD>2 Года </TD><TD>".(($stage2year /$total)*100)."</TD></TR>\n"; print "<TR><TD>5 Лет </TD><TD>".(($stage5year /$total)*100)."</TD></TR>\n"; print "<TR><TD>Больше </TD><TD>".(($stagemore /$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>Курение опасно:(%)</TD></TR>\n"; print "<TR><TD>Очень Опасно </TD><TD>".(($danger0/$total)*100)."</TD></TR>\n"; print "<TR><TD>Думаю,что да </TD><TD>".(($danger1/$total)*100)."</TD></TR>\n"; print "<TR><TD>Не знаю </TD><TD>".(($danger2/$total)*100)."</TD></TR>\n"; print "<TR><TD>Может самую малость</TD><TD>".(($danger3/$total)*100)."</TD></TR>\n"; print "<TR><TD>Нет,Безопасно. </TD><TD>".(($danger4/$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>Хотели ли вы бросить:(%)</TD></TR>\n"; print "<TR><TD>Уже бросаю </TD><TD>".(($stopsmoke0/$total)*100)."</TD></TR>\n"; print "<TR><TD>Думаю бросить</TD><TD>".(($stopsmoke1/$total)*100)."</TD></TR>\n"; print "<TR><TD>Иногда </TD><TD>".(($stopsmoke2/$total)*100)."</TD></TR>\n"; print "<TR><TD>Очень Редко </TD><TD>".(($stopsmoke3/$total)*100)."</TD></TR>\n"; print "<TR><TD>Никогда. </TD><TD>".(($stopsmoke4/$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "</BODY></HTML>"; Изображения ismapПосле такой серьезной темы как обработка форм,я перейду к чему-нибудь веселенькому. Я познакомлю вас с изображениями ismap потому что это просто есть такой способ. Он поддерживается браузерами и естественно имеет право на жизнь.Хотя с приходом новых веяний в HTML (особенно Java-аплетов) он стал настоящей редкостью.И хотя можно в 80% случаев найти ему более быструю замену,все-же вы можете в некоторых случаях найти именно ismap предпочтительней всего. Синтаксис очень простой,почти не отличается от того,если бы вы решили оформить рисунок для якорь гиперссылки: <A href="cgi-bin/somescript.cgi"><IMG src="somepic.gif" border=0 ismap></A>Заметьте что все отличие заключается в том,что в тэге IMG добавлен атрибут ismap. Он говорит браузеру,что когда пользователь щелкнет на картинке то нужно перейти не просто к URL указаному в <A href="URL"> а что нужно к этому URL добавить координаты той точки по которой пользователь щелкнул мышью . В нашем примере если пользователь щелкнул по точке x=10 ,y=15 то браузер перейдет на URL: http://www.somehost.ru/cgi-bin/somescript.cgi?10,15Т.е. координаты идут на скрипте в переменную QUERY_STRING ,их оттуда извлечь? Нет ничего проще: ($x,$y)=split /,/,$ENV{'QUERY_STRING'};Вот скрипт,который просто показывает координаты точки щелчка: #!/usr/bin/perl #ismap_xy.cgi ($x,$y)=split /,/,$ENV{'QUERY_STRING'}; print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>Ismap X Y</TITLE></HEAD>"; print "<BODY><H1>Вы щелкнули в точке: x=$x ,y=$y</H1></BODY></HTML>";А что с ними делать дальше это уже чисто зависит только от вашей фантазии.Дайте ей ход и все у вас получится!.Очень часто ismap применяют для графического оглавления сайта. Когда щелкают на разные части рисунка,то переходят к разным страничкам сайта. Это легко реализуется,если скрипт выдаст нужный URL в Location: (Вспомните заголовок ответа CGI). Вот пример и покажет это.Заготовьте файл urlmap.txt в котором будет информация из строк в таком формате: minx miny maxx maxy URLгде minx miny maxx maxy задают участок рисунка,а следующее за ними поле задает URL, которому этот участок соответствует.Пример: 1 1 20 50 http://www.uic.nnov.ru/~paaa/index_p.html 1 50 20 100 http://www.uic.nnov.ru/~paaa/projects.html 20 1 100 100 http://www.uic.nnov.ru/~paaa/cgi-bin/guestbook.cgiГде нибудь на своей страничке воткните что-то вроде: <A href="cgi-bin/testismap.cgi"><IMG src="gifs/doom2.jpg" border=0 ismap></A>А сам скрипт testismap.cgi будет иметь вот такой простенький вид: #!/usr/bin/perl #testismap.cgi $default_url="http://www.uic.nnov.ru/~paaa/";#URL по умолчанию,переходим к нему когда щелкнули #в участок,которому не сопоставлен URL $url_map_file="urlmap.txt"; #файл с информацией об URL ($x,$y)=split /,/,$ENV{'QUERY_STRING'}; open F,"$url_map_file" || print "Location: $default_url\n\n"; $url=$default_url; foreach(<F>){ chomp; ($minx,$miny,$maxx,$maxy,$URL)=split /\s+/; if(($x>=$minx)&&($x<$maxx)&& ($y>=$miny)&&($x<$maxy)){$url=$URL;} } close F; print "Location: $url\n\n"; АнимацияКогда говорят о каком-то популярном сайте,то частенько к преимуществам относят и анимацию. Действительно,когда изображение изменяется (и особенно к месту ;)),то это смотрится и пользователю нравится. Говоря об анимации нужно сразу отметить что нет лучшего способа. Анимацию можно сделать ДЕСЯТКАМИ Способов,каждый хорош в своей области применения. Я перечислю только некоторые из них,которые чаще всего применяются: Самый простой,но наименее функциональный способ это GIF с анимацией. Потом можно воткнуть анимационный файл MPEG или AVI они более отражают суть анимации, но имеют недостаток,что для проигрывания их на некоторых браузерах нужны специальные подключаемые модули.К тому же они не интерактивны. Можно реализовать анимацию в рамках Java-апплета,когда апплет находясь на страничке сам перерисовывается со временем. Таким же интерактивным средством служит обращение к массиву document.images[] из JavaScript.Достоинство-помимо интерактивности,полная интегрированость с HTML -станичкой.Но может как и предыдущий использоваться только с относительно новыми браузерами,которые поддерживают Java и JavaScript. В общем в каждом случае выбор остается за вами.Вам решать насколько тот или иной способ хорош в вашей ситуации.Я же познакомлю вас с еще одним. Вы даже уже были знакомы с этим способом,когда я вам рассказывал о nph- скриптах Теперь когда вы уже так много знаете,можно модифицировать тот пример, добавив в него вызов картинки по случайному принципу: #!/usr/bin/perl #nph-animate2.cgi $delay=3; @files = qw(img0.gif img1.gif img2.gif img3.gif); select (STDOUT); $|=1; #autoflush mode on #Generate header print "HTTP/1.0 200 Okay\n"; print "Content-Type: multipart/x-mixed-replace;boundary=myboundary\n\n"; srand; print "--myboundary\n"; while(1){ $file=$files[int(rand($#files))]; #random file print "Content-Type: image/gif\n\n"; open(PIC,"$file"); print <PIC>; close(PIC); print "\n--myboundary\n"; sleep($delay); }Конечно одно из самых примитивных применений такой системы.Более мощным примером могло бы послужить отслеживание на сервере какого-нибудь периодически изменяющегося файла и пересылка пользователю обновленной версии. Такая Система применяется например в Чате,при появлении новых сообщений. Чатовая система достаточно сложна для этого пособия и я не стал сюда ее включать.Однако,если вам очень интересно,то я с удовольствием пришлю ее вам. Несколько советов по отладкеCGI-программы -не самые простые в отладке,по сложности отладки они способны сравнится лишь с отладкой драйверов. Вся сложность заключается в том,что скрипт выполняется не как обычная программа. Он выполняется в специальной среде сервера,которая создается при клиентском запросе, к тому же он исполняется не из под вашего аккаунта,а на непривилегированом уровне. Если скрипт не исполняется потому,что вы допустили синтаксические ошибки,то самих этих ошибок вы не увидите,на экране будет только Ну вот ,хватит вас пугать,тем более что нас не запугаешь ;) ! Приступим к отладке.Я вам опишу достаточно примитивные меры,которыми я сам пользуюсь. Начнем с того что у нас есть скрипт test.cgi мы уже сделали его исполняемым chmod +x test.cgi Простейший способ проверить его на ошибки это команда Закоментируйте всю вашу программу ,т.е. перед каждой строчкой поставьте символ '#'. После чего,добавьте вот такие строчки: #!/usr/bin/perl #test.cgi print "Content-Type: text/html\n\n"; print "<HTML>Test</HTML>"; exit; #Программа как вы понимаете выполняется только до етого места # #if($ENV{'REQUEST_METHOD'} eq 'GET'){$query=$ENV{'QUERY_STRING'}} #else{sysread STDIN,$query,$ENV{'CONTENT_LENGTH'};} #if($query eq ''){ # @formfields=split /&/,$query; # ....... # ........А теперь запускайте скрипт.Естественно он выдаст Одно только слово 'Test'. Разкоментируйте несколько строчек.Еще раз запустите скрипт.Он опять выдаст 'Test'. Значит синтаксически эти только что разкоментированые строчки были правильные. И так далее.... Если очередной раз после раскоментирования вы запустили скрипт и получили 'Internal Server Error' - значит в этих строках содержалась какая-та синтаксическая ошибка. Это способ отловки синтаксических ошибок трудоемок,но к нему придется прибегнуть если ваш скрипт писан под ту версию Perl,что на сервере,а не под ту что у вас. Узнать версию Perl можно Ну вот мы отловили в нашем скрипте все синтаксические ошибки,он заработал, но это не значит,что он работает правильно. Что еще можно посоветовать при отладке CGI-скриптов от ошибок возникающих во время выполнения программы. Допустим какой-то файл не открылся.Конечно показывать перепуганому пользователю эти технические подробности никчему,поэтому заведите себе специальный файл debug.txt и пусть ваши скрипты пишут в этот файл причины своих ошибок и сбоев, да и вообще о всех непредвиденых событиях. Это можно реализовать так: sub debug_err{ open DEBUGFILE,">>debug.txt"; print DEBUGFILE $ENV{'SCRIPT_NAME'}.' '.scalar localtime.' '.@_."\n"; close DEBUGFILE; }Примеры использования (Напомню,что встроеная переменная Perl $! содержит сообщение о причине последней ошибки,поэтому включайте ее всегда в свои сообщения): open F,"+<$myfile" || debug_err("Cannot open $myfile $!"); seek F,0,0 || debug_err("Cannot seek $myfile $!"); connect SOCKET,$paddr || debug_err("Cannot connect to $remote $!"); ......Потом можно периодически заглядывать в этот файл debug.txt и смотреть,какие ошибки встречались при работе ваших скриптов.Таким образом ваши скрипты сами помогать будут в своей отладке ;). Кстати я хочу сказать о причине еще одной (совсем не очевидной) ошибки.Если вы набрали скрипт у себя дома на компутере,то полученый скрипт состоит из текста в DOS'ом формате, а не в Unix'ом так что имейте это ввиду. Запускать вам его придется в системе Unix , так что следует перевести програмный текст в нужный формат. |
helloworld.ru © 2001-2021 Все права защищены |
|
|