Документация Perl 5

Mojolicious::Lite


НАЗВАНИЕ

Mojolicious::Lite — микро веб-фреймворк

КРАТКИЙ ОБЗОР

    # При загрузке Mojolicious::Lite активируются режимы "strict" и "warnings"
    use Mojolicious::Lite;
     
    # Маршрут с заполнителем
    get '/:foo' => sub {
        my $self = shift;
        my $foo  = $self->param('foo');
        $self->render(text => "Hello from $foo!");
    };
     
    # Запуск системы команд Mojolicious
    app->start;

ОПИСАНИЕ

Mojolicious::Lite — это микро веб-фреймворк, входящий в состав Mojolicious.

УЧЕБНОЕ ПОСОБИЕ

Введение в удивительный Mojolicious::Lite на простых показательных примерах.

Hello World!

Простое приложение типа "Hello World" может выглядеть примерно как приведено ниже, при загрузке Mojolicious::Lite автоматически включается действие прагм strict и warnings и портируется несколько функций, превращая сценарий в полнофункциональное веб-приложение.

  #!/usr/bin/env perl
  use Mojolicious::Lite;
     
  get '/' => sub {
    my $self = shift;
    $self->render(text => 'Hello World!');
  };
     
  app->start;

Генератор

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

  $ mojo generate lite_app

Команды

Все обычные опции команд Mojolicious доступны из командной строки. Обратите внимание, что среды CGI, FastCGI и PSGI, как правило, автоматически обнаруживаются и правильно работают без команд.

  $ ./myapp.pl daemon
  Server available at http://127.0.0.1:3000.
     
  $ ./myapp.pl daemon --listen http://*:8080
  Server available at http://127.0.0.1:8080.
     
  $ ./myapp.pl cgi
  ...Вывод CGI...
     
  $ ./myapp.pl fastcgi
  ...Основной цикл блокирующего FastCGI...
     
  $ ./myapp.pl
  ...Перечень доступных команд(или автоматически определяется средой)...

Запуск

Вызов app->start, который запускает систему команд Mojolicious, может быть настроен так, чтобы переопределить обычное использование @ARGV.

    app->start('cgi');

Перезагрузка

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

  $ morbo myapp.pl
  Server available at http://127.0.0.1:3000.

Маршруты

Маршруты в основном просто изощренные пути, которые могут содержать различные типы заполнителей. $self является экземпляром Mojolicious::Controller, содержащий HTTP запрос и ответ.

    # /foo
    get '/foo' => sub {
        my $self = shift;
        $self->render(text => 'Hello World!');
    };

Параметры GET/POST

Все GET и POST параметры доступны через param.

  # /foo?user=sri
  get '/foo' => sub {
    my $self = shift;
    my $user = $self->param('user');
    $self->render(text => "Привет $user!");
  };

Stash

stash используется для передачи данных в шаблоны, которые могут быть встроены в секции DATA.

    # /bar
    get '/bar' => sub {
        my $self = shift;
        $self->stash(one => 23);
        $self->render('baz', two => 24);
    };
     
    __DATA__
     
    @@ baz.html.ep
    Магические числа <%= $one %> и <%= $two %>.

HTTP

Mojo::Message::Request и Mojo::Message::Response дают вам полный доступ ко всем функциям HTTP и информации

  # /agent
  get '/agent' => sub {
    my $self = shift;
    $self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!');
    $self->render(text => $self->req->headers->user_agent);
  };

Имена маршрутов

Все маршруты могут иметь имя, ассоциированное с ними, это позволяет автоматически обнаруживать шаблоны и обратную привязку с url_for, link_to и form_for. Безымянные маршруты автоматически формируют имена, которые эквивалентны самому маршруту без учета словообразующих символов.

  # /
  get '/' => 'index';
     
  # /hello
  get '/hello';
     
  __DATA__
     
  @@ index.html.ep
  <%= link_to Hello => 'hello' %>.
  <%= link_to Reload => 'index' %>.
     
  @@ hello.html.ep
  Hello World!

Общие шаблоны (Layouts)

