Основы работы с маршрутизатором запросов Mojolicious и его основные принципы |
- Statistics
- Participants
- Translate into Russian
- Translation result
- Translation complete.
# Copyright (C) 2008-2010, Sebastian Riedel.
=encoding utf8
=head1 НАЗВАНИЕ
Mojolicious::Guides::Routing - Маршрутизация
=head1 ОБЗОР
Этот документ содержит простое и приятное руководство по работе с маршрутизатором L<Mojolicious> и основные его понятия.
=head1 ПОНЯТИЯ
Основы, которые должен знать каждый L<Mojolicious> разработчик.
=head2 Диспетчер
Основой каждого веб фреймворка является небольшой черный ящик, связывающий входящие запросы с кодом, генерующим соответствующий ответ.
GET /user/show/1 -> $self->render(text => 'Себастьян!');
Этот черный ящик обычно называют диспетчером. Существует множество реализаций, использующих различные подходы для создания таких связей, но практически все, так или иначе, основаны на связывании путей запросов с каким-либо генератором ответа.
/user/show/1 -> $self->render(text => 'Себастьян!');
/user/show/2 -> $self->render(text => 'Сара!');
/user/show/3 -> $self->render(text => 'Беирбел!');
/user/show/4 -> $self->render(text => 'Вольфганг!');
Конечно возможно сделать все эти связи статичными, но, безусловно, это не эффективно. По этому часто применяются регулярные выражения, чтобы сделать процесс маршрутизации более динамичным.
qr|/user/show/(\d+)| -> $self->render(text => $users{$1});
В современных диспетчерах есть доступ ко всему, что может предложить HTTP. Можно использовать не только путь запроса, но и, к примеру, метод запроса или заголовки C<Host>, C<User-Agent> и C<Accept>.
GET /user/show/23 HTTP/1.1
Host: mojolicious.org
User-Agent: Mozilla/5.0 (compatible; Mojolicious; Perl)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
=head2 Маршруты
Хотя регулярные выражения достаточно мощное средство, они не очень понятно выглядят и слишком избыточны для простого сопоставления пути.
qr|/user/show/(\d+)| -> $self->render(text => $users{$1});
Это как раз тот случай, когда можно применить маршруты. Они были спроектированы специально для представления путей со специальными метками (placeholder'ами).
/user/show/:id -> $self->render(text => $users{$id});
Единственная разница между статическим путем и маршрутом приведенным выше - это специальная метка C<:id>. В любом месте маршрута может быть более одной метки .
/user/:action/:id
Основная идея маршрутизатора L<Mojolicious> в том, что извлеченные значения меток превращаются в хеш.
/user/show/23 -> /user/:action/:id -> {action => 'show', id => 23}
Этот хэш - центр каждого L<Mojolicious> приложения, вы узнаете об этом чуть позже. Внутри приложения маршруты компилируются в регулярные выражения, по этому вы можете использовать лучшее обоих подходов поднакопив немного опыта.
/user/show/:id -> qr/(?-xism:^\/user\/show/([^\/\.]+))/
=head2 Обратимость
Еще одно преимущество маршрутов перед регулярными выражениями - это возможность обратной операции. Выделенные значения могут быть превращены в путь когда угодно.
/sebastian -> /:name -> {name => 'sebastian'}
{name => 'sebastian'} -> /:name -> /sebastian
=head2 Универсальные метки
Универсальные метки - самый простой вариант меток. Они захватывают любые символы кроме C</> и С<.>.
/hello -> /:name/hello -> undef
/sebastian/23/hello -> /:name/hello -> undef
/sebastian.23/hello -> /:name/hello -> undef
/sebastian/hello -> /:name/hello -> {name => 'sebastian'}
/sebastian23/hello -> /:name/hello -> {name => 'sebastian23'}
/sebastian 23/hello -> /:name/hello -> {name => 'sebastian 23'}
Метку можно окружить круглыми скобками, если есть необходимость отделить ее от другого текста.
/hello -> /(:name)hello -> undef
/sebastian/23hello -> /(:name)hello -> undef
/sebastian.23hello -> /(:name)hello -> undef
/sebastianhello -> /(:name)hello -> {name => 'sebastian'}
/sebastian23hello -> /(:name)hello -> {name => 'sebastian23'}
/sebastian 23hello -> /(:name)hello -> {name => 'sebastian 23'}
=head2 Нестрогие метки
Очень похожи на универсальные метки, за исключением обязательных скобок и совпадением со всеми символами кроме C</>.
/hello -> /(.name)/hello -> undef
/sebastian/23/hello -> /(.name)/hello -> undef
/sebastian.23/hello -> /(.name)/hello -> {name => 'sebastian.23'}
/sebastian/hello -> /(.name)/hello -> {name => 'sebastian'}
/sebastian23/hello -> /(.name)/hello -> {name => 'sebastian23'}
/sebastian 23/hello -> /(.name)/hello -> {name => 'sebastian 23'}
=head2 Метки со специальными символами
Метки со специальными символами похожи на нестрогие метки, но соответствуют любым символам.
/hello -> /(*name)/hello -> undef
/sebastian/23/hello -> /(*name)/hello -> {name => 'sebastian/23'}
/sebastian.23/hello -> /(*name)/hello -> {name => 'sebastian.23'}
/sebastian/hello -> /(*name)/hello -> {name => 'sebastian'}
/sebastian23/hello -> /(*name)/hello -> {name => 'sebastian23'}
/sebastian 23/hello -> /(*name)/hello -> {name => 'sebastian 23'}
=head1 ОСНОВЫ
Часто используемые функциональные возможности о которых должен знать каждый разработчик.
=head2 Простой маршрут
В каждом L<Mojolicious> приложении есть объект-маршрутизатор, который можно использовать для создания маршрутов.
# Приложение
package MyApp;
use base 'Mojolicious';
sub startup {
my $self = shift;
# Маршрутизатор
my $r = $self->routes;
# Маршрут
$r->route('/welcome')->to(controller => 'foo', action => 'welcome');
}
1;
Простой статический маршрут, показанный выше, загрузит модуль C<MyApp::Foo>, создаст экземпляр класса и вызовет метод C<welcome>.
# Контроллер
package MyApp::Foo;
use base 'Mojolicious::Controller';
# Действие
sub welcome {
my $self = shift;
# Ответ
$self->render(text => 'Привет!');
}
1;
Маршруты обычно настраиваются в методе C<startup> приложения, но объект-маршрутизатор доступен везде (в том числе на этапе исполнения).
=head2 Пункт назначения маршрута
После того как вы начали новый маршрут вызвав метод C<route>, вы также можете указать ему пункт назначения в виде простого хеша используя связанный метод C<to>.
# /welcome -> {controller => 'foo', action => 'welcome'}
$r->route('/welcome')->to(controller => 'foo', action => 'welcome');
Теперь, если маршрут совпадает с запросом - маршрутизатор будет использовать значения указанного хеша чтобы проверить и найти подходящий код для формирования ответа.
=head2 Stash - хранилище данных приложения
Сформированный хеш соответствующего маршрута фактически основа всего цикла запроса L<Mojolicious>.
Мы называем это - stash, попросту это глобальное пространство имен, существующее до момента формирования ответа.
# /bye -> {controller => 'foo', action => 'bye', mymessage => 'Пока!'}
$r->route('/bye')
->to(controller => 'foo', action => 'bye', mymessage => 'Пока!');
Есть несколько специальных stash значений, например таких как, C<controller> и C<action>, однако их можно переопределить любыми данными, необходимыми для формирования ответа. После обработки запроса значения stash могут быть изменены когда угодно.
=head2 Специальные значения в stash (С<controller> и C<action>)
Когда диспетчер видит, значения C<controller> и C<action> в stash он
всегда будет пытаться превратить их в класс и метод для обработки.
Значение C<controller> приводится к ВерблюжемуСтилю и к нему добавляется префикс C<namespace>
(установленный по умолчанию для класса приложений) до тех пор пока значение action не изменится полностью. Из-за этого оба значения чувствительны к регистру.
# Приложение
package MyApp;
use base 'Mojolicious';
sub startup {
my $self = shift;
# Маршрутизатор
my $r = $self->routes;
# /bye -> {controller => 'foo', action => 'bye'} -> MyApp::Foo->bye
$r->route('/bye')->to(controller => 'foo', action => 'bye');
1;
# Контроллер
package MyApp::Foo;
use base 'Mojolicious::Controller';
# Действие
sub bye {
my $self = shift;
# Ответ
$self->render(text => 'До свидания!');
}
1;
Классы контроллеров идеальны для организации кода в больших проектах. Есть множество других стратегий обработки запроса, но так как контроллеры используются чаще всего, для них есть краткая форма записи C<controller#action>.
# /bye -> {controller => 'foo', action => 'bye', mymessage => 'Пока!'}
$r->route('/bye')->to('foo#bye', mymessage => 'Пока!');
Во время преобразования регистра симвлов C<-> заменяется на C<::>, это позволяет использовать многоуровневые иерархии
C<controller>.
# / -> {controller => 'foo-bar', action => 'Привет'} -> MyApp::Foo::Bar->hi
$r->route('/')->to('foo-bar#hi');
=head2 Маршрут к классу (C<namespace>)
Время от времени может возникнуть необходимость обработки запроса в совершенно другое пространство имен C<namespace>.
# /bye -> MyApp::Controller::Foo->bye
$r->route('/bye')
->to(namespace => 'MyApp::Controller::Foo', action => 'bye');
Имя контроллера C<controller> всегда добавляется к пространству имен C<namespace>, если оно установлено.
# /bye -> MyApp::Controller::Foo->bye
$r->route('/bye')->to('foo#bye', namespace => 'MyApp::Controller');
Вы так же можете изменять стандартные пространства имен для всех маршрутов.
$r->namespace('MyApp::Controller');
=head2 Маршрут связанный с функцией обратного вызова (C<cb>)
Переменная C<cb> из stash может быть использована для выполнения функции обратного вызова вместо контроллера.
$r->route('/bye')->to(cb => sub {
my $self = shift;
$self->render(text => 'Счастливо!');
});
Эта техника - основа L<Mojolicious::Lite>, подробнее о которой можно узнать из поставляемой документации.
=head2 Форматы
Расширения файлов такие как C<.html> и C<.txt> в конце перенаправления автоматически определяются и сохраняются в спрятанном значении C<format>.
# /foo -> {controller => 'foo', action => 'bar'}
# /foo.html -> {controller => 'foo', action => 'bar', format => 'html'}
# /foo.txt -> {controller => 'foo', action => 'bar', format => 'txt'}
$r->route('/foo')->to(controller => 'foo', action => 'bar');
Это, например, допускает многочисленные шаблоны для различных форматов представления одного и того же кода.
=head2 Метки и пункты назначения
Извлекаемые значения служебных меток просто переопределяют старые значения stash, если существовали ранее.
# /bye -> {controller => 'foo', action => 'bar', mymessage => 'bye'}
# /hey -> {controller => 'foo', action => 'bar', mymessage => 'hey'}
$r->route('/:mymessage')
->to(controller => 'foo', action => 'bar', mymessage => 'hi');
Еще один интересный эффект: если служебная метка находится в конце маршрута и в stash есть значение с таким же именем, метка автоматически становится необязательной.
# / -> {controller => 'foo', action => 'bar', mymessage => 'hi'}
$r->route('/:mymessage')
->to(controller => 'foo', action => 'bar', mymessage => 'hi');
