1 |
|
---|
2 | =encoding utf8
|
---|
3 |
|
---|
4 | =head1 NAME
|
---|
5 |
|
---|
6 | Mojolicious::Guides::Routing - Routing requests
|
---|
7 |
|
---|
8 | =head1 OVERVIEW
|
---|
9 |
|
---|
10 | This document contains a simple and fun introduction to the L<Mojolicious>
|
---|
11 | router and its underlying concepts.
|
---|
12 |
|
---|
13 | =head1 CONCEPTS
|
---|
14 |
|
---|
15 | Essentials every L<Mojolicious> developer should know.
|
---|
16 |
|
---|
17 | =head2 Dispatcher
|
---|
18 |
|
---|
19 | The foundation of every web framework is a tiny black box connecting incoming
|
---|
20 | requests with code generating the appropriate response.
|
---|
21 |
|
---|
22 | GET /user/show/1 -> $c->render(text => 'Daniel');
|
---|
23 |
|
---|
24 | This black box is usually called a dispatcher. There are many implementations
|
---|
25 | using different strategies to establish these connections, but pretty much all
|
---|
26 | are based around mapping the path part of the request URL to some kind of
|
---|
27 | response generator.
|
---|
28 |
|
---|
29 | /user/show/2 -> $c->render(text => 'Isabell');
|
---|
30 | /user/show/3 -> $c->render(text => 'Sara');
|
---|
31 | /user/show/4 -> $c->render(text => 'Stefan');
|
---|
32 | /user/show/5 -> $c->render(text => 'Fynn');
|
---|
33 |
|
---|
34 | While it is very well possible to make all these connections static, it is also
|
---|
35 | rather inefficient. That's why regular expressions are commonly used to make
|
---|
36 | the dispatch process more dynamic.
|
---|
37 |
|
---|
38 | qr!/user/show/(\d+)! -> $c->render(text => $users{$1});
|
---|
39 |
|
---|
40 | Modern dispatchers have pretty much everything HTTP has to offer at their
|
---|
41 | disposal and can use many more variables than just the request path, such as
|
---|
42 | request method and headers like C<Host>, C<User-Agent> and C<Accept>.
|
---|
43 |
|
---|
44 | GET /user/show/23 HTTP/1.1
|
---|
45 | Host: mojolicious.org
|
---|
46 | User-Agent: Mojolicious (Perl)
|
---|
47 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
---|
48 |
|
---|
49 | =head2 Routes
|
---|
50 |
|
---|
51 | While regular expressions are quite powerful they also tend to be unpleasant to
|
---|
52 | look at and are generally overkill for ordinary path matching.
|
---|
53 |
|
---|
54 | qr!/user/show/(\d+)! -> $c->render(text => $users{$1});
|
---|
55 |
|
---|
56 | This is where routes come into play, they have been designed from the ground up
|
---|
57 | to represent paths with placeholders.
|
---|
58 |
|
---|
59 | /user/show/:id -> $c->render(text => $users{$id});
|
---|
60 |
|
---|
61 | The only difference between a static path and the route above is the C<:id>
|
---|
62 | placeholder. One or more placeholders can be anywhere in the route.
|
---|
63 |
|
---|
64 | /user/:action/:id
|
---|
65 |
|
---|
66 | A fundamental concept of the L<Mojolicious> router is that extracted
|
---|
67 | placeholder values are turned into a hash.
|
---|
68 |
|
---|
69 | /user/show/23 -> /user/:action/:id -> {action => 'show', id => 23}
|
---|
70 |
|
---|
71 | This hash is basically the center of every L<Mojolicious> application, you will
|
---|
72 | learn more about this later on. Internally, routes get compiled to regular
|
---|
73 | expressions, so you can get the best of both worlds with a little bit of
|
---|
74 | experience.
|
---|
75 |
|
---|
76 | /user/show/:id -> qr/(?-xism:^\/user\/show\/([^\/.]+))/
|
---|
77 |
|
---|
78 | A trailing slash in the path is always optional.
|
---|
79 |
|
---|
80 | /user/show/23/ -> /user/:action/:id -> {action => 'show', id => 23}
|
---|
81 |
|
---|
82 | =head2 Reversibility
|
---|
83 |
|
---|
84 | One more huge advantage routes have over regular expressions is that they are
|
---|
85 | easily reversible, extracted placeholders can be turned back into a path at any
|
---|
86 | time.
|
---|
87 |
|
---|
88 | /sebastian -> /:name -> {name => 'sebastian'}
|
---|
89 | {name => 'sebastian'} -> /:name -> /sebastian
|
---|
90 |
|
---|
91 | Every placeholder has a name, even if it's just an empty string.
|
---|
92 |
|
---|
93 | =head2 Standard placeholders
|
---|
94 |
|
---|
95 | Standard placeholders are the simplest form of placeholders, they use a colon
|
---|
96 | prefix and match all characters except C</> and C<.>, similar to the regular
|
---|
97 | expression C<([^/.]+)>.
|
---|
98 |
|
---|
99 | /hello -> /:name/hello -> undef
|
---|
100 | /sebastian/23/hello -> /:name/hello -> undef
|
---|
101 | /sebastian.23/hello -> /:name/hello -> undef
|
---|
102 | /sebastian/hello -> /:name/hello -> {name => 'sebastian'}
|
---|
103 | /sebastian23/hello -> /:name/hello -> {name => 'sebastian23'}
|
---|
104 | /sebastian 23/hello -> /:name/hello -> {name => 'sebastian 23'}
|
---|
105 |
|
---|
106 | All placeholders can be surrounded by C<E<lt>> and C<E<gt>> to separate them
|
---|
107 | from the surrounding text.
|
---|
108 |
|
---|
109 | /hello -> /<:name>hello -> undef
|
---|
110 | /sebastian/23hello -> /<:name>hello -> undef
|
---|
111 | /sebastian.23hello -> /<:name>hello -> undef
|
---|
112 | /sebastianhello -> /<:name>hello -> {name => 'sebastian'}
|
---|
113 | /sebastian23hello -> /<:name>hello -> {name => 'sebastian23'}
|
---|
114 | /sebastian 23hello -> /<:name>hello -> {name => 'sebastian 23'}
|
---|
115 |
|
---|
116 | The colon prefix is optional for standard placeholders that are surrounded by
|
---|
117 | C<E<lt>> and C<E<gt>>.
|
---|
118 |
|
---|
119 | /iâ¥mojolicious -> /<one>â¥<two> -> {one => 'i', two => 'mojolicious'}
|
---|
120 |
|
---|
121 | =head2 Relaxed placeholders
|
---|
122 |
|
---|
123 | Relaxed placeholders are just like standard placeholders, but use a hash prefix
|
---|
124 | and match all characters except C</>, similar to the regular expression
|
---|
125 | C<([^/]+)>.
|
---|
126 |
|
---|
127 | /hello -> /#name/hello -> undef
|
---|
128 | /sebastian/23/hello -> /#name/hello -> undef
|
---|
129 | /sebastian.23/hello -> /#name/hello -> {name => 'sebastian.23'}
|
---|
130 | /sebastian/hello -> /#name/hello -> {name => 'sebastian'}
|
---|
131 | /sebastian23/hello -> /#name/hello -> {name => 'sebastian23'}
|
---|
132 | /sebastian 23/hello -> /#name/hello -> {name => 'sebastian 23'}
|
---|
133 |
|
---|
134 | They can be especially useful for manually matching file names with extensions,
|
---|
135 | rather than using L<format detection|/"Formats">.
|
---|
136 |
|
---|
137 | /music/song.mp3 -> /music/#filename -> {filename => 'song.mp3'}
|
---|
138 |
|
---|
139 | =head2 Wildcard placeholders
|
---|
140 |
|
---|
141 | Wildcard placeholders are just like the two types of placeholders above, but use
|
---|
142 | an asterisk prefix and match absolutely everything, including C</> and C<.>,
|
---|
143 | similar to the regular expression C<(.+)>.
|
---|
144 |
|
---|
145 | /hello -> /*name/hello -> undef
|
---|
146 | /sebastian/23/hello -> /*name/hello -> {name => 'sebastian/23'}
|
---|
147 | /sebastian.23/hello -> /*name/hello -> {name => 'sebastian.23'}
|
---|
148 | /sebastian/hello -> /*name/hello -> {name => 'sebastian'}
|
---|
149 | /sebastian23/hello -> /*name/hello -> {name => 'sebastian23'}
|
---|
150 | /sebastian 23/hello -> /*name/hello -> {name => 'sebastian 23'}
|
---|
151 |
|
---|
152 | They can be useful for manually matching entire file paths.
|
---|
153 |
|
---|
154 | /music/rock/song.mp3 -> /music/*filepath -> {filepath => 'rock/song.mp3'}
|
---|
155 |
|
---|
156 | =head1 BASICS
|
---|
157 |
|
---|
158 | Most commonly used features every L<Mojolicious> developer should know about.
|
---|
159 |
|
---|
160 | =head2 Minimal route
|
---|
161 |
|
---|
162 | The attribute L<Mojolicious/"routes"> contains a router you can use to generate
|
---|
163 | route structures.
|
---|
164 |
|
---|
165 | # Application
|
---|
166 | package MyApp;
|
---|
167 | use Mojo::Base 'Mojolicious';
|
---|
168 |
|
---|
169 | sub startup {
|
---|
170 | my $self = shift;
|
---|
171 |
|
---|
172 | # Router
|
---|
173 | my $r = $self->routes;
|
---|
174 |
|
---|
175 | # Route
|
---|
176 | $r->get('/welcome')->to(controller => 'foo', action => 'welcome');
|
---|
177 | }
|
---|
178 |
|
---|
179 | 1;
|
---|
180 |
|
---|
181 | The minimal route above will load and instantiate the class
|
---|
182 | C<MyApp::Controller::Foo> and call its C<welcome> method. Routes are usually
|
---|
183 | configured in the C<startup> method of the application class, but the router can
|
---|
184 | be accessed from everywhere (even at runtime).
|
---|
185 |
|
---|
186 | # Controller
|
---|
187 | package MyApp::Controller::Foo;
|
---|
188 | use Mojo::Base 'Mojolicious::Controller';
|
---|
189 |
|
---|
190 | # Action
|
---|
191 | sub welcome {
|
---|
192 | my $self = shift;
|
---|
193 |
|
---|
194 | # Render response
|
---|
195 | $self->render(text => 'Hello there.');
|
---|
196 | }
|
---|
197 |
|
---|
198 | 1;
|
---|
199 |
|
---|
200 | All routes match in the same order in which they were defined, and matching
|
---|
201 | stops as soon as a suitable route has been found. So you can improve the routing
|
---|
202 | performance by declaring your most frequently accessed routes first. A routing
|
---|
203 | cache will also be used automatically to handle sudden traffic spikes more
|
---|
204 | gracefully.
|
---|
205 |
|
---|
206 | =head2 Routing destination
|
---|
207 |
|
---|
208 | After you start a new route with methods like
|
---|
209 | L<Mojolicious::Routes::Route/"get">, you can also give it a destination in the
|
---|
210 | form of a hash using the chained method L<Mojolicious::Routes::Route/"to">.
|
---|
211 |
|
---|
212 | # /welcome -> {controller => 'foo', action => 'welcome'}
|
---|
213 | $r->get('/welcome')->to(controller => 'foo', action => 'welcome');
|
---|
214 |
|
---|
215 | Now if the route matches an incoming request it will use the content of this
|
---|
216 | hash to try and find appropriate code to generate a response.
|
---|
217 |
|
---|
218 | =head2 HTTP methods
|
---|
219 |
|
---|
220 | There are already shortcuts for the most common HTTP request methods like
|
---|
221 | L<Mojolicious::Routes::Route/"post">, and for more control
|
---|
222 | L<Mojolicious::Routes::Route/"any"> accepts an optional array reference with
|
---|
223 | arbitrary request methods as first argument.
|
---|
224 |
|
---|
225 | # PUT /hello -> undef
|
---|
226 | # GET /hello -> {controller => 'foo', action => 'hello'}
|
---|
227 | $r->get('/hello')->to(controller => 'foo', action => 'hello');
|
---|
228 |
|
---|
229 | # PUT /hello -> {controller => 'foo', action => 'hello'}
|
---|
230 | $r->put('/hello')->to(controller => 'foo', action => 'hello');
|
---|
231 |
|
---|
232 | # POST /hello -> {controller => 'foo', action => 'hello'}
|
---|
233 | $r->post('/hello')->to(controller => 'foo', action => 'hello');
|
---|
234 |
|
---|
235 | # GET|POST /bye -> {controller => 'foo', action => 'bye'}
|
---|
236 | $r->any(['GET', 'POST'] => '/bye')->to(controller => 'foo', action => 'bye');
|
---|
237 |
|
---|
238 | # * /whatever -> {controller => 'foo', action => 'whatever'}
|
---|
239 | $r->any('/whatever')->to(controller => 'foo', action => 'whatever');
|
---|
240 |
|
---|
241 | There is one small exception, C<HEAD> requests are considered equal to C<GET>,
|
---|
242 | but content will not be sent with the response even if it is present.
|
---|
243 |
|
---|
244 | # GET /test -> {controller => 'bar', action => 'test'}
|
---|
245 | # HEAD /test -> {controller => 'bar', action => 'test'}
|
---|
246 | $r->get('/test')->to(controller => 'bar', action => 'test');
|
---|
247 |
|
---|
248 | You can also use the C<_method> query parameter to override the request method.
|
---|
249 | This can be very useful when submitting forms with browsers that only support
|
---|
250 | C<GET> and C<POST>.
|
---|
251 |
|
---|
252 | # PUT /stuff -> {controller => 'baz', action => 'stuff'}
|
---|
253 | # POST /stuff?_method=PUT -> {controller => 'baz', action => 'stuff'}
|
---|
254 | $r->put('/stuff')->to(controller => 'baz', action => 'stuff');
|
---|
255 |
|
---|
256 | =head2 IRIs
|
---|
257 |
|
---|
258 | IRIs are handled transparently, that means paths are guaranteed to be unescaped
|
---|
259 | and decoded from bytes to characters.
|
---|
260 |
|
---|
261 | # GET /â (Unicode snowman) -> {controller => 'foo', action => 'snowman'}
|
---|
262 | $r->get('/â')->to(controller => 'foo', action => 'snowman');
|
---|
263 |
|
---|
264 | =head2 Stash
|
---|
265 |
|
---|
266 | The generated hash of a matching route is actually the center of the whole
|
---|
267 | L<Mojolicious> request cycle. We call it the stash, and it persists until a
|
---|
268 | response has been generated.
|
---|
269 |
|
---|
270 | # /bye -> {controller => 'foo', action => 'bye', mymessage => 'Bye'}
|
---|
271 | $r->get('/bye')
|
---|
272 | ->to(controller => 'foo', action => 'bye', mymessage => 'Bye');
|
---|
273 |
|
---|
274 | There are a few stash values with special meaning, such as C<controller> and
|
---|
275 | C<action>, but you can generally fill it with whatever data you need to
|
---|
276 | generate a response. Once dispatched the whole stash content can be changed at
|
---|
277 | any time.
|
---|
278 |
|
---|
279 | sub bye {
|
---|
280 | my $self = shift;
|
---|
281 |
|
---|
282 | # Get message from stash
|
---|
283 | my $msg = $self->stash('mymessage');
|
---|
284 |
|
---|
285 | # Change message in stash
|
---|
286 | $self->stash(mymessage => 'Welcome');
|
---|
287 | }
|
---|
288 |
|
---|
289 | For a full list of reserved stash values see
|
---|
290 | L<Mojolicious::Controller/"stash">.
|
---|
291 |
|
---|
292 | =head2 Nested routes
|
---|
293 |
|
---|
294 | It is also possible to build tree structures from routes to remove repetitive
|
---|
295 | code. A route with children can't match on its own though, only the actual
|
---|
296 | endpoints of these nested routes can.
|
---|
297 |
|
---|
298 | # /foo -> undef
|
---|
299 | # /foo/bar -> {controller => 'foo', action => 'bar'}
|
---|
300 | my $foo = $r->any('/foo')->to(controller => 'foo');
|
---|
301 | $foo->get('/bar')->to(action => 'bar');
|
---|
302 |
|
---|
303 | The stash is simply inherited from route to route and newer values override old
|
---|
304 | ones.
|
---|
305 |
|
---|
306 | # /cats -> {controller => 'cats', action => 'index'}
|
---|
307 | # /cats/nyan -> {controller => 'cats', action => 'nyan'}
|
---|
308 | # /cats/lol -> {controller => 'cats', action => 'default'}
|
---|
309 | my $cats = $r->any('/cats')->to(controller => 'cats', action => 'default');
|
---|
310 | $cats->get('/')->to(action => 'index');
|
---|
311 | $cats->get('/nyan')->to(action => 'nyan');
|
---|
312 | $cats->get('/lol');
|
---|
313 |
|
---|
314 | With a few common prefixes you can also greatly improve the routing performance
|
---|
315 | of applications with many routes, because children are only tried if the prefix
|
---|
316 | matched first.
|
---|
317 |
|
---|
318 | =head2 Special stash values
|
---|
319 |
|
---|
320 | When the dispatcher sees C<controller> and C<action> values in the stash it
|
---|
321 | will always try to turn them into a class and method to dispatch to. The
|
---|
322 | C<controller> value gets converted from C<snake_case> to C<CamelCase> using
|
---|
323 | L<Mojo::Util/"camelize"> and appended to one or more namespaces, defaulting to
|
---|
324 | a controller namespace based on the application class (C<MyApp::Controller>),
|
---|
325 | as well as the bare application class (C<MyApp>), and these namespaces are
|
---|
326 | searched in that order. The action value is not changed at all, so both values
|
---|
327 | are case-sensitive.
|
---|
328 |
|
---|
329 | # Application
|
---|
330 | package MyApp;
|
---|
331 | use Mojo::Base 'Mojolicious';
|
---|
332 |
|
---|
333 | sub startup {
|
---|
334 | my $self = shift;
|
---|
335 |
|
---|
336 | # /bye -> MyApp::Controller::Foo->bye
|
---|
337 | $self->routes->get('/bye')->to(controller => 'foo', action => 'bye');
|
---|
338 | }
|
---|
339 |
|
---|
340 | 1;
|
---|
341 |
|
---|
342 | # Controller
|
---|
343 | package MyApp::Controller::Foo;
|
---|
344 | use Mojo::Base 'Mojolicious::Controller';
|
---|
345 |
|
---|
346 | # Action
|
---|
347 | sub bye {
|
---|
348 | my $self = shift;
|
---|
349 |
|
---|
350 | # Render response
|
---|
351 | $self->render(text => 'Good bye.');
|
---|
352 | }
|
---|
353 |
|
---|
354 | 1;
|
---|
355 |
|
---|
356 | Controller classes are perfect for organizing code in larger projects. There
|
---|
357 | are more dispatch strategies, but because controllers are the most commonly
|
---|
358 | used ones they also got a special shortcut in the form of C<controller#action>.
|
---|
359 |
|
---|
360 | # /bye -> {controller => 'foo', action => 'bye', mymessage => 'Bye'}
|
---|
361 | $r->get('/bye')->to('foo#bye', mymessage => 'Bye');
|
---|
362 |
|
---|
363 | During camelization C<-> characters get replaced with C<::>, this allows
|
---|
364 | multi-level C<controller> hierarchies.
|
---|
365 |
|
---|
366 | # / -> MyApp::Controller::Foo::Bar->hi
|
---|
367 | $r->get('/')->to('foo-bar#hi');
|
---|
368 |
|
---|
369 | You can also just specify the C<controller> in CamelCase form instead of
|
---|
370 | snake_case.
|
---|
371 |
|
---|
372 | # / -> MyApp::Controller::Foo::Bar->hi
|
---|
373 | $r->get('/')->to('Foo::Bar#hi');
|
---|
374 |
|
---|
375 | For security reasons the dispatcher will always check if the C<controller> is
|
---|
376 | actually a subclass of L<Mojolicious::Controller> or L<Mojo> before dispatching
|
---|
377 | to it.
|
---|
378 |
|
---|
379 | =head2 Namespaces
|
---|
380 |
|
---|
381 | You can use the C<namespace> stash value to change the namespace of a whole
|
---|
382 | route with all its children.
|
---|
383 |
|
---|
384 | # /bye -> MyApp::MyController::Foo::Bar->bye
|
---|
385 | $r->get('/bye')
|
---|
386 | ->to(namespace => 'MyApp::MyController::Foo::Bar', action => 'bye');
|
---|
387 |
|
---|
388 | The C<controller> is always converted from C<snake_case> to C<CamelCase> with
|
---|
389 | L<Mojo::Util/"camelize">, and then appended to this C<namespace>.
|
---|
390 |
|
---|
391 | # /bye -> MyApp::MyController::Foo::Bar->bye
|
---|
392 | $r->get('/bye')->to('foo-bar#bye', namespace => 'MyApp::MyController');
|
---|
393 |
|
---|
394 | # /hey -> MyApp::MyController::Foo::Bar->hey
|
---|
395 | $r->get('/hey')->to('Foo::Bar#hey', namespace => 'MyApp::MyController');
|
---|
396 |
|
---|
397 | You can also change the default namespaces for all routes in the application
|
---|
398 | with the router attribute L<Mojolicious::Routes/"namespaces">, which usually
|
---|
399 | defaults to a namespace based on the application class (C<MyApp::Controller>),
|
---|
400 | as well as the bare application class (C<MyApp>).
|
---|
401 |
|
---|
402 | $r->namespaces(['MyApp::MyController']);
|
---|
403 |
|
---|
404 | =head2 Route to callback
|
---|
405 |
|
---|
406 | The C<cb> stash value, which won't be inherited by nested routes, can be used
|
---|
407 | to bypass controllers and execute a callback instead.
|
---|
408 |
|
---|
409 | $r->get('/bye')->to(cb => sub {
|
---|
410 | my $c = shift;
|
---|
411 | $c->render(text => 'Good bye.');
|
---|
412 | });
|
---|
413 |
|
---|
414 | But just like in L<Mojolicious::Lite> you can also pass the callback directly,
|
---|
415 | which usually looks much better.
|
---|
416 |
|
---|
417 | $r->get('/bye' => sub {
|
---|
418 | my $c = shift;
|
---|
419 | $c->render(text => 'Good bye.');
|
---|
420 | });
|
---|
421 |
|
---|
422 | =head2 Named routes
|
---|
423 |
|
---|
424 | Naming your routes will allow backreferencing in many methods and helpers
|
---|
425 | throughout the whole framework, most of which internally rely on
|
---|
426 | L<Mojolicious::Controller/"url_for"> for this.
|
---|
427 |
|
---|
428 | # /foo/marcus -> {controller => 'foo', action => 'bar', user => 'marcus'}
|
---|
429 | $r->get('/foo/:user')->to('foo#bar')->name('baz');
|
---|
430 |
|
---|
431 | # Generate URL "/foo/marcus" for route "baz"
|
---|
432 | my $url = $c->url_for('baz');
|
---|
433 |
|
---|
434 | # Generate URL "/foo/jan" for route "baz"
|
---|
435 | my $url = $c->url_for('baz', user => 'jan');
|
---|
436 |
|
---|
437 | # Generate URL "http://127.0.0.1:3000/foo/jan" for route "baz"
|
---|
438 | my $url = $c->url_for('baz', user => 'jan')->to_abs;
|
---|
439 |
|
---|
440 | You can assign a name with L<Mojolicious::Routes::Route/"name">, or let the
|
---|
441 | router generate one automatically, which would be equal to the route itself
|
---|
442 | without non-word characters, custom names have a higher precedence though.
|
---|
443 |
|
---|
444 | # /foo/bar ("foobar")
|
---|
445 | $r->get('/foo/bar')->to('test#stuff');
|
---|
446 |
|
---|
447 | # Generate URL "/foo/bar"
|
---|
448 | my $url = $c->url_for('foobar');
|
---|
449 |
|
---|
450 | To refer to the current route you can use the reserved name C<current> or no
|
---|
451 | name at all.
|
---|
452 |
|
---|
453 | # Generate URL for current route
|
---|
454 | my $url = $c->url_for('current');
|
---|
455 | my $url = $c->url_for;
|
---|
456 |
|
---|
457 | To check or get the name of the current route you can use the helper
|
---|
458 | L<Mojolicious::Plugin::DefaultHelpers/"current_route">.
|
---|
459 |
|
---|
460 | # Name for current route
|
---|
461 | my $name = $c->current_route;
|
---|
462 |
|
---|
463 | # Check route name in code shared by multiple routes
|
---|
464 | $c->stash(button => 'green') if $c->current_route('login');
|
---|
465 |
|
---|
466 | =head2 Optional placeholders
|
---|
467 |
|
---|
468 | Extracted placeholder values will simply redefine older stash values if they
|
---|
469 | already exist.
|
---|
470 |
|
---|
471 | # /bye -> {controller => 'foo', action => 'bar', mymessage => 'bye'}
|
---|
472 | # /hey -> {controller => 'foo', action => 'bar', mymessage => 'hey'}
|
---|
473 | $r->get('/:mymessage')->to('foo#bar', mymessage => 'hi');
|
---|
474 |
|
---|
475 | One more interesting effect, a placeholder automatically becomes optional if
|
---|
476 | there is already a stash value of the same name present, this works similar to
|
---|
477 | the regular expression C<([^/.]+)?>.
|
---|
478 |
|
---|
479 | # / -> {controller => 'foo', action => 'bar', mymessage => 'hi'}
|
---|
480 | $r->get('/:mymessage')->to('foo#bar', mymessage => 'hi');
|
---|
481 |
|
---|
482 | # /test/123 -> {controller => 'foo', action => 'bar', mymessage => 'hi'}
|
---|
483 | # /test/bye/123 -> {controller => 'foo', action => 'bar', mymessage => 'bye'}
|
---|
484 | $r->get('/test/:mymessage/123')->to('foo#bar', mymessage => 'hi');
|
---|
485 |
|
---|
486 | And if two optional placeholders are only separated by a slash, that slash can
|
---|
487 | become optional as well.
|
---|
488 |
|
---|
489 | # / -> {controller => 'foo', action => 'bar'}
|
---|
490 | # /users -> {controller => 'users', action => 'bar'}
|
---|
491 | # /users/list -> {controller => 'users', action => 'list'}
|
---|
492 | $r->get('/:controller/:action')->to('foo#bar');
|
---|
493 |
|
---|
494 | Special stash values like C<controller> and C<action> can also be placeholders,
|
---|
495 | which is very convenient especially during development, but should only be used
|
---|
496 | very carefully, because every controller method becomes a potential route. All
|
---|
497 | uppercase methods as well as those starting with an underscore are
|
---|
498 | automatically hidden from the router and you can use
|
---|
499 | L<Mojolicious::Routes/"hide"> to add additional ones.
|
---|
500 |
|
---|
501 | # Hide "create" method in all controllers
|
---|
502 | $r->hide('create');
|
---|
503 |
|
---|
504 | This has already been done for all attributes and methods from
|
---|
505 | L<Mojolicious::Controller>.
|
---|
506 |
|
---|
507 | =head2 Restrictive placeholders
|
---|
508 |
|
---|
509 | A very easy way to make placeholders more restrictive are alternatives, you
|
---|
510 | just make a list of possible values, which then work similar to the regular
|
---|
511 | expression C<(bender|leela)>.
|
---|
512 |
|
---|
513 | # /fry -> undef
|
---|
514 | # /bender -> {controller => 'foo', action => 'bar', name => 'bender'}
|
---|
515 | # /leela -> {controller => 'foo', action => 'bar', name => 'leela'}
|
---|
516 | $r->get('/:name' => [name => ['bender', 'leela']])->to('foo#bar');
|
---|
517 |
|
---|
518 | You can also adjust the regular expressions behind placeholders directly, just
|
---|
519 | make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
|
---|
520 | placeholders become part of a larger regular expression internally,
|
---|
521 | non-capturing groups C<(?:...)> are fine though.
|
---|
522 |
|
---|
523 | # /23 -> {controller => 'foo', action => 'bar', number => 23}
|
---|
524 | # /test -> undef
|
---|
525 | $r->get('/:number' => [number => qr/\d+/])->to('foo#bar');
|
---|
526 |
|
---|
527 | # /23 -> undef
|
---|
528 | # /test -> {controller => 'foo', action => 'bar', name => 'test'}
|
---|
529 | $r->get('/:name' => [name => qr/[a-zA-Z]+/])->to('foo#bar');
|
---|
530 |
|
---|
531 | This way you get easily readable routes and the raw power of regular
|
---|
532 | expressions.
|
---|
533 |
|
---|
534 | =head2 Placeholder types
|
---|
535 |
|
---|
536 | And if you have multiple routes using restrictive placeholders you can also turn
|
---|
537 | them into placeholder types with L<Mojolicious::Routes/"add_type">.
|
---|
538 |
|
---|
539 | # A type with alternatives
|
---|
540 | $r->add_type(futurama_name => ['bender', 'leela']);
|
---|
541 |
|
---|
542 | # /fry -> undef
|
---|
543 | # /bender -> {controller => 'foo', action => 'bar', name => 'bender'}
|
---|
544 | # /leela -> {controller => 'foo', action => 'bar', name => 'leela'}
|
---|
545 | $r->get('/<name:futurama_name>')->to('foo#bar');
|
---|
546 |
|
---|
547 | Placeholder types work just like restrictive placeholders, they are just
|
---|
548 | reusable with the C<E<lt>placeholder:typeE<gt>> notation.
|
---|
549 |
|
---|
550 | # A type adjusting the regular expression
|
---|
551 | $r->add_type(upper => qr/[A-Z]+/);
|
---|
552 |
|
---|
553 | # /user/ROOT -> {controller => 'users', action => 'show', name => 'ROOT'}
|
---|
554 | # /user/root -> undef
|
---|
555 | # /user/23 -> undef
|
---|
556 | $r->get('/user/<name:upper>')->to('users#show');
|
---|
557 |
|
---|
558 | Some types like C<num> are used so commonly that they are available by default.
|
---|
559 |
|
---|
560 | # /article/12 -> {controller => 'article', action => 'show', id => 12}
|
---|
561 | # /article/test -> undef
|
---|
562 | $r->get('/article/<id:num>')->to('articles#show');
|
---|
563 |
|
---|
564 | For a full list of available placeholder types see also
|
---|
565 | L<Mojolicious::Routes/"TYPES">.
|
---|
566 |
|
---|
567 | =head2 Introspection
|
---|
568 |
|
---|
569 | The command L<Mojolicious::Command::routes> can be used from the command line
|
---|
570 | to list all available routes together with names and underlying regular
|
---|
571 | expressions.
|
---|
572 |
|
---|
573 | $ ./myapp.pl routes -v
|
---|
574 | /foo/:name .... POST fooname ^/foo/([^/.]+)/?(?:\.([^/]+))?$
|
---|
575 | /bar ..U. * bar ^/bar
|
---|
576 | +/baz ...W GET baz ^/baz/?(?:\.([^/]+))?$
|
---|
577 | /yada .... * yada ^/yada/?(?:\.([^/]+))?$
|
---|
578 |
|
---|
579 | =head2 Under
|
---|
580 |
|
---|
581 | To share code with multiple nested routes you can use
|
---|
582 | L<Mojolicious::Routes::Route/"under">, because unlike normal nested routes, the
|
---|
583 | routes generated with it have their own intermediate destination and result in
|
---|
584 | additional dispatch cycles when they match.
|
---|
585 |
|
---|
586 | # /foo -> undef
|
---|
587 | # /foo/bar -> {controller => 'foo', action => 'baz'}
|
---|
588 | # {controller => 'foo', action => 'bar'}
|
---|
589 | my $foo = $r->under('/foo')->to('foo#baz');
|
---|
590 | $foo->get('/bar')->to('#bar');
|
---|
591 |
|
---|
592 | The actual action code for this destination needs to return a true value or the
|
---|
593 | dispatch chain will be broken, this can be a very powerful tool for
|
---|
594 | authentication.
|
---|
595 |
|
---|
596 | # /blackjack -> {cb => sub {...}}
|
---|
597 | # {controller => 'hideout', action => 'blackjack'}
|
---|
598 | my $auth = $r->under('/' => sub {
|
---|
599 | my $c = shift;
|
---|
600 |
|
---|
601 | # Authenticated
|
---|
602 | return 1 if $c->req->headers->header('X-Bender');
|
---|
603 |
|
---|
604 | # Not authenticated
|
---|
605 | $c->render(text => "You're not Bender.", status => 401);
|
---|
606 | return undef;
|
---|
607 | });
|
---|
608 | $auth->get('/blackjack')->to('hideout#blackjack');
|
---|
609 |
|
---|
610 | Broken dispatch chains can be continued by calling the method
|
---|
611 | L<Mojolicious::Controller/"continue">, this allows for example, non-blocking
|
---|
612 | operations to finish before reaching the next dispatch cycle.
|
---|
613 |
|
---|
614 | my $maybe = $r->under('/maybe' => sub {
|
---|
615 | my $c = shift;
|
---|
616 |
|
---|
617 | # Wait 3 seconds and then give visitors a 50% chance to continue
|
---|
618 | Mojo::IOLoop->timer(3 => sub {
|
---|
619 |
|
---|
620 | # Loser
|
---|
621 | return $c->render(text => 'No luck.') unless int rand 2;
|
---|
622 |
|
---|
623 | # Winner
|
---|
624 | $c->continue;
|
---|
625 | });
|
---|
626 |
|
---|
627 | return undef;
|
---|
628 | });
|
---|
629 | $maybe->get('/')->to('maybe#winner');
|
---|
630 |
|
---|
631 | Every destination is just a snapshot of the stash at the time the route
|
---|
632 | matched, and only the C<format> value is shared by all of them. For a little
|
---|
633 | more power you can introspect the preceding and succeeding destinations with
|
---|
634 | L<Mojolicious::Controller/"match">.
|
---|
635 |
|
---|
636 | # Action of the fourth dispatch cycle
|
---|
637 | my $action = $c->match->stack->[3]{action};
|
---|
638 |
|
---|
639 | =head2 Formats
|
---|
640 |
|
---|
641 | File extensions like C<.html> and C<.txt> at the end of a route are
|
---|
642 | automatically detected and stored in the stash value C<format>.
|
---|
643 |
|
---|
644 | # /foo -> {controller => 'foo', action => 'bar'}
|
---|
645 | # /foo.html -> {controller => 'foo', action => 'bar', format => 'html'}
|
---|
646 | # /foo.txt -> {controller => 'foo', action => 'bar', format => 'txt'}
|
---|
647 | $r->get('/foo')->to('foo#bar');
|
---|
648 |
|
---|
649 | This for example, allows multiple templates in different formats to share the
|
---|
650 | same action code. Restrictive placeholders can also be used to limit the
|
---|
651 | allowed formats.
|
---|
652 |
|
---|
653 | # /foo.txt -> undef
|
---|
654 | # /foo.rss -> {controller => 'foo', action => 'bar', format => 'rss'}
|
---|
655 | # /foo.xml -> {controller => 'foo', action => 'bar', format => 'xml'}
|
---|
656 | $r->get('/foo' => [format => ['rss', 'xml']])->to('foo#bar');
|
---|
657 |
|
---|
658 | A C<format> value can also be passed to L<Mojolicious::Controller/"url_for">.
|
---|
659 |
|
---|
660 | # /foo/bar.txt -> {controller => 'foo', action => 'bar', format => 'txt'}
|
---|
661 | $r->get('/foo/:action')->to('foo#')->name('baz');
|
---|
662 |
|
---|
663 | # Generate URL "/foo/bar.txt" for route "baz"
|
---|
664 | my $url = $c->url_for('baz', action => 'bar', format => 'txt');
|
---|
665 |
|
---|
666 | Or you can just disable format detection with a special type of restrictive
|
---|
667 | placeholder, which gets inherited by nested routes, and then re-enable it on
|
---|
668 | demand.
|
---|
669 |
|
---|
670 | # /foo -> {controller => 'foo', action => 'bar'}
|
---|
671 | # /foo.html -> undef
|
---|
672 | $r->get('/foo' => [format => 0])->to('foo#bar');
|
---|
673 |
|
---|
674 | # /foo -> {controller => 'foo', action => 'bar'}
|
---|
675 | # /foo.html -> undef
|
---|
676 | # /baz -> undef
|
---|
677 | # /baz.txt -> {controller => 'baz', action => 'yada', format => 'txt'}
|
---|
678 | # /baz.html -> {controller => 'baz', action => 'yada', format => 'html'}
|
---|
679 | # /baz.xml -> undef
|
---|
680 | my $inactive = $r->under([format => 0]);
|
---|
681 | $inactive->get('/foo')->to('foo#bar');
|
---|
682 | $inactive->get('/baz' => [format => ['txt', 'html']])->to('baz#yada');
|
---|
683 |
|
---|
684 | =head2 WebSockets
|
---|
685 |
|
---|
686 | With the method L<Mojolicious::Routes::Route/"websocket"> you can restrict
|
---|
687 | access to WebSocket handshakes, which are normal C<GET> requests with some
|
---|
688 | additional information.
|
---|
689 |
|
---|
690 | # /echo (WebSocket handshake)
|
---|
691 | $r->websocket('/echo')->to('foo#echo');
|
---|
692 |
|
---|
693 | # Controller
|
---|
694 | package MyApp::Controller::Foo;
|
---|
695 | use Mojo::Base 'Mojolicious::Controller';
|
---|
696 |
|
---|
697 | # Action
|
---|
698 | sub echo {
|
---|
699 | my $self = shift;
|
---|
700 | $self->on(message => sub {
|
---|
701 | my ($self, $msg) = @_;
|
---|
702 | $self->send("echo: $msg");
|
---|
703 | });
|
---|
704 | }
|
---|
705 |
|
---|
706 | 1;
|
---|
707 |
|
---|
708 | The connection gets established when you respond to the WebSocket handshake
|
---|
709 | request with a C<101> response status, which happens automatically if you
|
---|
710 | subscribe to an event with L<Mojolicious::Controller/"on"> or send a message
|
---|
711 | with L<Mojolicious::Controller/"send"> right away.
|
---|
712 |
|
---|
713 | GET /echo HTTP/1.1
|
---|
714 | Host: mojolicious.org
|
---|
715 | User-Agent: Mojolicious (Perl)
|
---|
716 | Connection: Upgrade
|
---|
717 | Upgrade: websocket
|
---|
718 | Sec-WebSocket-Key: IDM3ODE4NDk2MjA1OTcxOQ==
|
---|
719 | Sec-WebSocket-Version: 13
|
---|
720 |
|
---|
721 | HTTP/1.1 101 Switching Protocols
|
---|
722 | Server: Mojolicious (Perl)
|
---|
723 | Date: Tue, 03 Feb 2015 17:08:24 GMT
|
---|
724 | Connection: Upgrade
|
---|
725 | Upgrade: websocket
|
---|
726 | Sec-WebSocket-Accept: SWsp5N2iNxPbHlcOTIw8ERvyVPY=
|
---|
727 |
|
---|
728 | =head2 Catch-all route
|
---|
729 |
|
---|
730 | Since routes match in the order in which they were defined, you can catch all
|
---|
731 | requests that did not match in your last route with an optional wildcard
|
---|
732 | placeholder.
|
---|
733 |
|
---|
734 | # * /*
|
---|
735 | $r->any('/*whatever' => {whatever => ''} => sub {
|
---|
736 | my $c = shift;
|
---|
737 | my $whatever = $c->param('whatever');
|
---|
738 | $c->render(text => "/$whatever did not match.", status => 404);
|
---|
739 | });
|
---|
740 |
|
---|
741 | =head2 Conditions
|
---|
742 |
|
---|
743 | Conditions such as C<headers>, C<agent> and C<host> from
|
---|
744 | L<Mojolicious::Plugin::HeaderCondition> can be applied to any route with the
|
---|
745 | method L<Mojolicious::Routes::Route/"over">, and allow even more powerful route
|
---|
746 | constructs.
|
---|
747 |
|
---|
748 | # / (Origin: http://perl.org)
|
---|
749 | $r->get('/')->over(headers => {Origin => qr/perl\.org/})->to('foo#bar');
|
---|
750 |
|
---|
751 | # / (Firefox)
|
---|
752 | $r->get('/')->over(agent => qr/Firefox/)->to('browser-test#firefox');
|
---|
753 |
|
---|
754 | # / (Internet Explorer)
|
---|
755 | $r->get('/')->over(agent => qr/Internet Explorer/)->to('browser-test#ie');
|
---|
756 |
|
---|
757 | # http://mojolicious.org/perldoc
|
---|
758 | $r->get('/perldoc')->over(host => 'mojolicious.org')->to('perldoc#index');
|
---|
759 |
|
---|
760 | Just be aware that conditions are too complex for the routing cache, which
|
---|
761 | normally speeds up recurring requests, and can therefore reduce performance.
|
---|
762 |
|
---|
763 | =head2 Hooks
|
---|
764 |
|
---|
765 | Hooks operate outside the routing system and allow you to extend the framework
|
---|
766 | itself by sharing code with all requests indiscriminately through
|
---|
767 | L<Mojolicious/"hook">, which makes them a very powerful tool especially for
|
---|
768 | plugins.
|
---|
769 |
|
---|
770 | # Application
|
---|
771 | package MyApp;
|
---|
772 | use Mojo::Base 'Mojolicious';
|
---|
773 |
|
---|
774 | sub startup {
|
---|
775 | my $self = shift;
|
---|
776 |
|
---|
777 | # Check all requests for a "/test" prefix
|
---|
778 | $self->hook(before_dispatch => sub {
|
---|
779 | my $c = shift;
|
---|
780 | $c->render(text => 'This request did not reach the router.')
|
---|
781 | if $c->req->url->path->contains('/test');
|
---|
782 | });
|
---|
783 |
|
---|
784 | # These will not be reached if the hook above renders a response
|
---|
785 | my $r = $self->routes;
|
---|
786 | $r->get('/welcome')->to('foo#welcome');
|
---|
787 | $r->post('/bye')->to('foo#bye');
|
---|
788 | }
|
---|
789 |
|
---|
790 | 1;
|
---|
791 |
|
---|
792 | Post-processing the response to add or remove headers is a very common use.
|
---|
793 |
|
---|
794 | # Make sure static files are cached
|
---|
795 | $app->hook(after_static => sub {
|
---|
796 | my $c = shift;
|
---|
797 | $c->res->headers->cache_control('max-age=3600, must-revalidate');
|
---|
798 | });
|
---|
799 |
|
---|
800 | # Remove a default header
|
---|
801 | $app->hook(after_dispatch => sub {
|
---|
802 | my $c = shift;
|
---|
803 | $c->res->headers->remove('Server');
|
---|
804 | });
|
---|
805 |
|
---|
806 | Same for pre-processing the request.
|
---|
807 |
|
---|
808 | # Choose template variant based on request headers
|
---|
809 | $app->hook(before_dispatch => sub {
|
---|
810 | my $c = shift;
|
---|
811 | return unless my $agent = $c->req->headers->user_agent;
|
---|
812 | $c->stash(variant => 'ie') if $agent =~ /Internet Explorer/;
|
---|
813 | });
|
---|
814 |
|
---|
815 | Or more advanced extensions to add monitoring to your application.
|
---|
816 |
|
---|
817 | # Forward exceptions to a web service
|
---|
818 | $app->hook(after_dispatch => sub {
|
---|
819 | my $c = shift;
|
---|
820 | return unless my $e = $c->stash('exception');
|
---|
821 | $c->ua->post('https://example.com/bugs' => form => {exception => $e});
|
---|
822 | });
|
---|
823 |
|
---|
824 | You can even extend much of the core functionality.
|
---|
825 |
|
---|
826 | # Make controller object available to actions as $_
|
---|
827 | $app->hook(around_action => sub {
|
---|
828 | my ($next, $c, $action, $last) = @_;
|
---|
829 | local $_ = $c;
|
---|
830 | return $next->();
|
---|
831 | });
|
---|
832 |
|
---|
833 | # Pass route name as argument to actions
|
---|
834 | $app->hook(around_action => sub {
|
---|
835 | my ($next, $c, $action, $last) = @_;
|
---|
836 | return $c->$action($c->current_route);
|
---|
837 | });
|
---|
838 |
|
---|
839 | For a full list of available hooks see L<Mojolicious/"HOOKS">.
|
---|
840 |
|
---|
841 | =head1 ADVANCED
|
---|
842 |
|
---|
843 | Less commonly used and more powerful features.
|
---|
844 |
|
---|
845 | =head2 Shortcuts
|
---|
846 |
|
---|
847 | To make route generation more expressive, you can also add your own shortcuts
|
---|
848 | with L<Mojolicious::Routes/"add_shortcut">.
|
---|
849 |
|
---|
850 | # Simple "resource" shortcut
|
---|
851 | $r->add_shortcut(resource => sub {
|
---|
852 | my ($r, $name) = @_;
|
---|
853 |
|
---|
854 | # Prefix for resource
|
---|
855 | my $resource = $r->any("/$name")->to("$name#");
|
---|
856 |
|
---|
857 | # Render a list of resources
|
---|
858 | $resource->get->to('#index')->name($name);
|
---|
859 |
|
---|
860 | # Render a form to create a new resource (submitted to "store")
|
---|
861 | $resource->get('/create')->to('#create')->name("create_$name");
|
---|
862 |
|
---|
863 | # Store newly created resource (submitted by "create")
|
---|
864 | $resource->post->to('#store')->name("store_$name");
|
---|
865 |
|
---|
866 | # Render a specific resource
|
---|
867 | $resource->get('/:id')->to('#show')->name("show_$name");
|
---|
868 |
|
---|
869 | # Render a form to edit a resource (submitted to "update")
|
---|
870 | $resource->get('/:id/edit')->to('#edit')->name("edit_$name");
|
---|
871 |
|
---|
872 | # Store updated resource (submitted by "edit")
|
---|
873 | $resource->put('/:id')->to('#update')->name("update_$name");
|
---|
874 |
|
---|
875 | # Remove a resource
|
---|
876 | $resource->delete('/:id')->to('#remove')->name("remove_$name");
|
---|
877 |
|
---|
878 | return $resource;
|
---|
879 | });
|
---|
880 |
|
---|
881 | # GET /users -> {controller => 'users', action => 'index'}
|
---|
882 | # GET /users/create -> {controller => 'users', action => 'create'}
|
---|
883 | # POST /users -> {controller => 'users', action => 'store'}
|
---|
884 | # GET /users/23 -> {controller => 'users', action => 'show', id => 23}
|
---|
885 | # GET /users/23/edit -> {controller => 'users', action => 'edit', id => 23}
|
---|
886 | # PUT /users/23 -> {controller => 'users', action => 'update', id => 23}
|
---|
887 | # DELETE /users/23 -> {controller => 'users', action => 'remove', id => 23}
|
---|
888 | $r->resource('users');
|
---|
889 |
|
---|
890 | =head2 Rearranging routes
|
---|
891 |
|
---|
892 | From application startup until the first request has arrived, all routes can
|
---|
893 | still be moved around or even removed with methods like
|
---|
894 | L<Mojolicious::Routes::Route/"add_child"> and
|
---|
895 | L<Mojolicious::Routes::Route/"remove">.
|
---|
896 |
|
---|
897 | # GET /example/show -> {controller => 'example', action => 'show'}
|
---|
898 | my $show = $r->get('/show')->to('example#show');
|
---|
899 | $r->any('/example')->add_child($show);
|
---|
900 |
|
---|
901 | # Nothing
|
---|
902 | $r->get('/secrets/show')->to('secrets#show')->name('show_secrets');
|
---|
903 | $r->find('show_secrets')->remove;
|
---|
904 |
|
---|
905 | Especially for rearranging routes created by plugins this can be very useful,
|
---|
906 | to find routes by their name you can use L<Mojolicious::Routes::Route/"find">.
|
---|
907 |
|
---|
908 | # GET /example/test -> {controller => 'example', action => 'test'}
|
---|
909 | $r->get('/something/else')->to('something#else')->name('test');
|
---|
910 | my $test = $r->find('test');
|
---|
911 | $test->pattern->parse('/example/test');
|
---|
912 | $test->pattern->defaults({controller => 'example', action => 'test'});
|
---|
913 |
|
---|
914 | Even the route pattern and destination can still be changed with
|
---|
915 | L<Mojolicious::Routes::Pattern/"parse"> and
|
---|
916 | L<Mojolicious::Routes::Pattern/"defaults">.
|
---|
917 |
|
---|
918 | =head2 Adding conditions
|
---|
919 |
|
---|
920 | You can also add your own conditions with the method
|
---|
921 | L<Mojolicious::Routes/"add_condition">. All conditions are basically router
|
---|
922 | plugins that run every time a new request arrives, and which need to return a
|
---|
923 | true value for the route to match.
|
---|
924 |
|
---|
925 | # A condition that randomly allows a route to match
|
---|
926 | $r->add_condition(random => sub {
|
---|
927 | my ($route, $c, $captures, $num) = @_;
|
---|
928 |
|
---|
929 | # Loser
|
---|
930 | return undef if int rand $num;
|
---|
931 |
|
---|
932 | # Winner
|
---|
933 | return 1;
|
---|
934 | });
|
---|
935 |
|
---|
936 | # /maybe (25% chance)
|
---|
937 | $r->get('/maybe')->over(random => 4)->to('foo#bar');
|
---|
938 |
|
---|
939 | Use whatever request information you need.
|
---|
940 |
|
---|
941 | # A condition to check query parameters (useful for mock web services)
|
---|
942 | $r->add_condition(query => sub {
|
---|
943 | my ($route, $c, $captures, $hash) = @_;
|
---|
944 |
|
---|
945 | for my $key (keys %$hash) {
|
---|
946 | my $param = $c->req->url->query->param($key);
|
---|
947 | return undef unless defined $param && $param eq $hash->{$key};
|
---|
948 | }
|
---|
949 |
|
---|
950 | return 1;
|
---|
951 | });
|
---|
952 |
|
---|
953 | # /hello?to=world&test=1
|
---|
954 | $r->get('/hello')->over(query => {test => 1, to => 'world'})->to('foo#bar');
|
---|
955 |
|
---|
956 | =head2 Condition plugins
|
---|
957 |
|
---|
958 | You can also package your conditions as reusable plugins.
|
---|
959 |
|
---|
960 | # Plugin
|
---|
961 | package Mojolicious::Plugin::WerewolfCondition;
|
---|
962 | use Mojo::Base 'Mojolicious::Plugin';
|
---|
963 |
|
---|
964 | use Astro::MoonPhase;
|
---|
965 |
|
---|
966 | sub register {
|
---|
967 | my ($self, $app) = @_;
|
---|
968 |
|
---|
969 | # Add "werewolf" condition
|
---|
970 | $app->routes->add_condition(werewolf => sub {
|
---|
971 | my ($route, $c, $captures, $days) = @_;
|
---|
972 |
|
---|
973 | # Keep the werewolves out!
|
---|
974 | return undef if abs(14 - (phase(time))[2]) > ($days / 2);
|
---|
975 |
|
---|
976 | # It's ok, no werewolf
|
---|
977 | return 1;
|
---|
978 | });
|
---|
979 | }
|
---|
980 |
|
---|
981 | 1;
|
---|
982 |
|
---|
983 | Now just load the plugin and you are ready to use the condition in all your
|
---|
984 | applications.
|
---|
985 |
|
---|
986 | # Application
|
---|
987 | package MyApp;
|
---|
988 | use Mojo::Base 'Mojolicious';
|
---|
989 |
|
---|
990 | sub startup {
|
---|
991 | my $self = shift;
|
---|
992 |
|
---|
993 | # Plugin
|
---|
994 | $self->plugin('WerewolfCondition');
|
---|
995 |
|
---|
996 | # /hideout (keep them out for 4 days after full moon)
|
---|
997 | $self->routes->get('/hideout')->over(werewolf => 4)
|
---|
998 | ->to(controller => 'foo', action => 'bar');
|
---|
999 | }
|
---|
1000 |
|
---|
1001 | 1;
|
---|
1002 |
|
---|
1003 | =head2 Mount applications
|
---|
1004 |
|
---|
1005 | The easiest way to embed one application into another is
|
---|
1006 | L<Mojolicious::Plugin::Mount>, which allows you to mount whole self-contained
|
---|
1007 | applications under a domain and/or prefix.
|
---|
1008 |
|
---|
1009 | use Mojolicious::Lite;
|
---|
1010 |
|
---|
1011 | # Whole application mounted under "/prefix"
|
---|
1012 | plugin Mount => {'/prefix' => '/home/sri/myapp.pl'};
|
---|
1013 |
|
---|
1014 | # Mount application with subdomain
|
---|
1015 | plugin Mount => {'test.example.com' => '/home/sri/myapp2.pl'};
|
---|
1016 |
|
---|
1017 | # Normal route
|
---|
1018 | get '/' => sub { shift->render(text => 'Hello World!') };
|
---|
1019 |
|
---|
1020 | app->start;
|
---|
1021 |
|
---|
1022 | =head2 Embed applications
|
---|
1023 |
|
---|
1024 | For a little more power you can also embed applications by using them instead of
|
---|
1025 | a controller. This allows for example, the use of the L<Mojolicious::Lite>
|
---|
1026 | domain specific language in normal L<Mojolicious> controllers.
|
---|
1027 |
|
---|
1028 | # Controller
|
---|
1029 | package MyApp::Controller::Bar;
|
---|
1030 | use Mojolicious::Lite;
|
---|
1031 |
|
---|
1032 | # /hello
|
---|
1033 | get '/hello' => sub {
|
---|
1034 | my $c = shift;
|
---|
1035 | my $name = $c->param('name');
|
---|
1036 | $c->render(text => "Hello $name.");
|
---|
1037 | };
|
---|
1038 |
|
---|
1039 | 1;
|
---|
1040 |
|
---|
1041 | With the method L<Mojolicious::Routes::Route/"detour"> which is very similar to
|
---|
1042 | L<Mojolicious::Routes::Route/"to">, you can allow the route to partially match
|
---|
1043 | and use only the remaining path in the embedded application, the base path will
|
---|
1044 | be passed along in the C<path> stash value.
|
---|
1045 |
|
---|
1046 | # /foo/*
|
---|
1047 | $r->any('/foo')->detour('bar#', name => 'Mojo');
|
---|
1048 |
|
---|
1049 | A minimal embeddable application is nothing more than a subclass of
|
---|
1050 | L<Mojolicious>, containing a C<handler> method accepting
|
---|
1051 | L<Mojolicious::Controller> objects.
|
---|
1052 |
|
---|
1053 | package MyApp::Controller::Bar;
|
---|
1054 | use Mojo::Base 'Mojolicious';
|
---|
1055 |
|
---|
1056 | sub handler {
|
---|
1057 | my ($self, $c) = @_;
|
---|
1058 | $c->res->code(200);
|
---|
1059 | my $name = $c->param('name');
|
---|
1060 | $c->res->body("Hello $name.");
|
---|
1061 | }
|
---|
1062 |
|
---|
1063 | 1;
|
---|
1064 |
|
---|
1065 | The host application will only share very little information with the embedded
|
---|
1066 | application through the stash. So you cannot currently use route placeholders in
|
---|
1067 | routes leading to embedded applications, since that would cause problems with
|
---|
1068 | L<Mojolicious::Controller/"url_for">.
|
---|
1069 |
|
---|
1070 | =head2 Application plugins
|
---|
1071 |
|
---|
1072 | You can even package applications as self-contained reusable plugins.
|
---|
1073 |
|
---|
1074 | # Plugin
|
---|
1075 | package Mojolicious::Plugin::MyEmbeddedApp;
|
---|
1076 | use Mojo::Base 'Mojolicious::Plugin';
|
---|
1077 |
|
---|
1078 | sub register {
|
---|
1079 | my ($self, $app) = @_;
|
---|
1080 |
|
---|
1081 | # Automatically add route
|
---|
1082 | $app->routes->any('/foo')->detour(app => EmbeddedApp::app());
|
---|
1083 | }
|
---|
1084 |
|
---|
1085 | package EmbeddedApp;
|
---|
1086 | use Mojolicious::Lite;
|
---|
1087 |
|
---|
1088 | get '/bar' => 'bar';
|
---|
1089 |
|
---|
1090 | 1;
|
---|
1091 | __DATA__
|
---|
1092 | @@ bar.html.ep
|
---|
1093 | Hello World!
|
---|
1094 |
|
---|
1095 | The C<app> stash value, which won't be inherited by nested routes, can be used
|
---|
1096 | for already instantiated applications. Now just load the plugin and you're
|
---|
1097 | done.
|
---|
1098 |
|
---|
1099 | # Application
|
---|
1100 | package MyApp;
|
---|
1101 | use Mojo::Base 'Mojolicious';
|
---|
1102 |
|
---|
1103 | sub startup {
|
---|
1104 | my $self = shift;
|
---|
1105 |
|
---|
1106 | # Plugin
|
---|
1107 | $self->plugin('MyEmbeddedApp');
|
---|
1108 | }
|
---|
1109 |
|
---|
1110 | 1;
|
---|
1111 |
|
---|
1112 | =head1 MORE
|
---|
1113 |
|
---|
1114 | You can continue with L<Mojolicious::Guides> now or take a look at the
|
---|
1115 | L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
|
---|
1116 | documentation and examples by many different authors.
|
---|
1117 |
|
---|
1118 | =head1 SUPPORT
|
---|
1119 |
|
---|
1120 | If you have any questions the documentation might not yet answer, don't
|
---|
1121 | hesitate to ask on the
|
---|
1122 | L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
|
---|
1123 | channel C<#mojo> on C<irc.perl.org>
|
---|
1124 | (L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
|
---|
1125 |
|
---|
1126 | =cut
|
---|