В шаблонах могут использоваться общие шаблоны.

  # /with_layout
  get '/with_layout' => sub {
    my $self = shift;
    $self->render('with_layout');
  };
     
  __DATA__
     
  @@ with_layout.html.ep
  % title 'Green!';
  % layout 'green';
  We've got content!
     
  @@ layouts/green.html.ep
  <!doctype html><html>
    <head><title><%= title %></title></head>
    <body><%= content %></body>
  </html>

Блоки

Блоки шаблонов могут быть использованы как обычные Perl функции и всегда разделяются ключевыми словами begin и end.

  # /with_block
  get '/with_block' => 'block';
     
  __DATA__
     
  @@ block.html.ep
  <% my $link = begin %>
    <% my ($url, $name) = @_; %>
    Try <%= link_to $url => begin %><%= $name %><% end %>!
  <% end %>
  <!doctype html><html>
    <head><title>Sebastians Frameworks!</title></head>
    <body>
      <%= $link->('http://mojolicio.us', 'Mojolicious') %>
      <%= $link->('http://catalystframework.org', 'Catalyst') %>
    </body>
  </html>

Захваченный контент

Для передачи блоков захваченного контента можно использовать хэлпер content_for.

  # /captured
  get '/captured' => sub {
    my $self = shift;
    $self->render('captured');
  };
     
  __DATA__
     
  @@ captured.html.ep
  % layout 'blue', title => 'Green!';
  <% content_for header => begin %>
    <meta http-equiv="Pragma" content="no-cache">
  <% end %>
  We've got content!
  <% content_for header => begin %>
    <meta http-equiv="Expires" content="-1">
  <% end %>
     
  @@ layouts/blue.html.ep
  <!doctype html><html>
    <head>
      <title><%= title %></title>
      <%= content_for 'header' %>
    </head>
    <body><%= content %></body>
  </html>

Хэлперы (helpers)

Также вы можете также расширить Mojolicious вашими собственными хэлперами(вспомогательными функциями). Список всех встроенных хэлперов находится в документации Mojolicious::Plugin::DefaultHelpers и Mojolicious::Plugin::TagHelpers.

  # "whois" helper
  helper whois => sub {
    my $self  = shift;
    my $agent = $self->req->headers->user_agent || 'Anonymous';
    my $ip    = $self->tx->remote_address;
    return "$agent ($ip)";
  };
     
  # /secret
  get '/secret' => sub {
    my $self = shift;
    my $user = $self->whois;
    $self->app->log->debug("Request from $user.");
  };
     
  __DATA__
     
  @@ secret.html.ep
  We know who you are <%= whois %>.

Заполнители

Заполнители маршрутов позволяют захватывать части пути запроса до разделителей / или ., результаты будут храниться по имени в stash и param.

  # /foo/test
  # /foo/test123
  get '/foo/:bar' => sub {
    my $self = shift;
    my $bar  = $self->stash('bar');
    $self->render(text => "Our :bar placeholder matched $bar");
  };
     
  # /test/foo
  # /test123/foo
  get '/(:bar)something/foo' => sub {
    my $self = shift;
    my $bar  = $self->param('bar');
    $self->render(text => "Our :bar placeholder matched $bar");
  };

Групповые заполнители

Групповые заполнители (анг. wildcard placeholders) могут соответствовать абсолютно всем символам, в том числе и / и ..

  # /hello/test
  # /hello/test123
  # /hello/test.123/test/123
  get '/hello/*you' => sub {
    shift->render('groovy');
  };
     
  __DATA__
     
  @@ groovy.html.ep
  Your name is <%= $you %>.

HTTP методы

Маршруты можно ограничить конкретными методами запроса.

  # GET /bye
  get '/bye' => sub { shift->render(text => 'Bye!') };
     
  # POST /bye
  post '/bye' => sub { shift->render(text => 'Bye!') };
     
  # GET|POST|DELETE /bye
  any [qw/get post delete/] => '/bye' => sub {
    shift->render(text => 'Bye!');
  };
     
  # * /baz
  any '/baz' => sub {
    my $self   = shift;
    my $method = $self->req->method;
    $self->render(text => "You called /baz with $method");
  };

Необязательные заполнители

