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.