Статьи

CGI :: Application: простая расширяемая веб-платформа

В прошлый раз мы представили концепцию размещения нашего HTML вместо кода Perl в отдельных шаблонах . Наряду с предоставлением простого способа сохранить внешний вид сайта, этот подход также удаляет много нежелательных шумов из наших программ. Как разработчики, наша задача — учитывать потребности бизнеса; необходимость беспокоиться о пользовательском интерфейсе может отвлекать.

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

Многие веб-приложения имеют концепцию «режима запуска», хотя они могут не использовать эту терминологию. Примеры режимов запуска могут быть:

  • Создать пользователя
  • редактировать пользователя
  • список usrs

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

if (run_mode  eq 'create_user') { 
   perform create user code
} elsif (run mode eq 'edit_user') {
   perform edit user code  
} elsif (run mode eq 'list_users') {
   perform list code
} else {
   error - invalid run mode
}

Цель CGI :: Application — предоставить основу для повторно используемого кода, основанную на концепции режима выполнения, но таким образом, чтобы избежать большого количества спагетти-кода. Поскольку он является объектно-ориентированным (ОО), его можно легко расширять и улучшать, а позже мы рассмотрим некоторые плагины, которые значительно облегчат вашу работу. Но если вы не специалист по ОО, не бойтесь: вы не обязаны интенсивно использовать ОО-методы, если не хотите.

CGI :: Application рекомендует вам принять платформу Model-View-Controller (MVC). В мире веб-разработки MVC стал популярной концепцией для инженерных приложений, хотя его корни появились еще до Интернета. Модель — это фактическая бизнес-область, в которой вы работаете. Она включает логику и правила, которые приложение понимает и применяет. Представление — это просто пользовательский интерфейс, и по умолчанию CGI :: Application поддерживает HTML :: Template, хотя может использоваться любое шаблонное решение. Наконец, контроллер является посредником между моделью и представлением. Он принимает входные данные, передает их компонентам модели и при необходимости вызывает компоненты представления.

Образец заявки

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

  • usernamefirst_name
  • last_name
  • email
  • password
  • receive_newsletter
  • display_user_form

Мы заранее знаем, что нам понадобятся следующие режимы работы:

  • maintain_user
  • list_users
  •  1  package MyApp::User;
     2  use strict;
     3  use base 'CGI::Application';
     4  use CGI::Application::Plugin::DBH        (qw/dbh_config dbh/);
     5  use CGI::Application::Plugin::ConfigAuto (qw/cfg cfg_file/);
     6  use Data::Dumper;
     7
     8  # Define our run modes
     9  sub setup {
    10      my $self = shift;
    11      $self->start_mode('display_user_form');
    12      $self->error_mode('error');
    13      $self->mode_param('rm');
    14      $self->run_modes(
    15          'display_user_form' => 'display_user_form',
    16          'maintain_user'     => 'maintain_user',
    17          'list_users'        => 'list_users',
    18      );
    19  }
    20
    21  # Execute the following before we execute the requested run mode
    22  sub cgiapp_prerun {
    23  }
    24
    25  # Process any fatal errors
    26  sub error {
    27      my $self  = shift;
    28      my $error = shift;
    29      return "There has been an error: $error";
    30  }
    31
    32  # Display the form for creating/maintaining users
    33  sub display_user_form {
    34  }
    35
    36  # Process the submitted form
    37  sub maintain_user {
    38  }
    39
    40  # List users for editing
    41  sub list_users {
    42  }
    43
    44  # Rules for validating the submitted form parameters
    45  sub _user_profile {
    46  }
    47
    48  # We don't want to store plain text passwords....
    49  sub _encrypt_password {
    50  }
    51
    52  # Create a new user record in the database
    53  sub _create_user {
    54  }
    55
    56  # Update an existing user record in the database
    57  sub _update_user {
    58  }
    59
    60  # Retrieve the list of users from the database
    61  sub _get_users {
    62  }
    63
    64  # Retrieve the record for a given user_id
    65  sub _retrieve_user_details {
    66  }
    67  1;

