1 |
|
---|
2 | =encoding utf8
|
---|
3 |
|
---|
4 | =head1 NAME
|
---|
5 |
|
---|
6 | Mojolicious::Guides::Tutorial - Get started with Mojolicious
|
---|
7 |
|
---|
8 | =head1 TUTORIAL
|
---|
9 |
|
---|
10 | A quick example-driven introduction to the wonders of L<Mojolicious::Lite>.
|
---|
11 | Almost everything you'll learn here also applies to full L<Mojolicious>
|
---|
12 | applications.
|
---|
13 |
|
---|
14 | This is only the first of the L<Mojolicious::Guides>. Other guides delve deeper
|
---|
15 | into topics like L<growing|Mojolicious::Guides::Growing> a L<Mojolicious::Lite>
|
---|
16 | prototype into a well-structured L<Mojolicious> application,
|
---|
17 | L<routing|Mojolicious::Guides::Routing>,
|
---|
18 | L<rendering|Mojolicious::Guides::Rendering> and more. It is highly encouraged
|
---|
19 | that readers continue on to the remaining guides after reading this one.
|
---|
20 |
|
---|
21 | =head2 Hello World
|
---|
22 |
|
---|
23 | A simple Hello World application can look like this, L<strict>, L<warnings>,
|
---|
24 | L<utf8> and Perl 5.10 L<features|feature> are automatically enabled and a few
|
---|
25 | L<functions|Mojolicious::Lite/"FUNCTIONS"> imported, when you use
|
---|
26 | L<Mojolicious::Lite>, turning your script into a full featured web application.
|
---|
27 |
|
---|
28 | #!/usr/bin/env perl
|
---|
29 | use Mojolicious::Lite;
|
---|
30 |
|
---|
31 | get '/' => sub {
|
---|
32 | my $c = shift;
|
---|
33 | $c->render(text => 'Hello World!');
|
---|
34 | };
|
---|
35 |
|
---|
36 | app->start;
|
---|
37 |
|
---|
38 | With L<Mojolicious::Command::generate::lite_app> there is also a helper command
|
---|
39 | to generate a small example application.
|
---|
40 |
|
---|
41 | $ mojo generate lite_app myapp.pl
|
---|
42 |
|
---|
43 | =head2 Commands
|
---|
44 |
|
---|
45 | Many different L<commands|Mojolicious::Commands/"COMMANDS"> are automatically
|
---|
46 | available from the command line. CGI and L<PSGI> environments can even be
|
---|
47 | detected and will usually just work without commands.
|
---|
48 |
|
---|
49 | $ ./myapp.pl daemon
|
---|
50 | Server available at http://127.0.0.1:3000
|
---|
51 |
|
---|
52 | $ ./myapp.pl daemon -l http://*:8080
|
---|
53 | Server available at http://127.0.0.1:8080
|
---|
54 |
|
---|
55 | $ ./myapp.pl cgi
|
---|
56 | ...CGI output...
|
---|
57 |
|
---|
58 | $ ./myapp.pl get /
|
---|
59 | Hello World!
|
---|
60 |
|
---|
61 | $ ./myapp.pl
|
---|
62 | ...List of available commands (or automatically detected environment)...
|
---|
63 |
|
---|
64 | A call to L<Mojolicious/"start"> (C<app-E<gt>start>), which starts the command
|
---|
65 | system, should be the last expression in your application, because its return
|
---|
66 | value can be significant.
|
---|
67 |
|
---|
68 | # Use @ARGV to pick a command
|
---|
69 | app->start;
|
---|
70 |
|
---|
71 | # Start the "daemon" command
|
---|
72 | app->start('daemon', '-l', 'http://*:8080');
|
---|
73 |
|
---|
74 | =head2 Reloading
|
---|
75 |
|
---|
76 | Your application will automatically reload itself if you start it with the
|
---|
77 | L<morbo> development web server, so you don't have to restart the server after
|
---|
78 | every change.
|
---|
79 |
|
---|
80 | $ morbo ./myapp.pl
|
---|
81 | Server available at http://127.0.0.1:3000
|
---|
82 |
|
---|
83 | For more information about how to deploy your application see also
|
---|
84 | L<Mojolicious::Guides::Cookbook/"DEPLOYMENT">.
|
---|
85 |
|
---|
86 | =head2 Routes
|
---|
87 |
|
---|
88 | Routes are basically just fancy paths that can contain different kinds of
|
---|
89 | placeholders and usually lead to an action, if they match the path part of the
|
---|
90 | request URL. The first argument passed to all actions (C<$c>) is a
|
---|
91 | L<Mojolicious::Controller> object, containing both the HTTP request and
|
---|
92 | response.
|
---|
93 |
|
---|
94 | use Mojolicious::Lite;
|
---|
95 |
|
---|
96 | # Route leading to an action that renders some text
|
---|
97 | get '/foo' => sub {
|
---|
98 | my $c = shift;
|
---|
99 | $c->render(text => 'Hello World!');
|
---|
100 | };
|
---|
101 |
|
---|
102 | app->start;
|
---|
103 |
|
---|
104 | Response content is often generated by actions with
|
---|
105 | L<Mojolicious::Controller/"render">, but more about that later.
|
---|
106 |
|
---|
107 | =head2 GET/POST parameters
|
---|
108 |
|
---|
109 | All C<GET> and C<POST> parameters sent with the request are accessible via
|
---|
110 | L<Mojolicious::Controller/"param">.
|
---|
111 |
|
---|
112 | use Mojolicious::Lite;
|
---|
113 |
|
---|
114 | # /foo?user=sri
|
---|
115 | get '/foo' => sub {
|
---|
116 | my $c = shift;
|
---|
117 | my $user = $c->param('user');
|
---|
118 | $c->render(text => "Hello $user.");
|
---|
119 | };
|
---|
120 |
|
---|
121 | app->start;
|
---|
122 |
|
---|
123 | =head2 Stash and templates
|
---|
124 |
|
---|
125 | The L<Mojolicious::Controller/"stash"> is used to pass data to templates, which
|
---|
126 | can be inlined in the C<DATA> section. A few stash values like C<template>,
|
---|
127 | C<text> and C<data> are reserved and will be used by
|
---|
128 | L<Mojolicious::Controller/"render"> to decide how a response should be
|
---|
129 | generated.
|
---|
130 |
|
---|
131 | use Mojolicious::Lite;
|
---|
132 |
|
---|
133 | # Route leading to an action that renders a template
|
---|
134 | get '/foo' => sub {
|
---|
135 | my $c = shift;
|
---|
136 | $c->stash(one => 23);
|
---|
137 | $c->render(template => 'magic', two => 24);
|
---|
138 | };
|
---|
139 |
|
---|
140 | app->start;
|
---|
141 | __DATA__
|
---|
142 |
|
---|
143 | @@ magic.html.ep
|
---|
144 | The magic numbers are <%= $one %> and <%= $two %>.
|
---|
145 |
|
---|
146 | For more information about templates see also
|
---|
147 | L<Mojolicious::Guides::Rendering/"Embedded Perl">.
|
---|
148 |
|
---|
149 | =head2 HTTP
|
---|
150 |
|
---|
151 | L<Mojolicious::Controller/"req"> and L<Mojolicious::Controller/"res"> give you
|
---|
152 | full access to all HTTP features and information.
|
---|
153 |
|
---|
154 | use Mojolicious::Lite;
|
---|
155 |
|
---|
156 | # Access request information
|
---|
157 | get '/agent' => sub {
|
---|
158 | my $c = shift;
|
---|
159 | my $host = $c->req->url->to_abs->host;
|
---|
160 | my $ua = $c->req->headers->user_agent;
|
---|
161 | $c->render(text => "Request by $ua reached $host.");
|
---|
162 | };
|
---|
163 |
|
---|
164 | # Echo the request body and send custom header with response
|
---|
165 | post '/echo' => sub {
|
---|
166 | my $c = shift;
|
---|
167 | $c->res->headers->header('X-Bender' => 'Bite my shiny metal ass!');
|
---|
168 | $c->render(data => $c->req->body);
|
---|
169 | };
|
---|
170 |
|
---|
171 | app->start;
|
---|
172 |
|
---|
173 | You can test the more advanced examples right from the command line with
|
---|
174 | L<Mojolicious::Command::get>.
|
---|
175 |
|
---|
176 | $ ./myapp.pl get -v -M POST -c 'test' /echo
|
---|
177 |
|
---|
178 | =head2 JSON
|
---|
179 |
|
---|
180 | JSON is the most commonly used data-interchange format for web services.
|
---|
181 | L<Mojolicious> loves JSON and comes with the possibly fastest pure-Perl
|
---|
182 | implementation L<Mojo::JSON> built right in, which is accessible through
|
---|
183 | L<Mojo::Message/"json"> as well as the reserved stash value C<json>.
|
---|
184 |
|
---|
185 | use Mojolicious::Lite;
|
---|
186 |
|
---|
187 | # Modify the received JSON document and return it
|
---|
188 | put '/reverse' => sub {
|
---|
189 | my $c = shift;
|
---|
190 | my $hash = $c->req->json;
|
---|
191 | $hash->{message} = reverse $hash->{message};
|
---|
192 | $c->render(json => $hash);
|
---|
193 | };
|
---|
194 |
|
---|
195 | app->start;
|
---|
196 |
|
---|
197 | You can send JSON documents from the command line with
|
---|
198 | L<Mojolicious::Command::get>.
|
---|
199 |
|
---|
200 | $ ./myapp.pl get -M PUT -c '{"message":"Hello Mojo!"}' /reverse
|
---|
201 |
|
---|
202 | =head2 Built-in C<exception> and C<not_found> pages
|
---|
203 |
|
---|
204 | During development you will encounter these pages whenever you make a mistake,
|
---|
205 | they are gorgeous and contain a lot of valuable information that will aid you
|
---|
206 | in debugging your application.
|
---|
207 |
|
---|
208 | use Mojolicious::Lite;
|
---|
209 |
|
---|
210 | # Not found (404)
|
---|
211 | get '/missing' => sub { shift->render(template => 'does_not_exist') };
|
---|
212 |
|
---|
213 | # Exception (500)
|
---|
214 | get '/dies' => sub { die 'Intentional error' };
|
---|
215 |
|
---|
216 | app->start;
|
---|
217 |
|
---|
218 | You can even use CSS selectors with L<Mojolicious::Command::get> to extract only
|
---|
219 | the information you're actually interested in.
|
---|
220 |
|
---|
221 | $ ./myapp.pl get /dies '#error'
|
---|
222 |
|
---|
223 | And don't worry about revealing too much information on these pages, they are
|
---|
224 | only available during development, and will be replaced automatically with pages
|
---|
225 | that don't reveal any sensitive information in a production environment.
|
---|
226 |
|
---|
227 | =head2 Route names
|
---|
228 |
|
---|
229 | All routes can have a name associated with them, this allows automatic template
|
---|
230 | detection and backreferencing with L<Mojolicious::Controller/"url_for">, on
|
---|
231 | which many methods and helpers like
|
---|
232 | L<Mojolicious::Plugin::TagHelpers/"link_to"> rely.
|
---|
233 |
|
---|
234 | use Mojolicious::Lite;
|
---|
235 |
|
---|
236 | # Render the template "index.html.ep"
|
---|
237 | get '/' => sub {
|
---|
238 | my $c = shift;
|
---|
239 | $c->render;
|
---|
240 | } => 'index';
|
---|
241 |
|
---|
242 | # Render the template "hello.html.ep"
|
---|
243 | get '/hello';
|
---|
244 |
|
---|
245 | app->start;
|
---|
246 | __DATA__
|
---|
247 |
|
---|
248 | @@ index.html.ep
|
---|
249 | <%= link_to Hello => 'hello' %>.
|
---|
250 | <%= link_to Reload => 'index' %>.
|
---|
251 |
|
---|
252 | @@ hello.html.ep
|
---|
253 | Hello World!
|
---|
254 |
|
---|
255 | Nameless routes get an automatically generated one assigned that is simply
|
---|
256 | equal to the route itself without non-word characters.
|
---|
257 |
|
---|
258 | =head2 Layouts
|
---|
259 |
|
---|
260 | Templates can have layouts too, you just select one with the helper
|
---|
261 | L<Mojolicious::Plugin::DefaultHelpers/"layout"> and place the result of the
|
---|
262 | current template with the helper
|
---|
263 | L<Mojolicious::Plugin::DefaultHelpers/"content">.
|
---|
264 |
|
---|
265 | use Mojolicious::Lite;
|
---|
266 |
|
---|
267 | get '/with_layout';
|
---|
268 |
|
---|
269 | app->start;
|
---|
270 | __DATA__
|
---|
271 |
|
---|
272 | @@ with_layout.html.ep
|
---|
273 | % title 'Green';
|
---|
274 | % layout 'green';
|
---|
275 | Hello World!
|
---|
276 |
|
---|
277 | @@ layouts/green.html.ep
|
---|
278 | <!DOCTYPE html>
|
---|
279 | <html>
|
---|
280 | <head><title><%= title %></title></head>
|
---|
281 | <body><%= content %></body>
|
---|
282 | </html>
|
---|
283 |
|
---|
284 | The stash or helpers like L<Mojolicious::Plugin::DefaultHelpers/"title"> can be
|
---|
285 | used to pass additional data to the layout.
|
---|
286 |
|
---|
287 | =head2 Blocks
|
---|
288 |
|
---|
289 | Template blocks can be used like normal Perl functions and are always delimited
|
---|
290 | by the C<begin> and C<end> keywords, they are the foundation for many helpers.
|
---|
291 |
|
---|
292 | use Mojolicious::Lite;
|
---|
293 |
|
---|
294 | get '/with_block' => 'block';
|
---|
295 |
|
---|
296 | app->start;
|
---|
297 | __DATA__
|
---|
298 |
|
---|
299 | @@ block.html.ep
|
---|
300 | % my $link = begin
|
---|
301 | % my ($url, $name) = @_;
|
---|
302 | Try <%= link_to $url => begin %><%= $name %><% end %>.
|
---|
303 | % end
|
---|
304 | <!DOCTYPE html>
|
---|
305 | <html>
|
---|
306 | <head><title>Sebastians frameworks</title></head>
|
---|
307 | <body>
|
---|
308 | %= $link->('http://mojolicious.org', 'Mojolicious')
|
---|
309 | %= $link->('http://catalystframework.org', 'Catalyst')
|
---|
310 | </body>
|
---|
311 | </html>
|
---|
312 |
|
---|
313 | =head2 Helpers
|
---|
314 |
|
---|
315 | Helpers are little functions you can create with the keyword
|
---|
316 | L<Mojolicious::Lite/"helper"> and reuse throughout your whole application, from
|
---|
317 | actions to templates.
|
---|
318 |
|
---|
319 | use Mojolicious::Lite;
|
---|
320 |
|
---|
321 | # A helper to identify visitors
|
---|
322 | helper whois => sub {
|
---|
323 | my $c = shift;
|
---|
324 | my $agent = $c->req->headers->user_agent || 'Anonymous';
|
---|
325 | my $ip = $c->tx->remote_address;
|
---|
326 | return "$agent ($ip)";
|
---|
327 | };
|
---|
328 |
|
---|
329 | # Use helper in action and template
|
---|
330 | get '/secret' => sub {
|
---|
331 | my $c = shift;
|
---|
332 | my $user = $c->whois;
|
---|
333 | $c->app->log->debug("Request from $user");
|
---|
334 | };
|
---|
335 |
|
---|
336 | app->start;
|
---|
337 | __DATA__
|
---|
338 |
|
---|
339 | @@ secret.html.ep
|
---|
340 | We know who you are <%= whois %>.
|
---|
341 |
|
---|
342 | A list of all built-in ones can be found in
|
---|
343 | L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
|
---|
344 |
|
---|
345 | =head2 Placeholders
|
---|
346 |
|
---|
347 | Route placeholders allow capturing parts of a request path until a C</> or C<.>
|
---|
348 | separator occurs, similar to the regular expression C<([^/.]+)>. Results are
|
---|
349 | accessible via L<Mojolicious::Controller/"stash"> and
|
---|
350 | L<Mojolicious::Controller/"param">.
|
---|
351 |
|
---|
352 | use Mojolicious::Lite;
|
---|
353 |
|
---|
354 | # /foo/test
|
---|
355 | # /foo/test123
|
---|
356 | get '/foo/:bar' => sub {
|
---|
357 | my $c = shift;
|
---|
358 | my $bar = $c->stash('bar');
|
---|
359 | $c->render(text => "Our :bar placeholder matched $bar");
|
---|
360 | };
|
---|
361 |
|
---|
362 | # /testsomething/foo
|
---|
363 | # /test123something/foo
|
---|
364 | get '/<:bar>something/foo' => sub {
|
---|
365 | my $c = shift;
|
---|
366 | my $bar = $c->param('bar');
|
---|
367 | $c->render(text => "Our :bar placeholder matched $bar");
|
---|
368 | };
|
---|
369 |
|
---|
370 | app->start;
|
---|
371 |
|
---|
372 | To separate them from the surrounding text, you can surround your placeholders
|
---|
373 | with C<E<lt>> and C<E<gt>>, which also makes the colon prefix optional.
|
---|
374 |
|
---|
375 | =head2 Relaxed Placeholders
|
---|
376 |
|
---|
377 | Relaxed placeholders allow matching of everything until a C</> occurs, similar
|
---|
378 | to the regular expression C<([^/]+)>.
|
---|
379 |
|
---|
380 | use Mojolicious::Lite;
|
---|
381 |
|
---|
382 | # /hello/test
|
---|
383 | # /hello/test.html
|
---|
384 | get '/hello/#you' => 'groovy';
|
---|
385 |
|
---|
386 | app->start;
|
---|
387 | __DATA__
|
---|
388 |
|
---|
389 | @@ groovy.html.ep
|
---|
390 | Your name is <%= $you %>.
|
---|
391 |
|
---|
392 | =head2 Wildcard placeholders
|
---|
393 |
|
---|
394 | Wildcard placeholders allow matching absolutely everything, including C</> and
|
---|
395 | C<.>, similar to the regular expression C<(.+)>.
|
---|
396 |
|
---|
397 | use Mojolicious::Lite;
|
---|
398 |
|
---|
399 | # /hello/test
|
---|
400 | # /hello/test123
|
---|
401 | # /hello/test.123/test/123
|
---|
402 | get '/hello/*you' => 'groovy';
|
---|
403 |
|
---|
404 | app->start;
|
---|
405 | __DATA__
|
---|
406 |
|
---|
407 | @@ groovy.html.ep
|
---|
408 | Your name is <%= $you %>.
|
---|
409 |
|
---|
410 | =head2 HTTP methods
|
---|
411 |
|
---|
412 | Routes can be restricted to specific request methods with different keywords
|
---|
413 | like L<Mojolicious::Lite/"get"> and L<Mojolicious::Lite/"any">.
|
---|
414 |
|
---|
415 | use Mojolicious::Lite;
|
---|
416 |
|
---|
417 | # GET /hello
|
---|
418 | get '/hello' => sub {
|
---|
419 | my $c = shift;
|
---|
420 | $c->render(text => 'Hello World!');
|
---|
421 | };
|
---|
422 |
|
---|
423 | # PUT /hello
|
---|
424 | put '/hello' => sub {
|
---|
425 | my $c = shift;
|
---|
426 | my $size = length $c->req->body;
|
---|
427 | $c->render(text => "You uploaded $size bytes to /hello.");
|
---|
428 | };
|
---|
429 |
|
---|
430 | # GET|POST|PATCH /bye
|
---|
431 | any ['GET', 'POST', 'PATCH'] => '/bye' => sub {
|
---|
432 | my $c = shift;
|
---|
433 | $c->render(text => 'Bye World!');
|
---|
434 | };
|
---|
435 |
|
---|
436 | # * /whatever
|
---|
437 | any '/whatever' => sub {
|
---|
438 | my $c = shift;
|
---|
439 | my $method = $c->req->method;
|
---|
440 | $c->render(text => "You called /whatever with $method.");
|
---|
441 | };
|
---|
442 |
|
---|
443 | app->start;
|
---|
444 |
|
---|
445 | =head2 Optional placeholders
|
---|
446 |
|
---|
447 | All placeholders require a value, but by assigning them default values you can
|
---|
448 | make capturing optional.
|
---|
449 |
|
---|
450 | use Mojolicious::Lite;
|
---|
451 |
|
---|
452 | # /hello
|
---|
453 | # /hello/Sara
|
---|
454 | get '/hello/:name' => {name => 'Sebastian', day => 'Monday'} => sub {
|
---|
455 | my $c = shift;
|
---|
456 | $c->render(template => 'groovy', format => 'txt');
|
---|
457 | };
|
---|
458 |
|
---|
459 | app->start;
|
---|
460 | __DATA__
|
---|
461 |
|
---|
462 | @@ groovy.txt.ep
|
---|
463 | My name is <%= $name %> and it is <%= $day %>.
|
---|
464 |
|
---|
465 | Default values that don't belong to a placeholder simply get merged into the
|
---|
466 | stash all the time.
|
---|
467 |
|
---|
468 | =head2 Restrictive placeholders
|
---|
469 |
|
---|
470 | A very easy way to make placeholders more restrictive are alternatives, you just
|
---|
471 | make a list of possible values.
|
---|
472 |
|
---|
473 | use Mojolicious::Lite;
|
---|
474 |
|
---|
475 | # /test
|
---|
476 | # /123
|
---|
477 | any '/:foo' => [foo => ['test', '123']] => sub {
|
---|
478 | my $c = shift;
|
---|
479 | my $foo = $c->param('foo');
|
---|
480 | $c->render(text => "Our :foo placeholder matched $foo");
|
---|
481 | };
|
---|
482 |
|
---|
483 | app->start;
|
---|
484 |
|
---|
485 | All placeholders get compiled to a regular expression internally, this process
|
---|
486 | can also be customized. Just make sure not to use C<^> and C<$>, or capturing
|
---|
487 | groups C<(...)>, non-capturing groups C<(?:...)> are fine though.
|
---|
488 |
|
---|
489 | use Mojolicious::Lite;
|
---|
490 |
|
---|
491 | # /1
|
---|
492 | # /123
|
---|
493 | any '/:bar' => [bar => qr/\d+/] => sub {
|
---|
494 | my $c = shift;
|
---|
495 | my $bar = $c->param('bar');
|
---|
496 | $c->render(text => "Our :bar placeholder matched $bar");
|
---|
497 | };
|
---|
498 |
|
---|
499 | app->start;
|
---|
500 |
|
---|
501 | You can take a closer look at all the generated regular expressions with the
|
---|
502 | command L<Mojolicious::Command::routes>.
|
---|
503 |
|
---|
504 | $ ./myapp.pl routes -v
|
---|
505 |
|
---|
506 | =head2 Under
|
---|
507 |
|
---|
508 | Authentication and code shared between multiple routes can be realized easily
|
---|
509 | with routes generated by L<Mojolicious::Lite/"under">. All following routes are
|
---|
510 | only evaluated if the callback returned a true value.
|
---|
511 |
|
---|
512 | use Mojolicious::Lite;
|
---|
513 |
|
---|
514 | # Authenticate based on name parameter
|
---|
515 | under sub {
|
---|
516 | my $c = shift;
|
---|
517 |
|
---|
518 | # Authenticated
|
---|
519 | my $name = $c->param('name') || '';
|
---|
520 | return 1 if $name eq 'Bender';
|
---|
521 |
|
---|
522 | # Not authenticated
|
---|
523 | $c->render(template => 'denied');
|
---|
524 | return undef;
|
---|
525 | };
|
---|
526 |
|
---|
527 | # Only reached when authenticated
|
---|
528 | get '/' => 'index';
|
---|
529 |
|
---|
530 | app->start;
|
---|
531 | __DATA__
|
---|
532 |
|
---|
533 | @@ denied.html.ep
|
---|
534 | You are not Bender, permission denied.
|
---|
535 |
|
---|
536 | @@ index.html.ep
|
---|
537 | Hi Bender.
|
---|
538 |
|
---|
539 | Prefixing multiple routes is another good use for it.
|
---|
540 |
|
---|
541 | use Mojolicious::Lite;
|
---|
542 |
|
---|
543 | # /foo
|
---|
544 | under '/foo';
|
---|
545 |
|
---|
546 | # /foo/bar
|
---|
547 | get '/bar' => {text => 'foo bar'};
|
---|
548 |
|
---|
549 | # /foo/baz
|
---|
550 | get '/baz' => {text => 'foo baz'};
|
---|
551 |
|
---|
552 | # / (reset)
|
---|
553 | under '/' => {msg => 'whatever'};
|
---|
554 |
|
---|
555 | # /bar
|
---|
556 | get '/bar' => {inline => '<%= $msg %> works'};
|
---|
557 |
|
---|
558 | app->start;
|
---|
559 |
|
---|
560 | You can also group related routes with L<Mojolicious::Lite/"group">, which
|
---|
561 | allows nesting of routes generated with L<Mojolicious::Lite/"under">.
|
---|
562 |
|
---|
563 | use Mojolicious::Lite;
|
---|
564 |
|
---|
565 | # Global logic shared by all routes
|
---|
566 | under sub {
|
---|
567 | my $c = shift;
|
---|
568 | return 1 if $c->req->headers->header('X-Bender');
|
---|
569 | $c->render(text => "You're not Bender.");
|
---|
570 | return undef;
|
---|
571 | };
|
---|
572 |
|
---|
573 | # Admin section
|
---|
574 | group {
|
---|
575 |
|
---|
576 | # Local logic shared only by routes in this group
|
---|
577 | under '/admin' => sub {
|
---|
578 | my $c = shift;
|
---|
579 | return 1 if $c->req->headers->header('X-Awesome');
|
---|
580 | $c->render(text => "You're not awesome enough.");
|
---|
581 | return undef;
|
---|
582 | };
|
---|
583 |
|
---|
584 | # GET /admin/dashboard
|
---|
585 | get '/dashboard' => {text => 'Nothing to see here yet.'};
|
---|
586 | };
|
---|
587 |
|
---|
588 | # GET /welcome
|
---|
589 | get '/welcome' => {text => 'Hi Bender.'};
|
---|
590 |
|
---|
591 | app->start;
|
---|
592 |
|
---|
593 | =head2 Formats
|
---|
594 |
|
---|
595 | Formats can be automatically detected from file extensions like C<.html>, they
|
---|
596 | are used to find the right template and generate the correct C<Content-Type>
|
---|
597 | header.
|
---|
598 |
|
---|
599 | use Mojolicious::Lite;
|
---|
600 |
|
---|
601 | # /detection
|
---|
602 | # /detection.html
|
---|
603 | # /detection.txt
|
---|
604 | get '/detection' => sub {
|
---|
605 | my $c = shift;
|
---|
606 | $c->render(template => 'detected');
|
---|
607 | };
|
---|
608 |
|
---|
609 | app->start;
|
---|
610 | __DATA__
|
---|
611 |
|
---|
612 | @@ detected.html.ep
|
---|
613 | <!DOCTYPE html>
|
---|
614 | <html>
|
---|
615 | <head><title>Detected</title></head>
|
---|
616 | <body>HTML was detected.</body>
|
---|
617 | </html>
|
---|
618 |
|
---|
619 | @@ detected.txt.ep
|
---|
620 | TXT was detected.
|
---|
621 |
|
---|
622 | The default format is C<html>, and restrictive placeholders can be used to
|
---|
623 | limit possible values.
|
---|
624 |
|
---|
625 | use Mojolicious::Lite;
|
---|
626 |
|
---|
627 | # /hello.json
|
---|
628 | # /hello.txt
|
---|
629 | get '/hello' => [format => ['json', 'txt']] => sub {
|
---|
630 | my $c = shift;
|
---|
631 | return $c->render(json => {hello => 'world'})
|
---|
632 | if $c->stash('format') eq 'json';
|
---|
633 | $c->render(text => 'hello world');
|
---|
634 | };
|
---|
635 |
|
---|
636 | app->start;
|
---|
637 |
|
---|
638 | Or you can just disable format detection with a special type of restrictive
|
---|
639 | placeholder.
|
---|
640 |
|
---|
641 | use Mojolicious::Lite;
|
---|
642 |
|
---|
643 | # /hello
|
---|
644 | get '/hello' => [format => 0] => {text => 'No format detection.'};
|
---|
645 |
|
---|
646 | # Disable detection and allow the following routes to re-enable it on demand
|
---|
647 | under [format => 0];
|
---|
648 |
|
---|
649 | # /foo
|
---|
650 | get '/foo' => {text => 'No format detection again.'};
|
---|
651 |
|
---|
652 | # /bar.txt
|
---|
653 | get '/bar' => [format => 'txt'] => {text => ' Just one format.'};
|
---|
654 |
|
---|
655 | app->start;
|
---|
656 |
|
---|
657 | =head2 Content negotiation
|
---|
658 |
|
---|
659 | For resources with different representations and that require truly RESTful
|
---|
660 | content negotiation you can also use L<Mojolicious::Controller/"respond_to">.
|
---|
661 |
|
---|
662 | use Mojolicious::Lite;
|
---|
663 |
|
---|
664 | # /hello (Accept: application/json)
|
---|
665 | # /hello (Accept: application/xml)
|
---|
666 | # /hello.json
|
---|
667 | # /hello.xml
|
---|
668 | # /hello?format=json
|
---|
669 | # /hello?format=xml
|
---|
670 | get '/hello' => sub {
|
---|
671 | my $c = shift;
|
---|
672 | $c->respond_to(
|
---|
673 | json => {json => {hello => 'world'}},
|
---|
674 | xml => {text => '<hello>world</hello>'},
|
---|
675 | any => {data => '', status => 204}
|
---|
676 | );
|
---|
677 | };
|
---|
678 |
|
---|
679 | app->start;
|
---|
680 |
|
---|
681 | MIME type mappings can be extended or changed easily with
|
---|
682 | L<Mojolicious/"types">.
|
---|
683 |
|
---|
684 | app->types->type(rdf => 'application/rdf+xml');
|
---|
685 |
|
---|
686 | =head2 Static files
|
---|
687 |
|
---|
688 | Similar to templates, but with only a single file extension and optional Base64
|
---|
689 | encoding, static files can be inlined in the C<DATA> section and are served
|
---|
690 | automatically.
|
---|
691 |
|
---|
692 | use Mojolicious::Lite;
|
---|
693 |
|
---|
694 | app->start;
|
---|
695 | __DATA__
|
---|
696 |
|
---|
697 | @@ something.js
|
---|
698 | alert('hello!');
|
---|
699 |
|
---|
700 | @@ test.txt (base64)
|
---|
701 | dGVzdCAxMjMKbGFsYWxh
|
---|
702 |
|
---|
703 | External static files are not limited to a single file extension and will be
|
---|
704 | served automatically from a C<public> directory if it exists.
|
---|
705 |
|
---|
706 | $ mkdir public
|
---|
707 | $ mv something.js public/something.js
|
---|
708 | $ mv mojolicious.tar.gz public/mojolicious.tar.gz
|
---|
709 |
|
---|
710 | Both have a higher precedence than routes for C<GET> and C<HEAD> requests.
|
---|
711 | Content negotiation with C<Range>, C<If-None-Match> and C<If-Modified-Since>
|
---|
712 | headers is supported as well and can be tested very easily with
|
---|
713 | L<Mojolicious::Command::get>.
|
---|
714 |
|
---|
715 | $ ./myapp.pl get /something.js -v -H 'Range: bytes=2-4'
|
---|
716 |
|
---|
717 | =head2 External templates
|
---|
718 |
|
---|
719 | External templates will be searched by the renderer in a C<templates> directory
|
---|
720 | if it exists.
|
---|
721 |
|
---|
722 | $ mkdir -p templates/foo
|
---|
723 | $ echo 'Hello World!' > templates/foo/bar.html.ep
|
---|
724 |
|
---|
725 | They have a higher precedence than templates in the C<DATA> section.
|
---|
726 |
|
---|
727 | use Mojolicious::Lite;
|
---|
728 |
|
---|
729 | # Render template "templates/foo/bar.html.ep"
|
---|
730 | any '/external' => sub {
|
---|
731 | my $c = shift;
|
---|
732 | $c->render(template => 'foo/bar');
|
---|
733 | };
|
---|
734 |
|
---|
735 | app->start;
|
---|
736 |
|
---|
737 | =head2 Home
|
---|
738 |
|
---|
739 | You can use L<Mojolicious/"home"> to interact with the directory your
|
---|
740 | application considers its home. This is the directory it will search for
|
---|
741 | C<public> and C<templates> directories, but you can use it to store all sorts of
|
---|
742 | application specific data.
|
---|
743 |
|
---|
744 | $ mkdir cache
|
---|
745 | $ echo 'Hello World!' > cache/hello.txt
|
---|
746 |
|
---|
747 | There are many useful methods L<Mojo::Home> inherits from L<Mojo::File>, like
|
---|
748 | L<Mojo::File/"child"> and L<Mojo::File/"slurp">, that will help you keep your
|
---|
749 | application portable across many different operating systems.
|
---|
750 |
|
---|
751 | use Mojolicious::Lite;
|
---|
752 |
|
---|
753 | # Load message into memory
|
---|
754 | my $hello = app->home->child('cache', 'hello.txt')->slurp;
|
---|
755 |
|
---|
756 | # Display message
|
---|
757 | get '/' => sub {
|
---|
758 | my $c = shift;
|
---|
759 | $c->render(text => $hello);
|
---|
760 | };
|
---|
761 |
|
---|
762 | You can also introspect your application from the command line with
|
---|
763 | L<Mojolicious::Command::eval>.
|
---|
764 |
|
---|
765 | $ ./myapp.pl eval -v 'app->home'
|
---|
766 |
|
---|
767 | =head2 Conditions
|
---|
768 |
|
---|
769 | Conditions such as C<agent> and C<host> from
|
---|
770 | L<Mojolicious::Plugin::HeaderCondition> allow even more powerful route
|
---|
771 | constructs.
|
---|
772 |
|
---|
773 | use Mojolicious::Lite;
|
---|
774 |
|
---|
775 | # Firefox
|
---|
776 | get '/foo' => (agent => qr/Firefox/) => sub {
|
---|
777 | my $c = shift;
|
---|
778 | $c->render(text => 'Congratulations, you are using a cool browser.');
|
---|
779 | };
|
---|
780 |
|
---|
781 | # Internet Explorer
|
---|
782 | get '/foo' => (agent => qr/Internet Explorer/) => sub {
|
---|
783 | my $c = shift;
|
---|
784 | $c->render(text => 'Dude, you really need to upgrade to Firefox.');
|
---|
785 | };
|
---|
786 |
|
---|
787 | # http://mojolicious.org/bar
|
---|
788 | get '/bar' => (host => 'mojolicious.org') => sub {
|
---|
789 | my $c = shift;
|
---|
790 | $c->render(text => 'Hello Mojolicious.');
|
---|
791 | };
|
---|
792 |
|
---|
793 | app->start;
|
---|
794 |
|
---|
795 | =head2 Sessions
|
---|
796 |
|
---|
797 | Cookie-based sessions just work out of the box, as soon as you start using them
|
---|
798 | through the helper L<Mojolicious::Plugin::DefaultHelpers/"session">. Just be
|
---|
799 | aware that all session data gets serialized with L<Mojo::JSON> and stored
|
---|
800 | client-side, with a cryptographic signature to prevent tampering.
|
---|
801 |
|
---|
802 | use Mojolicious::Lite;
|
---|
803 |
|
---|
804 | # Access session data in action and template
|
---|
805 | get '/counter' => sub {
|
---|
806 | my $c = shift;
|
---|
807 | $c->session->{counter}++;
|
---|
808 | };
|
---|
809 |
|
---|
810 | app->start;
|
---|
811 | __DATA__
|
---|
812 |
|
---|
813 | @@ counter.html.ep
|
---|
814 | Counter: <%= session 'counter' %>
|
---|
815 |
|
---|
816 | Note that you should use custom L<Mojolicious/"secrets"> to make signed cookies
|
---|
817 | really tamper resistant.
|
---|
818 |
|
---|
819 | app->secrets(['My secret passphrase here']);
|
---|
820 |
|
---|
821 | =head2 File uploads
|
---|
822 |
|
---|
823 | All files uploaded via C<multipart/form-data> request are automatically
|
---|
824 | available as L<Mojo::Upload> objects from L<Mojolicious::Controller/"param">.
|
---|
825 | And you don't have to worry about memory usage, because all files above 250KiB
|
---|
826 | will be automatically streamed into a temporary file. To build HTML forms more
|
---|
827 | efficiently, you can also use tag helpers like
|
---|
828 | L<Mojolicious::Plugin::TagHelpers/"form_for">.
|
---|
829 |
|
---|
830 | use Mojolicious::Lite;
|
---|
831 |
|
---|
832 | # Upload form in DATA section
|
---|
833 | get '/' => 'form';
|
---|
834 |
|
---|
835 | # Multipart upload handler
|
---|
836 | post '/upload' => sub {
|
---|
837 | my $c = shift;
|
---|
838 |
|
---|
839 | # Check file size
|
---|
840 | return $c->render(text => 'File is too big.', status => 200)
|
---|
841 | if $c->req->is_limit_exceeded;
|
---|
842 |
|
---|
843 | # Process uploaded file
|
---|
844 | return $c->redirect_to('form') unless my $example = $c->param('example');
|
---|
845 | my $size = $example->size;
|
---|
846 | my $name = $example->filename;
|
---|
847 | $c->render(text => "Thanks for uploading $size byte file $name.");
|
---|
848 | };
|
---|
849 |
|
---|
850 | app->start;
|
---|
851 | __DATA__
|
---|
852 |
|
---|
853 | @@ form.html.ep
|
---|
854 | <!DOCTYPE html>
|
---|
855 | <html>
|
---|
856 | <head><title>Upload</title></head>
|
---|
857 | <body>
|
---|
858 | %= form_for upload => (enctype => 'multipart/form-data') => begin
|
---|
859 | %= file_field 'example'
|
---|
860 | %= submit_button 'Upload'
|
---|
861 | % end
|
---|
862 | </body>
|
---|
863 | </html>
|
---|
864 |
|
---|
865 | To protect you from excessively large files there is also a limit of 16MiB by
|
---|
866 | default, which you can tweak with the attribute
|
---|
867 | L<Mojolicious/"max_request_size">.
|
---|
868 |
|
---|
869 | # Increase limit to 1GiB
|
---|
870 | app->max_request_size(1073741824);
|
---|
871 |
|
---|
872 | =head2 User agent
|
---|
873 |
|
---|
874 | With L<Mojo::UserAgent>, which is available through the helper
|
---|
875 | L<Mojolicious::Plugin::DefaultHelpers/"ua">, there's a full featured HTTP and
|
---|
876 | WebSocket user agent built right in. Especially in combination with
|
---|
877 | L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool.
|
---|
878 |
|
---|
879 | use Mojolicious::Lite;
|
---|
880 |
|
---|
881 | # Blocking
|
---|
882 | get '/headers' => sub {
|
---|
883 | my $c = shift;
|
---|
884 | my $url = $c->param('url') || 'https://mojolicious.org';
|
---|
885 | my $dom = $c->ua->get($url)->result->dom;
|
---|
886 | $c->render(json => $dom->find('h1, h2, h3')->map('text')->to_array);
|
---|
887 | };
|
---|
888 |
|
---|
889 | # Non-blocking
|
---|
890 | get '/title' => sub {
|
---|
891 | my $c = shift;
|
---|
892 | $c->ua->get('mojolicious.org' => sub {
|
---|
893 | my ($ua, $tx) = @_;
|
---|
894 | $c->render(data => $tx->result->dom->at('title')->text);
|
---|
895 | });
|
---|
896 | };
|
---|
897 |
|
---|
898 | # Concurrent non-blocking
|
---|
899 | get '/titles' => sub {
|
---|
900 | my $c = shift;
|
---|
901 | my $mojo = $c->ua->get_p('https://mojolicious.org');
|
---|
902 | my $cpan = $c->ua->get_p('https://metacpan.org');
|
---|
903 | Mojo::Promise->all($mojo, $cpan)->then(sub {
|
---|
904 | my ($mojo, $cpan) = @_;
|
---|
905 | $c->render(json => {
|
---|
906 | mojo => $mojo->[0]->result->dom->at('title')->text,
|
---|
907 | cpan => $cpan->[0]->result->dom->at('title')->text
|
---|
908 | });
|
---|
909 | })->wait;
|
---|
910 | };
|
---|
911 |
|
---|
912 | app->start;
|
---|
913 |
|
---|
914 | For more information about the user agent see also
|
---|
915 | L<Mojolicious::Guides::Cookbook/"USER AGENT">.
|
---|
916 |
|
---|
917 | =head2 WebSockets
|
---|
918 |
|
---|
919 | WebSocket applications have never been this simple before. Just receive
|
---|
920 | messages by subscribing to events such as
|
---|
921 | L<Mojo::Transaction::WebSocket/"json"> with L<Mojolicious::Controller/"on"> and
|
---|
922 | return them with L<Mojolicious::Controller/"send">.
|
---|
923 |
|
---|
924 | use Mojolicious::Lite;
|
---|
925 |
|
---|
926 | websocket '/echo' => sub {
|
---|
927 | my $c = shift;
|
---|
928 | $c->on(json => sub {
|
---|
929 | my ($c, $hash) = @_;
|
---|
930 | $hash->{msg} = "echo: $hash->{msg}";
|
---|
931 | $c->send({json => $hash});
|
---|
932 | });
|
---|
933 | };
|
---|
934 |
|
---|
935 | get '/' => 'index';
|
---|
936 |
|
---|
937 | app->start;
|
---|
938 | __DATA__
|
---|
939 |
|
---|
940 | @@ index.html.ep
|
---|
941 | <!DOCTYPE html>
|
---|
942 | <html>
|
---|
943 | <head>
|
---|
944 | <title>Echo</title>
|
---|
945 | <script>
|
---|
946 | var ws = new WebSocket('<%= url_for('echo')->to_abs %>');
|
---|
947 | ws.onmessage = function (event) {
|
---|
948 | document.body.innerHTML += JSON.parse(event.data).msg;
|
---|
949 | };
|
---|
950 | ws.onopen = function (event) {
|
---|
951 | ws.send(JSON.stringify({msg: 'I ⥠Mojolicious!'}));
|
---|
952 | };
|
---|
953 | </script>
|
---|
954 | </head>
|
---|
955 | </html>
|
---|
956 |
|
---|
957 | For more information about real-time web features see also
|
---|
958 | L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB">.
|
---|
959 |
|
---|
960 | =head2 Mode
|
---|
961 |
|
---|
962 | You can use the L<Mojo::Log> object from L<Mojolicious/"log"> to portably
|
---|
963 | collect debug messages and automatically disable them later in a production
|
---|
964 | setup by changing the L<Mojolicious> operating mode, which can also be retrieved
|
---|
965 | from the attribute L<Mojolicious/"mode">.
|
---|
966 |
|
---|
967 | use Mojolicious::Lite;
|
---|
968 |
|
---|
969 | # Prepare mode specific message during startup
|
---|
970 | my $msg = app->mode eq 'development' ? 'Development!' : 'Something else!';
|
---|
971 |
|
---|
972 | get '/' => sub {
|
---|
973 | my $c = shift;
|
---|
974 | $c->app->log->debug('Rendering mode specific message');
|
---|
975 | $c->render(text => $msg);
|
---|
976 | };
|
---|
977 |
|
---|
978 | app->log->debug('Starting application');
|
---|
979 | app->start;
|
---|
980 |
|
---|
981 | The default operating mode will usually be C<development> and can be changed
|
---|
982 | with command line options or the C<MOJO_MODE> and C<PLACK_ENV> environment
|
---|
983 | variables. A mode other than C<development> will raise the log level from
|
---|
984 | C<debug> to C<info>.
|
---|
985 |
|
---|
986 | $ ./myapp.pl daemon -m production
|
---|
987 |
|
---|
988 | All messages will be written to C<STDERR> or a C<log/$mode.log> file if a
|
---|
989 | C<log> directory exists.
|
---|
990 |
|
---|
991 | $ mkdir log
|
---|
992 |
|
---|
993 | Mode changes also affect a few other aspects of the framework, such as the
|
---|
994 | built-in C<exception> and C<not_found> pages. Once you switch modes from
|
---|
995 | C<development> to C<production>, no sensitive information will be revealed on
|
---|
996 | those pages anymore.
|
---|
997 |
|
---|
998 | =head2 Testing
|
---|
999 |
|
---|
1000 | Testing your application is as easy as creating a C<t> directory and filling it
|
---|
1001 | with normal Perl tests like C<t/basic.t>, which can be a lot of fun thanks to
|
---|
1002 | L<Test::Mojo>.
|
---|
1003 |
|
---|
1004 | use Test::More;
|
---|
1005 | use Test::Mojo;
|
---|
1006 |
|
---|
1007 | use FindBin;
|
---|
1008 | require "$FindBin::Bin/../myapp.pl";
|
---|
1009 |
|
---|
1010 | my $t = Test::Mojo->new;
|
---|
1011 | $t->get_ok('/')->status_is(200)->content_like(qr/Funky/);
|
---|
1012 |
|
---|
1013 | done_testing();
|
---|
1014 |
|
---|
1015 | Just run your tests with the command L<Mojolicious::Command::test> or L<prove>.
|
---|
1016 |
|
---|
1017 | $ ./myapp.pl test
|
---|
1018 | $ ./myapp.pl test -v t/basic.t
|
---|
1019 | $ prove -l -v t/basic.t
|
---|
1020 |
|
---|
1021 | =head1 MORE
|
---|
1022 |
|
---|
1023 | You can continue with L<Mojolicious::Guides> now or take a look at the
|
---|
1024 | L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
|
---|
1025 | documentation and examples by many different authors.
|
---|
1026 |
|
---|
1027 | =head1 SUPPORT
|
---|
1028 |
|
---|
1029 | If you have any questions the documentation might not yet answer, don't
|
---|
1030 | hesitate to ask on the
|
---|
1031 | L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
|
---|
1032 | channel C<#mojo> on C<irc.perl.org>
|
---|
1033 | (L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
|
---|
1034 |
|
---|
1035 | =cut
|
---|