Маршрутам можно указывать значения по умолчанию, чтобы сделать заполнители необязательными.

  # /hello
  # /hello/Sara
  get '/hello/:name' => {name => 'Sebastian'} => sub {
    my $self = shift;
    $self->render('groovy', format => 'txt');
  };
     
  __DATA__
     
  @@ groovy.txt.ep
  My name is <%= $name %>.

Ограничительные заполнители

Самый простой способ сделать заполнители более ограничительными альтернативами, просто составить список возможных значений.

  # /test
  # /123
  any '/:foo' => [foo => [qw/test 123/]] => sub {
    my $self = shift;
    my $foo  = $self->param('foo');
    $self->render(text => "Our :foo placeholder matched $foo");
  };

Все заполнители внутренне компилируются в регулярное выражение, этот процесс можно также легко настроить.

  # /1
  # /123
  any '/:bar' => [bar => qr/\d+/] => sub {
    my $self = shift;
    my $bar  = $self->param('bar');
    $self->render(text => "Our :bar placeholder matched $bar");
  };

Просто убедитесь, что не используются ^ и $ или захватывающие группы (...), потому что заполнители внутренне становятся частью большого регулярного выражения, хотя можно использовать (?:...).

Форматы

Форматы могут автоматически определяться по расширению файлов.

  # /detection.html
  # /detection.txt
  get '/detection' => sub {
    my $self = shift;
    $self->render('detected');
  };
     
  __DATA__
     
  @@ detected.html.ep
  <!doctype html><html>
    <head><title>Detected!</title></head>
    <body>Определен формат HTML.</body>
  </html>
     
  @@ detected.txt.ep
  определен формат TXT

Ограничительные заполнители могут также использоваться для определения формата.

  # /hello.json
  # /hello.txt
  get '/hello' => [format => [qw/json txt/]] => sub {
    my $self = shift;
    return $self->render_json({hello => 'world!'})
      if $self->stash('format') eq 'json';
    $self->render_text('hello world!');
  };

Under

Аутентификация и код, разделенные между несколькими маршрутами, могут быть реализованы легко c помощью оператора under. Все последующие маршруты вычисляются только если функция обратного вызова under возвращает истину.

  use Mojolicious::Lite;
     
  # Проверка подлинности по параметру name
  under sub {
    my $self = shift;
     
    # Проверка прошла успешно
    my $name = $self->param('name') || '';
    return 1 if $name eq 'Bender';
     
    # Безуспешная проверка
    $self->render('denied');
    return;
  };
     
  # / (с проверкой подлинности)
  get '/' => 'index';
     
  app->start;
  __DATA__;
     
  @@ denied.html.ep
  Вы не Bender, доступ запрещен!
     
  @@ index.html.ep
  Привет Bender!

Еще один хороший способ применения under — задание префикса для нескольких маршрутов.

  use Mojolicious::Lite;
     
  # /foo
  under '/foo';
     
  # /foo/bar
  get '/bar' => sub { shift->render(text => 'bar!') };
     
  # /foo/baz
  get '/baz' => sub { shift->render(text => 'baz!') };
     
  app->start;

Условия

Условия, такие как agent и host из Mojolicious::Plugin::HeaderCondition позволяют создавать еще более мощные конструкции маршрутов.

  # /foo
  get '/foo' => (agent => qr/Firefox/) => sub {
    shift->render(text => 'Поздравляем, у вас хороший браузер!');
  };
     
  # /foo
  get '/foo' => (agent => qr/Internet Explorer/) => sub {
    shift->render(text => 'Чувак, тебе действительно нужно перейти на Firefox!');
  };
     
  # /bar
  get '/bar' => (host => 'mojolicio.us') => sub {
    shift->render(text => 'Hello Mojolicious!');
  };

Сессии

