53. Структура серверного скрипта. Язык Perl. Пример
Web-приложение - сетевое приложение, построенное по архитектуре "клиент-сервер" и использующее протокол HTTP в качестве механизма взаимодействия клиентских частей с сервером. Типичное web-приложение использует несколько стандартных элементов:
1) браузер, выполняющий на клиентской стороне функции отображения информации и организации непосредственного взаимодействия с пользователем;
2) web-сервер, обеспечивающий среду выполнения серверной части приложения и реализующий взаимодействие серверной и клиентской части по протоколу HTTP;
3) компоненты операционных систем клиента и сервера, реализующие стек протоколов TCP/IP;
4) реляционная база данных в качестве хранилища динамически изменяемых данных при необходимости работать с такими данными;
и, возможно
5) других вспомогательных элементов (кэширующих серверов, балансировщиков нагрузки между серверами, административных утилит и пр.).
Web-сервер в такой схеме является промежуточным звеном между клиентской частью приложения и серверными скриптами, реализующими основную логику работы приложения. Сервер разбирает HTTP запрос, полученный от клиента, проверяет его корректность, права доступа, определяет действия, необходимые для удовлетворения запроса и выполняет другие управляющие функции. В случае запроса статического ресурса сервер сам формирует HTTP ответ и отсылает его клиенту. В случае же запроса динамического ресурса сервер должен выполнить приложение, формирующее такой ресурс "на лету". Для такого приложения требуется обеспечить унифицированные условия работы, передав ему необходимую информацию о запросе, клиенте, сделавшем этот запрос и о среде выполнения приложения.
Набор правил взаимодействия web-сервера и серверных приложений стандартизован и называется протоколом CGI (Common Gateway Interface). Основным моментом этого протокола является соглашение о том, что все тело HTTP запроса передается на стандартный ввод приложения, а его стандартный вывод является полным ответом на запрос (т.е. должен включать HTTP заголовки) и отправляется клиенту "как есть". Вся дополнительная информация о самом запросе, заголовках запроса, характеристиках среды выполнения и прочая вспомогательная информация передается приложению через фиксированный набор переменных среды окружения (environment variables). Таких переменных в стандарте определено несколько десятков. Наиболее важными и часто используемыми переменными являются:
REQUEST_METHOD - метод, использованный в запросе;
QUERY_STRING - строка GET запроса;
SCRIPT_NAME - запускаемого имя приложения;
REQUEST_URI - полный затребованный URI;
REMOTE_ADDR - IP адрес клиента;
REMOTE_HOST - название хоста клиента;
HTTP_USER_AGENT - информация о браузере клиента;
HTTP_ACCEPT - MIME типы ответов, которые клиент готов принимать;
HTTP_ACCEPT_CHARSET - предпочитаемые кодировки;
SERVER_NAME - имя сервера.
Запускаемое сервером приложение может являться не выполняемым файлом, а исходным кодом программы, интерпретируемой сторонним приложением. Такие программы называются web-скриптами. В целях ускорения разработки динамических web-сайтов такой подход в настоящее время является доминирующим. В качестве скриптовых языков обычно используются такие языки как Perl, PHP и, реже, некоторые другие.
Таким образом, общая структура серверного скрипта определяется такими базовыми функциями как получение входной информации от пользователя по протоколу CGI, ее обработка и формирование HTTP ответа, включая заголовки HTTP. Кроме того, ввиду отсутствия понятия сессии в протоколе HTTP, при необходимости организации логической связи между несколькими запросами одного пользователя (например, с целью переноса однократно введенной авторизующей информации на все последующие действия пользователя) серверный скрипт в большинстве случаев занимается поддержкой сессий работы пользователей.
Основной задачей большинства web-приложений является предоставление или сохранение какой-либо информации по запросу пользователя. Для реализации этой функции обычно используется система управления базой данных (СУБД). Таким образом, еще одной фукцией серверного скрипта обычно является организация взаимодействия с СУБД.
Наконец, если требуемая функциональность приложения не ограничивается лишь организацией взаимодействия пользователей с СУБД, серверный скрипт реализует собственно логику работы приложения и производит требуемую обработку информации.
За исключением последнего пункта, все остальные функции скрипта достаточно стандартны и для их реализации в каждом языке, используемом в web-программировании, существуют готовые решения. Так как язык Perl является универсальным и, будучи созданным до распространения Internet, изначально не ориентировался на использование в качестве языка web-программирования, такие решения оформлены в виде модулей (аналог динамических библиотек в других языках программирования). Такая ситуация имеет как плюсы так и минусы по сравнению с языками, где эти средства встроены в базовое ядро языка (например, PHP). К плюсам можно отнести возможность выбора конкретного модуля под свою задачу, доступность исходного кода любого модуля (и, следовательно, возможность оперативно исправлять обнаруживаемые в модулях ошибки и защищаться от потенциальных угроз безопасности), уменьшение объема как интерпретатора языка, так и документации по нему и так далее. Минусами является несколько бОльшая сложность реализации приложения и больший простор для совершения ошибок и создания уязвимостей ввиду большой гибкости синтаксиса языка при его высокой универсальности.
Пример CGI скрипта на языке Perl.
Задача: Требуется создать страницу на сайте, позволяющую получить информацию о последних зарегистрированных пользователях.
Так как для решения этой задачи нам необходимо получить от пользователя информацию об интересующем его интервале времени регистрации, нам пригодится модуль CGI::Minimal. Интерфейс с СУБД, как это происходит в подавляющем большинстве случаев, реализуем на основе модуля DBI. В web-программировании всегда полезно разделять код скрипта и оформление результата. Для такого разделения используем модуль HTML::Template, реализующий шаблоны.
Для определенности зададим конфигурацию как:
Имя базы: state
Логин: stud
Пароль: 2009
Путь к Perl’у: d:/perl/bin/perl.exe
Корневая директория web-сервера: c:/Program Files/Apache/htdocs/
Директория для скриптов: c:/Program Files/Apache/cgi-bin/
Директория для шаблонов: c:/Program Files/Apache/templates/
------ тело скрипта -----
#!d:/perl/bin/perl
use DBI;
use CGI::Minimal;
use HTML::Template;
my $cgi = CGI::Minimal->new();
my $d = $cgi->'param'('d');
print "Content-type: text/html\n\n";
if ($d =~ /\D/) { print "Incorrect day number\n"; exit }
my $dbh = DBI->connect('dbi:mysql:db=state', 'stud', '2009', {RaiseError => 0});
if (not defined $dbh) { print "Can not connect to database: $!\n"; exit }
my $sth = $dbh->prepare("SELECT uid, login, name, reg_date FROM user ".
"WHERE datediff(NOW(), reg_date) < ?");
if (not defined $sth) { print "Error in query: $!\n"; exit }
$sth->execute($d);
while ( my $row = $sth->fetchrow_arrayref) {
push @data, {'uid' => $row->[0], 'login' => $row->[1],
'name' => $row->[2], 'date' => $row->[3]};
}
my $tmpl = HTML::Template->new('filename' => '../templates/query.tmpl');
if (not defined $tmpl) { print "Can not open template: $!\n"; exit }
$tmpl->param('LOOP' => \@data);
$tmpl->output;
----------------- вызывающая форма ------------------------
Input number of days to query<br>
<form method=GET action=/cgi-bin/practice.pl>
<input type=text name=d>
<input type=submit value=Go>
</form>
-------------------- шаблон query.tmpl ----------------------
New users: <br>
<table><tr><td> ID<td>Login<td>Name<td> Registration date</tr>
<TMPL_LOOP NAME='LOOP'><tr>
<td><TMPL_VAR NAME=uid>
<td><TMPL_VAR NAME=login>
<td><TMPL_VAR NAME=name>
<td><TMPL_VAR NAME=date>
</tr></TMPL_LOOP>
</table>