Основы работы с маршрутизатором запросов Mojolicious и его основные принципы

Sebastian Riedel (kraih), “Introduction to the Mojolicious router and its underlying concepts”, public translation into Russian from English More about this translation.

Translate into another language.

# 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');

Pages: ← previous Ctrl next
1 2

© 2008-2010, Sebastian Riedel.

Original (English): Introduction to the Mojolicious router and its underlying concepts

Translation: © r3code, cubloid, Foxcool, xoma, korshak, vti, koban, zhdinar .

translated.by crowd

Like this translation? Share it or bookmark!