Подписанные cookies, основанные на сессиях, работают прямо "из коробки", как только вы начнете их использовать. Для хранения значений, которые должны быть доступны только в следующем запросе, используется flash (в отличие от stash, который доступен только для текущего запроса), это очень полезно в сочетании с redirect_to.

  use Mojolicious::Lite;
     
  get '/login' => sub {
    my $self = shift;
    my $name = $self->param('name') || '';
    my $pass = $self->param('pass') || '';
    return $self->render unless $name eq 'sebastian' && $pass eq '1234';
    $self->session(name => $name);
    $self->flash(message => 'Вы успешно вошли в систему!');
    $self->redirect_to('index');
  } => 'login';
     
  get '/' => sub {
    my $self = shift;
    return $self->redirect_to('login') unless $self->session('name');
    $self->render;
  } => 'index';
     
  get '/logout' => sub {
    my $self = shift;
    $self->session(expires => 1);
    $self->redirect_to('index');
  } => 'logout';
     
  app->start;
  __DATA__
     
  @@ layouts/default.html.ep
  <!doctype html><html>
    <head><title><%= title %></title></head>
    <body><%= content %></body>
  </html>
     
  @@ login.html.ep
  % layout 'default';
  % title 'Login';
  <%= form_for login => begin %>
    <% if (param 'name') { %>
      <b>Неверное имя или пароль, пожалуйста, попробуйте еще раз.</b><br>
    <% } %>
    Имя:<br>
    <%= text_field 'name' %><br>
    Пароль:<br>
    <%= password_field 'pass' %><br>
    <%= submit_button 'Войти' %>
  <% end %>
     
  @@ index.html.ep
  % layout 'default';
  % title 'Добро пожаловать!';
  <% if (my $message = flash 'message' ) { %>
    <b><%= $message %></b><br>
  <% } %>
  Welcome <%= session 'name' %>!<br>
  <%= link_to logout => begin %>
    Выход из системы
  <% end %>

Секретная подпись

Отметим, что вы должны использовать пользовательский secret, чтобы сделать подписанные cookies действительно секретными.

    app->secret('Здесь мой секретный пароль!');

Загрузка файлов

Все файлы, загруженные посредством запроса multipart/form-data, автоматически доступны как экземпляры Mojo::Upload. И вам не придется беспокоиться о памяти, потому что все файлы больше 250KB будут автоматически направлены во временной файл.

  use Mojolicious::Lite;
     
  any '/upload' => sub {
    my $self = shift;
    if (my $example = $self->req->upload('example')) {
      my $size = $example->size;
      my $name = $example->filename;
      $self->render(text => "Спасибо за закачку $size байт файла $name.");
    }
  };
     
  app->start;
  __DATA__
     
  @@ upload.html.ep
  <!doctype html><html>
    <head><title>Закачка</title></head>
    <body>
      <%= form_for upload =>
            (method => 'post', enctype => 'multipart/form-data') => begin %>
        <%= file_field 'example' %>
        <%= submit_button 'Закачать' %>
      <% end %>
    </body>
  </html>

Чтобы защитить вас от слишком больших файлов, есть также общий предел в 5MB по умолчанию, который вы можете настроить переменной среды MOJO_MAX_MESSAGE_SIZE.

  # Увеличение предела до 1GB
  $ENV{MOJO_MAX_MESSAGE_SIZE} = 1073741824;

Агент пользователя

С Mojo::UserAgent есть полнофункциональный HTTP 1.1 и WebSocket агент пользователя встроенный в него. Особенно в сочетании с Mojo::JSON и Mojo::DOM это может быть очень мощным инструментом.

  get '/test' => sub {
    my $self = shift;
    $self->render(data => $self->ua->get('http://mojolicio.us')->res->body);
  };

Веб-сокеты

Приложение на основе технологии web-сокетов (WebSockets) никогда не было настолько простым.

    websocket '/echo' => sub {
        my $self = shift;
        $self->on_message(sub {
            my ($self, $message) = @_;
            $self->send_message("echo: $message");
        });
    };

Внешние шаблоны

Рендерер производит поиск внешних шаблонов в каталоге template.

    # /external
    any '/external' => sub {
        my $self = shift;
     
        # templates/foo/bar.html.ep
        $self->render('foo/bar');
    };

Статические файлы

Статические файлы подаются из секции DATA(даже закодированные в base64) или из каталога public, если он существует.

  @@ something.js
  alert('hello!');
     
  @@ test.txt (base64)
  dGVzdCAxMjMKbGFsYWxh
     
  $ mkdir public
  $ mv something.js public/something.js