Скелет нашего приложения будет выглядеть так:

 MyApp::User

Строка 1: наше приложение реализовано в пакете, который мы назвали $banana Perl реализует свои классы в пакетах.

Строка 2: эта прагма включает строгую проверку. Он улавливает ошибки, которые часто допускаются в скриптовых языках, например, при опечатке в имени переменной, вынуждая разработчика объявлять переменную перед ее использованием ( $banananadisplay_user_form Есть и другие преимущества, которые у нас нет времени обсуждать здесь. Вы всегда должны использовать эту прагму.

Строка 3: Здесь мы наследуем все функции CGI :: Application. Для многих приложений это почти столько же ОО, сколько вам нужно знать.

Строка 4: этот плагин обрабатывает файлы конфигурации.

Строка 5: этот плагин предоставляет API для стандартного интерфейса базы данных Perl.

Строки 8-18: Здесь мы настраиваем режимы бега.

Строка 10: если режим запуска не указан, используйте rm

Строка 11: мы можем указать метод, который вызывается при возникновении фатальной ошибки.

Строка 12: параметр запроса cgiapp_prerun

Строки 13-17: мы сопоставляем каждое имя режима выполнения с методом, который будет вызываться при запросе режима выполнения. Обратите внимание, что имя режима выполнения не обязательно должно совпадать с именем метода, но я предпочитаю сохранять их одинаковыми, чтобы избежать путаницы.

Строки 21-22: CGI :: Application вызовет метод  1  #!c:/perl/bin/perl -T
 2  use strict;
 3  use CGI::Carp qw(fatalsToBrowser);
 4  use MyApp::User;
 5  my $webapp = MyApp::User->new(tmpl_path => '/path/to/templates/');
 6  $webapp->run();
Это полезно, если вам нужно настроить любую конфигурацию приложения или соединение с базой данных.

Строки 25-29: обрабатывать любые фатальные ошибки. В этом случае обработчик ошибок мало что делает — пойманная ошибка просто отображается в браузере. Однако в рабочем коде вы, вероятно, заносите сообщение в файл или базу данных и, возможно, отправляете электронное письмо системному администратору. Пользователь получит простое сообщение «Мы приносим свои извинения за технические трудности». Вы не хотите показывать оператор SQL, который потерпел неудачу, или что-либо, что раскрыло бы внутреннюю работу приложения.

Строки 32-41: заглушки для методов режима запуска, которые будут вызываться (по сути, компонент контроллера нашего MVC). Мы уточним некоторые из них позже.

Строки 44-45: Здесь мы разместим наш проверочный код. По соглашению мы добавляем подчеркивание к любому методу, который следует считать закрытым.

Строки 48-65: Эти строки представляют компонент модели нашего MVC. Мы могли бы поместить их в отдельный пакет, но в этом приложении имеет смысл сохранить все вместе.

Нам понадобится экземпляр сценария user.cgi, чтобы вызвать пакет:

 @INC

И это все: наш CGI-скрипт имеет длину всего шесть строк! Быстрое объяснение:

Строка 1: позволяет веб-серверу узнать, где находится наш интерпретатор Perl.

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

Строка 4: используйте наш новый пакет.

Строка 5: создайте новый объект на основе нашего пакета. Мы передаем имя каталога, в котором расположены наши шаблоны.

Строка 6: Запустите приложение.

Perl ищет свои пакеты в каталогах, определенных в массиве @INC Другими словами, интерпретатор будет искать файл с именем MyApp / User.pm в любом из каталогов @INC Кроме того, если ни один из этих каталогов не подходит, вы можете программно добавить в use lib  1  #!c:/perl/bin/perl -T
 2  use lib 'path/to/my/libs';
 3  use strict;
 4  use CGI::Carp qw(fatalsToBrowser);
 5  use MyApp::User;
 6  my $webapp = MyApp::User->new(tmpl_path => '/path/to/templates/');
 7  $webapp->run();

   1  <!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">  
 2  <html>  
 3  <head>  
 4  <title>Maintain Users</title>  
 5  </head>  
 6  <body>  
 7  <tmpl_include menu.tmpl><br><br>  
 8  <tmpl_var name=message>  
 9  <tmpl_if some_errors>There are errors in your form</tmpl_if>  
10  <form action="" method="post">  
11    <table>  
12      <tr>  
13        <td>Username<tmpl_unless user_id>*</tmpl_unless></td>  
14        <td><tmpl_unless user_id>  
15                        <input type="Text" name="username" size="15"  
16                            value="<tmpl_var name=username>">  
17                    <tmpl_var name=err_username>  
18                    <tmpl_else>  
19                        <tmpl_var name=username>  
20                    </tmpl_unless>  
21            </td>  
22      </tr>  
23      <tr>  
24        <td>First Name*</td>  
25        <td><input type="Text" name="first_name" size="15"  
26                value="<tmpl_var name=first_name>">  
27                <tmpl_var name=err_first_name></td>  
28      </tr>  
29      <tr>  
30        <td>Last Name*</td>  
31        <td><input type="Text" name="last_name" size="15"  
32                value="<tmpl_var name=last_name>">  
33                <tmpl_var name=err_last_name></td>  
34      </tr>  
35      <tr>  
36        <td>Email</td>  
37        <td><input type="Text" name="email" size="15"  
38                value="<tmpl_var name=email>">  
39                <tmpl_var name=err_email></td>  
40      </tr>  
41      <tr>  
42        <td>Password<tmpl_unless user_id>*</tmpl_unless></td>  
43        <td><input type="Password" name="password" size="15"  
44                value="<tmpl_var name=password>">  
45                <tmpl_var name=err_password> <tmpl_var name=err_user_id></td>  
46      </tr>  
47      <tr>  
48        <td>Repeat Password<tmpl_unless user_id>*</tmpl_unless></td>  
49        <td><input type="Password" name="password2" size="15"  
50                value="<tmpl_var name=password2>">  
51                <tmpl_var name=err_password2></td>  
52      </tr>  
53      <tr>  
54        <td>Receive Newsletter</td>  
55        <td><input type="Checkbox" name="receive_newsletter" value="y"  
56                <tmpl_var name=checked>></td>  
57      </tr>  
58      <tr>  
59        <td colspan="2"><input type="Submit" value="Save"></td>  
60      </tr>  
61    </table>  
62    <input type="Hidden" name="user_id" value="<tmpl_var name=user_id>">  
63    <input type="Hidden" name="rm" value="maintain_user">  
64  </form>  
65  </body>  
66  </html>

Примечание: одним из многих преимуществ CGI :: Application является то, что основное приложение реализовано в пакете, а не в CGI-скрипте. Это означает, что скрипт экземпляра начальной загрузки CGI можно заменить на скрипт экземпляра mod_perl. Для тех, кто не знает, mod_perl — это модуль Apache, который компилирует интерпретатор Perl в ядро ​​Apache. В этом есть много преимуществ, одним из которых является то, что он делает ваш код ослепительно быстрым. Вы можете узнать больше о mod_perl на http://perl.apache.org .

Отображение формы пользователя

Создание шаблона

Как упоминалось ранее, HTML :: Template поддерживается по умолчанию, поэтому наша форма будет довольно простой:

 username

Если вы раньше не сталкивались с HTML :: Template, посмотрите предыдущую статью . Единственные различия между стандартным HTML и нашим шаблоном — это заполнители для атрибута value входных тегов (например, err_usernameuser_id Заполнители будут пустыми при создании новых пользователей и заполнены соответствующими данными во время выполнения.

Достопримечательности включают в себя:

  • Строка 7: мы включаем простое меню, в котором есть опции «создать пользователя» и «изменить пользователя».
  • Строки 14-21: если мы создаем нового пользователя, user_id Если мы изменяем пользователя, мы просто отображаем имя пользователя. Мы также отмечаем обязательные поля звездочкой. Некоторые из этих полей не являются обязательными при редактировании пользователя, поэтому мы скрываем звездочку, если "rm"
  • Строка 63: Когда мы отправляем форму, CGI :: Application запускает режим запуска, указанный параметром display_user_form()

Создание нашего первого метода Run-mode

Метод  1  sub display_user_form {  
 2      my $self = shift;  
 3      my $errs = shift;  
 4      my $q    = $self->query;  
 5  
 6      my $user_id  = $q->param('user_id') || 0;  
 7      # If we have a user id, then get the user's details  
 8      $self->_retrieve_user_details($user_id) if ($user_id);  
 9  
10      # Process the template  
11      my $template = $self->load_tmpl('user_form.tmpl');  
12  
13      # Populate the template  
14      foreach my $form_val qw(username first_name last_name email message) {  
15          $template->param($form_val, $self->param($form_val) || '');  
16      }  
17  
18      $template->param('checked', 'checked')  
19          if ($self->param('receive_newsletter') eq 'y');  
20  
21      $template->param('user_id', $user_id);  
22      $template->param($errs) if $errs;  
23  
24      #Display the form  
25      return $template->output();  
26  }

 $q

Строка 3: мы можем пока игнорировать это. Он используется, когда мы хотим отобразить наши ошибки валидации, о которых я расскажу позже.

Строка 4: по умолчанию CGI :: Application использует модуль CGI за кулисами, и все параметры запроса доступны для метода запроса. Мы присваиваем им значение $q->paramuser_id

Строка 6: получить id Если его нет (т. Е. Мы создаем нового пользователя), установите user_id

Строка 8: если мы обновляем пользователя ( param

Строка 11: загрузить шаблон.

Строки 14-16: CGI :: Application позволяет сохранять параметры приложения, используя метод header_type Мы сохранили данные пользователя в методе, вызванном в строке 8, и здесь мы передаем эти значения в шаблон. Поскольку HTTP является протоколом без сохранения состояния, параметры приложения не будут доступны для последующих запросов. Если вам нужны постоянные данные, вы должны сохранить значения в переменных сеанса.

Строки 18-22: установить остальные параметры шаблона. Строка 22 устанавливает любые сообщения об ошибках, которые могут быть возвращены при проверке формы. Валидация обсуждается позже.

Строка 25: CGI :: Application обрабатывает весь вывод для нас. Мы никогда не выдаем команду «печать» и не выводим заголовки HTTP напрямую. Если вы хотите изменить заголовки, вы можете использовать методы header_propmaintain_user

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

Давайте рассмотрим тривиальную версию метода keep_user.

   1  sub maintain_user {  
 2      my $self = shift;  
 3      my $q    = $self->query;  
 4  
 5      if ($q->param('user_id') == 0) {  
 6          $self->_create_user();  
 7      } else {  
 8          $self->_update_user();  
 9      }  
10  
11      $self->param('message', 'User saved');  
12      $self->display_user_form();  
13  }

В этом режиме выполнения, если user_id_create_user У нас есть два других метода, _update_user  1  sub _user_profile {  
 2      my $self = shift;  
 3      return {  
 4          required          => [qw(username first_name last_name user_id)],  
 5          optional          => [qw(email receive_newsletter)],  
 6          dependencies      => {receive_newsletter => ['email']},  
 7          dependency_groups => {password_group => [qw/password password2/]},  
 8          filters           => ['trim'],  
 9          constraints       => {  
10              email    => 'email',  
11              username => qr/^w{6,10}$/,  
12              password => [  
13              {  
14                  constraint => qr/^w{6,10}$/  
15              },  
16              {  
17                  name       => 'password_mismatch',  
18                  constraint => sub {  
19                      my ($password, $password2) = @_;  
20                      return ($password eq $password2);  
21                  },  
22                  params => [qw(password password2)]  
23              }  
24              ]  
25          },  
26          msgs => {  
27              any_errors  => 'some_errors',  
28              prefix      => 'err_',  
29              constraints => {'password_mismatch' =>    
                                    "Passwords don't match"}  
30          }  
31      };  
32  }
Но, в конечном счете, мы хотим проверить данные в соответствии с набором правил:

  • Имя пользователя, имя и фамилия являются обязательными
  • Пароли должны совпадать
  • Письмо не является обязательным, если пользователь не выбрал получение бюллетеня
  • Адрес электронной почты должен быть в формате электронной почты
  • Имя пользователя должно быть не менее 6 буквенных символов и не более 10 буквенных символов

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

Представляем CGI :: Приложение :: Плагин :: ValidateRM

CGI :: Application :: Plugin :: ValidateRM представляет собой совокупность ряда других модулей: Data :: FormValidator проверяет ввод пользователя с использованием профиля, выраженного в виде структуры данных Perl; HTML :: FillInForm заполняет формы HTML данными, анализируя сам HTML.

Самый простой способ проверить данные — это создать метод, который возвращает профиль проверки:

 "qr/^[a-z]w{2,5}$/i"

Строка 2: это может не потребоваться, если вы не используете какие-либо атрибуты объекта. В этом примере это не так, но если бы нам нужно было расширить процедуру проверки для включения доступа к базе данных, то нам потребовался бы доступ к дескриптору базы данных. Мы обсудим доступ к базе данных позже.

Строка 3: мы возвращаем структуру данных проверки данных. Обратите внимание, что мы могли бы определить это в хеш-ссылке в вызывающем методе, но мы отделяем наши методы контроллера от наших методов модели. Если бы вы были очень строги, вы могли бы поместить их в отдельные пакеты, но я не нашел, что это будет особенно полезно на практике.

Строка 3: список обязательных полей.

Строка 4: список необязательных полей.

Строка 5: поле электронной почты не является обязательным. Однако, если пользователь нажимает на опцию «Получать новостную рассылку», тогда требуется адрес электронной почты.

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

Строки 7: я обычно обрезаю начальное и конечное пространство, так как это очень редко требуется для моих приложений. Оставьте это, если пробелы важны для вас.

Строки 9-25: это список более сложных ограничений.

Строка 9: плагин ValidateRM поставляется с набором ограничений общего формата, одно из которых проверяет адреса электронной почты. Процитируем документацию: «[Ограничение] проверяет, выглядит ли электронная почта как адрес электронной почты. Проверяется, содержит ли вход одно доменное имя @ и двухуровневое имя. Часть адреса проверяется довольно свободно».

Line10: мы можем использовать наши собственные регулярные выражения. Мы говорим, что имя пользователя должно содержать не менее шести буквенно-цифровых символов и не более десяти. Поскольку я пытаюсь проиллюстрировать полезность плагина проверки, я избегал использования более сложного выражения, которое отвлекает от того, с чем я пытаюсь разобраться. Вы можете решить, что «123456» не должно быть действительным именем пользователя, и в этом случае вы можете изменить свое регулярное выражение на params

Строки 12-25: Вы можете иметь несколько ограничений на поле. Первый просто применяет то же правило проверки к паролю, который мы использовали для имени пользователя.

Строки 16-23: этот раздел иллюстрирует три концепции:

  1. Вы можете назвать свои ограничения. Мы воспользуемся этим позже, когда будем указывать пользовательские сообщения об ошибках.
  2. Ограничение может быть применено анонимной подпрограммой. Вам нужно будет сделать это, если ваше правило более сложное, чем простой шаблон.
  3. Массив some_errors

Строки 26-30: определить сообщения, которые мы вернем.

Строка 27: если обнаружены какие-либо ошибки, будет установлена ​​переменная шаблона err_<original_variable_name>

Строки 28: всякий раз, когда мы сталкиваемся с ошибкой поля, мы передаем сообщение об ошибке переменной шаблона с именем "password_mismatch"

Строки 29: Мы предоставляем пользовательское сообщение об ошибке для ограничения maintain_user

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

После того, как мы настроили профиль, нам нужно изменить наш метод keep_user, чтобы использовать его:

   1  sub maintain_user {   
 2      my $self = shift;  
 3      my $q    = $self->query;  
 4  
 5      use CGI::Application::Plugin::ValidateRM (qw/check_rm/);  
 6      my ($results, $err_page) =  
 7          $self->check_rm('display_user_form', '_user_profile');  
 8      return $err_page if $err_page;
 
 9  
10      if ($q->param('user_id') == 0) {  
11          $self->_create_user();  
12      } else {  
13          $self->_update_user();  
14      }  
15  
16      $self->param('message', 'User saved');  
17      $self->display_user_form();  
18  }

Новый код находится в строках 5-8. Мы предоставляем методу check_rm

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

Безопасность ввода пользователя

Одной из основных нереализованных возможностей Perl является режим испорченности: он предполагает, что весь пользовательский ввод является потенциально вредоносным, и испорченный ввод нельзя использовать в коде, который обращается к системной среде. Несмотря на то, что режим загрязнения явно полезен, мы можем обеспечить дополнительный уровень проверки данных в процедуре проверки. Как показано в нашем профиле проверки, легко добавить регулярное выражение, которое гарантирует, что ввод не пытается внедрить HTML или JavaScript, которые могут быть использованы при атаке сценариев.

Такие проверки могут быть довольно трудоемкими при кодировании с нуля, но с этим плагином ValidateRM нагрузка значительно снижается.

Дополнительные проверки

В целях обсуждения я сохранил простой профиль проверки, но вы можете включить некоторые дополнительные правила:

  • Имя пользователя обязательно при создании записи. Это не требуется при обновлении существующего пользователя.
  • При обновлении пользователя поле пароля является необязательным. Если оставить пустым, оригинальный пароль останется.
  • Нам нельзя разрешать создавать имя пользователя, которое уже существует.

Если вы не хотите сами писать эти проверки, вы можете найти их в архиве кода, прилагаемом к этому руководству .

Конфигурация приложения и соединения с базой данных

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

CGI :: Application :: Plugin :: ConfigAuto использует инфраструктуру Config :: Auto. Мы можем поместить информацию о нашей конфигурации в отдельный файл, и наш экземплярный скрипт передает детали в наш пакет приложения. CGI :: Application :: Plugin :: DBH обеспечивает легкий доступ к DBI. Одним из его преимуществ является то, что это ленивая загрузка, что означает, что наше приложение на самом деле не подключается к базе данных, пока нам это не нужно. Это особенно полезно, потому что некоторые из наших режимов запуска не требуют доступа к базе данных.

Во-первых, создайте файл конфигурации, myapp.cfg:

 $cfg{user}            = 'myusername';         
$cfg{password}        = 'mypassword';    
$cfg{dsn}             = 'dbi:driver:mydb';    
$cfg{template_path}   = 'templates/';    
%cfg;

Далее нам нужно внести небольшие изменения в скрипт экземпляра:

   1  #!c:/perl/bin/perl -T    
 2  use lib 'path/to/my/libs';    
 3  use strict;    
 4  use CGI::Carp qw(fatalsToBrowser);    
 5  use MyApp::User;    
 6  my $webapp = MyApp::User->new();    
 7  $webapp->cfg_file('myapp.cfg');
   
 8  $webapp->run();

Мы удалили путь к шаблону из строки 6 и добавили строку 7. Далее мы cgiapp_prerun

   1  sub cgiapp_prerun {    
 2      my $self = shift;    
 3      $self->dbh_config($self->cfg('dsn'), $self->cfg('user'),    
 4          $self->cfg('password'));    
 5      $self->tmpl_path($self->cfg('template_path'));    
 6  }

Теперь к дескриптору базы данных можно получить доступ через атрибут объекта $self->dbh Итак, где мы должны написать следующий код DBI в стандартном приложении:

 $sth = $dbh->prepare("select column from table");

Вместо этого мы пишем:

 $sth = $self->dbh->prepare("select column from table");

Теперь мы можем написать код для создания нашего пользователя:

   1  sub _encrypt_password {    
 2      my $password = shift;    
 3      my @chars    = ('.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z');    
 4      my $salt     = $chars[rand @chars] . $chars[rand @chars];    
 5      return crypt($password, $salt);    
 6  }    
 7    
 8  sub _create_user {    
 9      my $self = shift;    
10      my $q    = $self->query;    
11    
12      my $encrypted_password = _encrypt_password($q->param('password'));    
13    
14      my $sql = "insert into app_user (username, password, first_name, last_name,    
15          email, receive_newsletter) values (?, ?, ?, ?, ?, ?)";    
16    
17      $self->dbh->{PrintError} = 0;    
18      $self->dbh->{RaiseError} = 1;    
19      $self->dbh->do($sql, undef, ($q->param('username'), $encrypted_password,    
20          $q->param('first_name'), $q->param('last_name'), $q->param('email'),    
21          $q->param('receive_newsletter')));    
22  }

Строки 1-6: зашифруйте пароль — мы не храним его в виде простого текста. Этот метод также from _update_user()

Строки 8-22: создать пользователя в базе данных.

Строка 17: Мы выключаем печать ошибок для стандартного вывода, так как это приведет к сбою CGI :: Application. Помните, CGI :: Application обрабатывает весь вывод для нас, включая генерацию заголовков HTTP, поэтому мы не хотим вмешиваться в это. Мы могли бы установить этот атрибут в методе cgiapp_prerun

Строка 18: всякий раз, когда возникает ошибка SQL, мы хотим, чтобы приложение умерло, и в этом случае обработчик сигнала CGI :: Application будет перехватывать ошибку и отправлять ее в метод ошибки, определенный в настройке.

Строки 19-21: вставить данные.

Код для остальных режимов запуска находится в zip-файле, который сопровождает это руководство .

Расширение CGI :: Приложение

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

 use base 'CGI::Application';

с:

 use base 'MyApp::Base';

Затем в методе MyApp :: Base cgiapp_prerun Поскольку этот метод предварительного запуска вызывается автоматически перед выполнением запрошенного режима выполнения, он является идеальным местом для централизации такой логики.

Подсказка: CGI :: Application :: Plugin: Сессия должна помочь вам здесь. Он предоставляет приятный интерфейс для CGI :: Session и делает создание и чтение переменных сеанса такими простыми, как:

 $self->session->param('logged-in', 'y');

И:

 return if ($self->session->param('logged-in') eq 'y');
Резюме

Я надеюсь, что преимущества CGI :: Application очевидны, но давайте суммируем некоторые из них:

  1. CGI :: Приложение вызывает для нас наши режимы работы. Это избавляет от большого количества кода спагетти, который часто пишут разработчики, когда сами реализуют логику режима выполнения.
  2. Наш код более удобен в обслуживании. Мы можем передать шаблоны нашим HTML-дизайнерам, пока мы просто беспокоимся о логике приложения. Помните MVC: методы базы данных и профили валидации действуют как наши модели, режимы выполнения — это контроллеры, а шаблоны — это наши представления. Некоторые разработчики могут по-разному перекладывать или перераспределять ответственность за эти компоненты — возможно, по нескольким пакетам. Но по сути у нас есть мощная, но простая модель для разработки наших приложений.
  3. Каркас является расширяемым. Мы можем расширить или превзойти любой метод, который нам нравится, чтобы удовлетворить наши потребности.
  4. Растет количество плагинов, включая все, от логирования до потоковых файлов. Эти плагины часто используются в качестве оберток вокруг других модулей, но они делают большую часть тяжелой работы для нас.
  5. Мы не ограничены CGI-скриптами. Поскольку почти весь наш код реализован в пакетах, мы можем использовать сценарии экземпляра mod_perl вместо эквивалентов CGI. Таким образом, мы получаем все преимущества прямого доступа к внутренностям Apache, а также сверхбыстрого кода и постоянных соединений с базами данных.
  6. Существует активное и полезное пользовательское сообщество. Вы можете зарегистрироваться в списке рассылки или выполнить поиск в архиве . Существует также довольно хорошая вики .

Наконец, я хотел бы отметить, что некоторые бесстрашные разработчики перенесли CGI :: Application на другие языки. Доступна версия PHP , и автор в своем блоге рассказывает о ее полезности из первых рук . Аналогично, любой программист Python должен посетить http://thraxil.org/code/cgi_app/ .