Тестирование

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

  use Test::More tests => 3;
  use Test::Mojo;
     
  use FindBin;
  require "$FindBin::Bin/../myapp.pl";
     
  my $t = Test::Mojo->new;
  $t->get_ok('/')->status_is(200)->content_like(qr/Прикольно!/);

Выполняет все модульные тесты с помощью команды test.

  $ ./myapp.pl test

Чтобы сделать ваши тесты более шумными и показать вам все сообщения журнала, вы также можете изменить уровень логирования событий приложения непосредственно в ваших тестовых файлах.

    $t->app->log->level('debug');

Режим

Чтобы отключить сообщения отладки позже в производственной установке, можно изменить режим Mojolicious, по умолчанию будет development.

  $ ./myapp.pl --mode production

Логирование

Сообщения Mojo::Log автоматически записываются в файл log/$mode.log, если существует каталог log.

  $ mkdir log

Для большего контроля к экземпляру Mojolicious можно обратиться напрямую.

  app->log->level('error');
  app->routes->route('/foo/:bar')->via('get')->to(cb => sub {
    my $self = shift;
    $self->app->log->debug('Есть запрос "Hello Mojo!".');
    $self->render(text => 'Hello Mojo!');
  });

Развитие

В случае, если lite приложение должно расти, приложения lite и Mojolicious можно легко совмещать, чтобы сделать гладким переходный процесс.

  package MyApp::Foo;
  use Mojo::Base 'Mojolicious::Controller';
     
  sub index { shift->render(text => 'Это работает!') }
     
  package main;
  use Mojolicious::Lite;
     
  get '/bar' => sub { shift->render(text => 'Это тоже работает!') };
     
  app->routes->namespace('MyApp');
  app->routes->route('/foo/:action')->via('get')->to('foo#index');
     
  app->start;

Есть также вспомогательная команда для генерирования полного примера Mojolicious, что позволяет вам исследовать удивительные сходства между приложениями Mojolicious::Lite и Mojolicious. Они оба содержат примерно 99% того же самого кода, так что почти все, что вы узнали в этом руководстве применимо и к Mojolicious. :)

  $ mojo generate app

Дополнительно

Вы можете продолжить перейдя на страницу Mojolicious::Guides сейчас, и не забывайте развлекаться!

ФУНКЦИИ

Mojolicioust::Lite реализует следующие функции.

any

    my $route = any '/:foo' => sub {...};
    my $route = any [qw/get post/] => '/:foo' => sub {...};

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

app

    my $app = app;

Приложение Mojolicious::Lite.

del

    my $route = del '/:foo' => sub {...};

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

get

    my $route = get '/:foo' => sub {...};

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

helper

  helper foo => sub {...};

Добавление нового хэлпера, который будет доступен как метод объекта контроллера и объекта приложения, а также, в виде функции в ep шаблонах.

  # Хэлпер
  helper add => sub { $_[1] + $_[2] };
     
  # Контроллер/Приложение
  my $result = $self->add(2, 3);
     
  # Шаблон
  <%= add 2, 3 %>

Обратите внимание, что эта функция является экспериментальной и может измениться без предупреждения!

hook

  hook after_dispatch => sub {...};

Добавление хуков в именованные события, см. перечень доступных событий в Mojolicious. Отметим, что эта функция является экспериментальной и может измениться без предупреждения!

plugin

    plugin 'something';
    plugin 'something', foo => 23;
    plugin 'something', {foo => 23};
    plugin 'Foo::Bar';
    plugin 'Foo::Bar', foo => 23;
    plugin 'Foo::Bar', {foo => 23};

Загрузка плагинов, см. перечень всех входящих плагинов-образцов в Mojolicious.

post

    my $route = post '/:foo' => sub {...};

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

put

    my $route = put '/:foo' => sub {...};

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

under

    my $route = under sub {...};
    my $route = under '/:foo';

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

websocket

    my $route = websocket '/:foo' => sub {...};

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

АТРИБУТЫ

Mojolicious::Lite реализует все атрибуты из Mojolicious.

МЕТОДЫ

Mojolicious::Lite реализует все методы Mojolicious.

СМ. ТАКЖЕ

Mojolicious, Mojolicious::Guides, http://mojolicio.us.