1 |
|
---|
2 | =encoding utf8
|
---|
3 |
|
---|
4 | =head1 NAME
|
---|
5 |
|
---|
6 | Mojolicious::Guides::Rendering - Rendering content
|
---|
7 |
|
---|
8 | =head1 OVERVIEW
|
---|
9 |
|
---|
10 | This document explains content generation with the L<Mojolicious> renderer.
|
---|
11 |
|
---|
12 | =head1 CONCEPTS
|
---|
13 |
|
---|
14 | Essentials every L<Mojolicious> developer should know.
|
---|
15 |
|
---|
16 | =head2 Renderer
|
---|
17 |
|
---|
18 | The renderer is a tiny black box turning stash data into actual responses
|
---|
19 | utilizing multiple template systems and data encoding modules.
|
---|
20 |
|
---|
21 | {text => 'Hello.'} -> 200 OK, text/html, 'Hello.'
|
---|
22 | {json => {x => 3}} -> 200 OK, application/json, '{"x":3}'
|
---|
23 | {text => 'Oops.', status => '410'} -> 410 Gone, text/html, 'Oops.'
|
---|
24 |
|
---|
25 | Templates can be automatically detected if enough information is provided by
|
---|
26 | the developer or routes. Template names are expected to follow the
|
---|
27 | C<template.format.handler> scheme, with C<template> defaulting to
|
---|
28 | C<controller/action> or the route name, C<format> defaulting to C<html> and
|
---|
29 | C<handler> to C<ep>.
|
---|
30 |
|
---|
31 | {controller => 'users', action => 'list'} -> 'users/list.html.ep'
|
---|
32 | {template => 'foo', format => 'txt'} -> 'foo.txt.ep'
|
---|
33 | {template => 'foo', handler => 'epl'} -> 'foo.html.epl'
|
---|
34 |
|
---|
35 | The C<controller> value gets converted from C<CamelCase> to C<snake_case> using
|
---|
36 | L<Mojo::Util/"decamelize"> and C<-> characters replaced with C</>.
|
---|
37 |
|
---|
38 | {controller => 'My::Users', action => 'add'} -> 'my/users/add.html.ep'
|
---|
39 | {controller => 'my-users', action => 'show'} -> 'my/users/show.html.ep'
|
---|
40 |
|
---|
41 | All templates should be in the C<templates> directories of the application,
|
---|
42 | which can be customized with L<Mojolicious::Renderer/"paths">, or one of the
|
---|
43 | the C<DATA> sections from L<Mojolicious::Renderer/"classes">.
|
---|
44 |
|
---|
45 | __DATA__
|
---|
46 |
|
---|
47 | @@ time.html.ep
|
---|
48 | % use Time::Piece;
|
---|
49 | % my $now = localtime;
|
---|
50 | <!DOCTYPE html>
|
---|
51 | <html>
|
---|
52 | <head><title>Time</title></head>
|
---|
53 | <body>The time is <%= $now->hms %>.</body>
|
---|
54 | </html>
|
---|
55 |
|
---|
56 | @@ hello.txt.ep
|
---|
57 | ...
|
---|
58 |
|
---|
59 | The renderer can be easily extended to support additional template systems with
|
---|
60 | plugins, but more about that later.
|
---|
61 |
|
---|
62 | =head2 Embedded Perl
|
---|
63 |
|
---|
64 | L<Mojolicious> includes a minimalistic but very powerful template system out of
|
---|
65 | the box called Embedded Perl or C<ep> for short. It is based on
|
---|
66 | L<Mojo::Template> and allows the embedding of Perl code right into actual
|
---|
67 | content using a small set of special tags and line start characters. For all
|
---|
68 | templates L<strict>, L<warnings>, L<utf8> and Perl 5.10 L<features|feature> are
|
---|
69 | automatically enabled.
|
---|
70 |
|
---|
71 | <% Perl code %>
|
---|
72 | <%= Perl expression, replaced with XML escaped result %>
|
---|
73 | <%== Perl expression, replaced with result %>
|
---|
74 | <%# Comment, useful for debugging %>
|
---|
75 | <%% Replaced with "<%", useful for generating templates %>
|
---|
76 | % Perl code line, treated as "<% line =%>" (explained later)
|
---|
77 | %= Perl expression line, treated as "<%= line %>"
|
---|
78 | %== Perl expression line, treated as "<%== line %>"
|
---|
79 | %# Comment line, useful for debugging
|
---|
80 | %% Replaced with "%", useful for generating templates
|
---|
81 |
|
---|
82 | Tags and lines work pretty much the same, but depending on context one will
|
---|
83 | usually look a bit better. Semicolons get automatically appended to all
|
---|
84 | expressions.
|
---|
85 |
|
---|
86 | <% my $i = 10; %>
|
---|
87 | <ul>
|
---|
88 | <% for my $j (1 .. $i) { %>
|
---|
89 | <li>
|
---|
90 | <%= $j %>
|
---|
91 | </li>
|
---|
92 | <% } %>
|
---|
93 | </ul>
|
---|
94 |
|
---|
95 | % my $i = 10;
|
---|
96 | <ul>
|
---|
97 | % for my $j (1 .. $i) {
|
---|
98 | <li>
|
---|
99 | %= $j
|
---|
100 | </li>
|
---|
101 | % }
|
---|
102 | </ul>
|
---|
103 |
|
---|
104 | Aside from differences in whitespace handling, both examples generate similar
|
---|
105 | Perl code, a naive translation could look like this.
|
---|
106 |
|
---|
107 | my $output = '';
|
---|
108 | my $i = 10;
|
---|
109 | $output .= '<ul>';
|
---|
110 | for my $j (1 .. $i) {
|
---|
111 | $output .= '<li>';
|
---|
112 | $output .= xml_escape scalar + $j;
|
---|
113 | $output .= '</li>';
|
---|
114 | }
|
---|
115 | $output .= '</ul>';
|
---|
116 | return $output;
|
---|
117 |
|
---|
118 | An additional equal sign can be used to disable escaping of the characters
|
---|
119 | C<E<lt>>, C<E<gt>>, C<&>, C<'> and C<"> in results from Perl expressions, which
|
---|
120 | is the default to prevent XSS attacks against your application.
|
---|
121 |
|
---|
122 | <%= 'I ⥠Mojolicious!' %>
|
---|
123 | <%== '<p>I ⥠Mojolicious!</p>' %>
|
---|
124 |
|
---|
125 | Only L<Mojo::ByteStream> objects are excluded from automatic escaping.
|
---|
126 |
|
---|
127 | <%= b('<p>I ⥠Mojolicious!</p>') %>
|
---|
128 |
|
---|
129 | Whitespace characters around tags can be trimmed by adding an additional equal
|
---|
130 | sign to the end of a tag.
|
---|
131 |
|
---|
132 | <% for (1 .. 3) { %>
|
---|
133 | <%= 'Trim all whitespace characters around this expression' =%>
|
---|
134 | <% } %>
|
---|
135 |
|
---|
136 | Newline characters can be escaped with a backslash.
|
---|
137 |
|
---|
138 | This is <%= 1 + 1 %> a\
|
---|
139 | single line
|
---|
140 |
|
---|
141 | And a backslash in front of a newline character can be escaped with another
|
---|
142 | backslash.
|
---|
143 |
|
---|
144 | This will <%= 1 + 1 %> result\\
|
---|
145 | in multiple\\
|
---|
146 | lines
|
---|
147 |
|
---|
148 | A newline character gets appended automatically to every template, unless the
|
---|
149 | last character is a backslash. And empty lines at the end of a template are
|
---|
150 | ignored.
|
---|
151 |
|
---|
152 | There is <%= 1 + 1 %> no newline at the end here\
|
---|
153 |
|
---|
154 | At the beginning of the template, stash values that don't have invalid
|
---|
155 | characters in their name get automatically initialized as normal variables, and
|
---|
156 | the controller object as both C<$self> and C<$c>.
|
---|
157 |
|
---|
158 | $c->stash(name => 'tester');
|
---|
159 |
|
---|
160 | Hello <%= $name %> from <%= $c->tx->remote_address %>.
|
---|
161 |
|
---|
162 | A prefix like C<myapp.*> is commonly used for stash values that you don't want
|
---|
163 | to expose in templates.
|
---|
164 |
|
---|
165 | $c->stash('myapp.name' => 'tester');
|
---|
166 |
|
---|
167 | There are also many helper functions available, but more about that later.
|
---|
168 |
|
---|
169 | <%= dumper {foo => 'bar'} %>
|
---|
170 |
|
---|
171 | =head1 BASICS
|
---|
172 |
|
---|
173 | Most commonly used features every L<Mojolicious> developer should know about.
|
---|
174 |
|
---|
175 | =head2 Automatic rendering
|
---|
176 |
|
---|
177 | The renderer can be manually started by calling the method
|
---|
178 | L<Mojolicious::Controller/"render">, but that's usually not necessary, because
|
---|
179 | it will get automatically called if nothing has been rendered after the router
|
---|
180 | finished its work. This also means you can have routes pointing only to
|
---|
181 | templates without actual actions.
|
---|
182 |
|
---|
183 | $c->render;
|
---|
184 |
|
---|
185 | There is one big difference though, by calling it manually you can make sure
|
---|
186 | that templates use the current controller object, and not the default
|
---|
187 | controller specified with the attribute L<Mojolicious/"controller_class">.
|
---|
188 |
|
---|
189 | $c->render_later;
|
---|
190 |
|
---|
191 | You can also disable automatic rendering with the method
|
---|
192 | L<Mojolicious::Controller/"render_later">, which can be very useful to delay
|
---|
193 | rendering when a non-blocking operation has to be performed first.
|
---|
194 |
|
---|
195 | =head2 Rendering templates
|
---|
196 |
|
---|
197 | The renderer will always try to detect the right template, but you can also use
|
---|
198 | the C<template> stash value to render a specific one. Everything before the
|
---|
199 | last slash will be interpreted as the subdirectory path in which to find the
|
---|
200 | template.
|
---|
201 |
|
---|
202 | # foo/bar/baz.*.*
|
---|
203 | $c->render(template => 'foo/bar/baz');
|
---|
204 |
|
---|
205 | Choosing a specific C<format> and C<handler> is just as easy.
|
---|
206 |
|
---|
207 | # foo/bar/baz.txt.epl
|
---|
208 | $c->render(template => 'foo/bar/baz', format => 'txt', handler => 'epl');
|
---|
209 |
|
---|
210 | Because rendering a specific template is the most common task it also has a
|
---|
211 | shortcut.
|
---|
212 |
|
---|
213 | $c->render('foo/bar/baz');
|
---|
214 |
|
---|
215 | If you're not sure in advance if a template actually exists, you can also use
|
---|
216 | the method L<Mojolicious::Controller/"render_maybe"> to try multiple
|
---|
217 | alternatives.
|
---|
218 |
|
---|
219 | $c->render_maybe('localized/baz') or $c->render('foo/bar/baz');
|
---|
220 |
|
---|
221 | =head2 Rendering to strings
|
---|
222 |
|
---|
223 | Sometimes you might want to use the rendered result directly instead of
|
---|
224 | generating a response, for example, to send emails, this can be done with
|
---|
225 | L<Mojolicious::Controller/"render_to_string">.
|
---|
226 |
|
---|
227 | my $html = $c->render_to_string('mail');
|
---|
228 |
|
---|
229 | No encoding will be performed, making it easy to reuse the result in other
|
---|
230 | templates or to generate binary data.
|
---|
231 |
|
---|
232 | my $pdf = $c->render_to_string('invoice', format => 'pdf');
|
---|
233 | $c->render(data => $pdf, format => 'pdf');
|
---|
234 |
|
---|
235 | All arguments passed will get localized automatically and are only available
|
---|
236 | during this render operation.
|
---|
237 |
|
---|
238 | =head2 Template variants
|
---|
239 |
|
---|
240 | To make your application look great on many different devices you can also use
|
---|
241 | the C<variant> stash value to choose between different variants of your
|
---|
242 | templates.
|
---|
243 |
|
---|
244 | # foo/bar/baz.html+phone.ep
|
---|
245 | # foo/bar/baz.html.ep
|
---|
246 | $c->render('foo/bar/baz', variant => 'phone');
|
---|
247 |
|
---|
248 | This can be done very liberally since it only applies when a template with the
|
---|
249 | correct name actually exists and falls back to the generic one otherwise.
|
---|
250 |
|
---|
251 | =head2 Rendering inline templates
|
---|
252 |
|
---|
253 | Some renderers such as C<ep> allow templates to be passed C<inline>.
|
---|
254 |
|
---|
255 | $c->render(inline => 'The result is <%= 1 + 1 %>.');
|
---|
256 |
|
---|
257 | Since auto-detection depends on a path you might have to supply a C<handler>
|
---|
258 | too.
|
---|
259 |
|
---|
260 | $c->render(inline => "<%= shift->param('foo') %>", handler => 'epl');
|
---|
261 |
|
---|
262 | =head2 Rendering text
|
---|
263 |
|
---|
264 | Characters can be rendered to bytes with the C<text> stash value, the given
|
---|
265 | content will be automatically encoded with L<Mojolicious::Renderer/"encoding">.
|
---|
266 |
|
---|
267 | $c->render(text => 'I ⥠Mojolicious!');
|
---|
268 |
|
---|
269 | =head2 Rendering data
|
---|
270 |
|
---|
271 | Bytes can be rendered with the C<data> stash value, no encoding will be
|
---|
272 | performed.
|
---|
273 |
|
---|
274 | $c->render(data => $bytes);
|
---|
275 |
|
---|
276 | =head2 Rendering JSON
|
---|
277 |
|
---|
278 | The C<json> stash value allows you to pass Perl data structures to the renderer
|
---|
279 | which get directly encoded to JSON with L<Mojo::JSON>.
|
---|
280 |
|
---|
281 | $c->render(json => {foo => [1, 'test', 3]});
|
---|
282 |
|
---|
283 | =head2 Status code
|
---|
284 |
|
---|
285 | Response status codes can be changed with the C<status> stash value.
|
---|
286 |
|
---|
287 | $c->render(text => 'Oops.', status => 500);
|
---|
288 |
|
---|
289 | =head2 Content type
|
---|
290 |
|
---|
291 | The C<Content-Type> header of the response is actually based on the MIME type
|
---|
292 | mapping of the C<format> stash value.
|
---|
293 |
|
---|
294 | # Content-Type: text/plain
|
---|
295 | $c->render(text => 'Hello.', format => 'txt');
|
---|
296 |
|
---|
297 | # Content-Type: image/png
|
---|
298 | $c->render(data => $bytes, format => 'png');
|
---|
299 |
|
---|
300 | These mappings can be easily extended or changed with L<Mojolicious/"types">.
|
---|
301 |
|
---|
302 | # Add new MIME type
|
---|
303 | $app->types->type(md => 'text/markdown');
|
---|
304 |
|
---|
305 | =head2 Stash data
|
---|
306 |
|
---|
307 | Any of the native Perl data types can be passed to templates as references
|
---|
308 | through the L<Mojolicious::Controller/"stash">.
|
---|
309 |
|
---|
310 | $c->stash(description => 'web framework');
|
---|
311 | $c->stash(frameworks => ['Catalyst', 'Mojolicious']);
|
---|
312 | $c->stash(spinoffs => {minion => 'job queue'});
|
---|
313 |
|
---|
314 | %= $description
|
---|
315 | %= $frameworks->[1]
|
---|
316 | %= $spinoffs->{minion}
|
---|
317 |
|
---|
318 | Since everything is just Perl normal control structures just work.
|
---|
319 |
|
---|
320 | % for my $framework (@$frameworks) {
|
---|
321 | <%= $framework %> is a <%= $description %>.
|
---|
322 | % }
|
---|
323 |
|
---|
324 | % if (my $description = $spinoffs->{minion}) {
|
---|
325 | Minion is a <%= $description %>.
|
---|
326 | % }
|
---|
327 |
|
---|
328 | For templates that might get rendered in different ways and where you're not
|
---|
329 | sure if a stash value will actually be set, you can just use the helper
|
---|
330 | L<Mojolicious::Plugin::DefaultHelpers/"stash">.
|
---|
331 |
|
---|
332 | % if (my $spinoffs = stash 'spinoffs') {
|
---|
333 | Minion is a <%= $spinoffs->{minion} %>.
|
---|
334 | % }
|
---|
335 |
|
---|
336 | =head2 Helpers
|
---|
337 |
|
---|
338 | Helpers are little functions you can use in templates as well as application
|
---|
339 | and controller code.
|
---|
340 |
|
---|
341 | # Template
|
---|
342 | %= dumper [1, 2, 3]
|
---|
343 |
|
---|
344 | # Application
|
---|
345 | my $serialized = $app->dumper([1, 2, 3]);
|
---|
346 |
|
---|
347 | # Controller
|
---|
348 | my $serialized = $c->dumper([1, 2, 3]);
|
---|
349 |
|
---|
350 | We differentiate between default helpers, which are more general purpose like
|
---|
351 | L<Mojolicious::Plugin::DefaultHelpers/"dumper">, and tag helpers like
|
---|
352 | L<Mojolicious::Plugin::TagHelpers/"link_to">, which are template specific and
|
---|
353 | mostly used to generate HTML tags.
|
---|
354 |
|
---|
355 | %= link_to Mojolicious => 'https://mojolicious.org'
|
---|
356 |
|
---|
357 | In controllers you can also use the method L<Mojolicious::Controller/"helpers">
|
---|
358 | to fully qualify helper calls and ensure that they don't conflict with existing
|
---|
359 | methods you may already have.
|
---|
360 |
|
---|
361 | my $serialized = $c->helpers->dumper([1, 2, 3]);
|
---|
362 |
|
---|
363 | A list of all built-in helpers can be found in
|
---|
364 | L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
|
---|
365 |
|
---|
366 | =head2 Content negotiation
|
---|
367 |
|
---|
368 | For resources with different representations and that require truly RESTful
|
---|
369 | content negotiation you can also use L<Mojolicious::Controller/"respond_to">
|
---|
370 | instead of L<Mojolicious::Controller/"render">.
|
---|
371 |
|
---|
372 | # /hello (Accept: application/json) -> "json"
|
---|
373 | # /hello (Accept: application/xml) -> "xml"
|
---|
374 | # /hello.json -> "json"
|
---|
375 | # /hello.xml -> "xml"
|
---|
376 | # /hello?format=json -> "json"
|
---|
377 | # /hello?format=xml -> "xml"
|
---|
378 | $c->respond_to(
|
---|
379 | json => {json => {hello => 'world'}},
|
---|
380 | xml => {text => '<hello>world</hello>'}
|
---|
381 | );
|
---|
382 |
|
---|
383 | The best possible representation will be automatically selected from the
|
---|
384 | C<format> C<GET>/C<POST> parameter, C<format> stash value or C<Accept> request
|
---|
385 | header and stored in the C<format> stash value. To change MIME type mappings for
|
---|
386 | the C<Accept> request header or the C<Content-Type> response header you can use
|
---|
387 | L<Mojolicious/"types">.
|
---|
388 |
|
---|
389 | $c->respond_to(
|
---|
390 | json => {json => {hello => 'world'}},
|
---|
391 | html => sub {
|
---|
392 | $c->content_for(head => '<meta name="author" content="sri">');
|
---|
393 | $c->render(template => 'hello', message => 'world')
|
---|
394 | }
|
---|
395 | );
|
---|
396 |
|
---|
397 | Callbacks can be used for representations that are too complex to fit into a
|
---|
398 | single render call.
|
---|
399 |
|
---|
400 | # /hello (Accept: application/json) -> "json"
|
---|
401 | # /hello (Accept: text/html) -> "html"
|
---|
402 | # /hello (Accept: image/png) -> "any"
|
---|
403 | # /hello.json -> "json"
|
---|
404 | # /hello.html -> "html"
|
---|
405 | # /hello.png -> "any"
|
---|
406 | # /hello?format=json -> "json"
|
---|
407 | # /hello?format=html -> "html"
|
---|
408 | # /hello?format=png -> "any"
|
---|
409 | $c->respond_to(
|
---|
410 | json => {json => {hello => 'world'}},
|
---|
411 | html => {template => 'hello', message => 'world'},
|
---|
412 | any => {text => '', status => 204}
|
---|
413 | );
|
---|
414 |
|
---|
415 | And if no viable representation could be found, the C<any> fallback will be
|
---|
416 | used or an empty C<204> response rendered automatically.
|
---|
417 |
|
---|
418 | # /hello -> "html"
|
---|
419 | # /hello (Accept: text/html) -> "html"
|
---|
420 | # /hello (Accept: text/xml) -> "xml"
|
---|
421 | # /hello (Accept: text/plain) -> undef
|
---|
422 | # /hello.html -> "html"
|
---|
423 | # /hello.xml -> "xml"
|
---|
424 | # /hello.txt -> undef
|
---|
425 | # /hello?format=html -> "html"
|
---|
426 | # /hello?format=xml -> "xml"
|
---|
427 | # /hello?format=txt -> undef
|
---|
428 | if (my $format = $c->accepts('html', 'xml')) {
|
---|
429 | ...
|
---|
430 | }
|
---|
431 |
|
---|
432 | For even more advanced negotiation logic you can also use the helper
|
---|
433 | L<Mojolicious::Plugin::DefaultHelpers/"accepts">.
|
---|
434 |
|
---|
435 | =head2 Rendering C<exception> and C<not_found> pages
|
---|
436 |
|
---|
437 | By now you've probably already encountered the built-in C<404> (Not Found) and
|
---|
438 | C<500> (Server Error) pages, that get rendered automatically when you make a
|
---|
439 | mistake. Those are fallbacks for when your own exception handling fails, which
|
---|
440 | can be especially helpful during development. You can also render them manually
|
---|
441 | with the helpers L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception">
|
---|
442 | and L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found">.
|
---|
443 |
|
---|
444 | use Mojolicious::Lite;
|
---|
445 | use Scalar::Util 'looks_like_number';
|
---|
446 |
|
---|
447 | get '/divide/:dividend/by/:divisor' => sub {
|
---|
448 | my $c = shift;
|
---|
449 |
|
---|
450 | my $dividend = $c->param('dividend');
|
---|
451 | my $divisor = $c->param('divisor');
|
---|
452 |
|
---|
453 | # 404
|
---|
454 | return $c->reply->not_found
|
---|
455 | unless looks_like_number $dividend && looks_like_number $divisor;
|
---|
456 |
|
---|
457 | # 500
|
---|
458 | return $c->reply->exception('Division by zero!') if $divisor == 0;
|
---|
459 |
|
---|
460 | # 200
|
---|
461 | $c->render(text => $dividend / $divisor);
|
---|
462 | };
|
---|
463 |
|
---|
464 | app->start;
|
---|
465 |
|
---|
466 | You can also change the templates of those pages, since you most likely want to
|
---|
467 | show your users something more closely related to your application in
|
---|
468 | production. The renderer will always try to find C<exception.$mode.$format.*>
|
---|
469 | or C<not_found.$mode.$format.*> before falling back to the built-in default
|
---|
470 | templates.
|
---|
471 |
|
---|
472 | use Mojolicious::Lite;
|
---|
473 |
|
---|
474 | get '/dies' => sub { die 'Intentional error' };
|
---|
475 |
|
---|
476 | app->start;
|
---|
477 | __DATA__
|
---|
478 |
|
---|
479 | @@ exception.production.html.ep
|
---|
480 | <!DOCTYPE html>
|
---|
481 | <html>
|
---|
482 | <head><title>Server error</title></head>
|
---|
483 | <body>
|
---|
484 | <h1>Exception</h1>
|
---|
485 | <p><%= $exception->message %></p>
|
---|
486 | <h1>Stash</h1>
|
---|
487 | <pre><%= dumper $snapshot %></pre>
|
---|
488 | </body>
|
---|
489 | </html>
|
---|
490 |
|
---|
491 | The hook L<Mojolicious/"before_render"> makes even more advanced customizations
|
---|
492 | possible by allowing you to intercept and modify the arguments passed to the
|
---|
493 | renderer.
|
---|
494 |
|
---|
495 | use Mojolicious::Lite;
|
---|
496 |
|
---|
497 | hook before_render => sub {
|
---|
498 | my ($c, $args) = @_;
|
---|
499 |
|
---|
500 | # Make sure we are rendering the exception template
|
---|
501 | return unless my $template = $args->{template};
|
---|
502 | return unless $template eq 'exception';
|
---|
503 |
|
---|
504 | # Switch to JSON rendering if content negotiation allows it
|
---|
505 | $args->{json} = {exception => $args->{exception}} if $c->accepts('json');
|
---|
506 | };
|
---|
507 |
|
---|
508 | get '/' => sub { die "This sho...ALL GLORY TO THE HYPNOTOAD!\n" };
|
---|
509 |
|
---|
510 | app->start;
|
---|
511 |
|
---|
512 | =head2 Layouts
|
---|
513 |
|
---|
514 | Most of the time when using C<ep> templates you will want to wrap your
|
---|
515 | generated content in an HTML skeleton, thanks to layouts that's absolutely
|
---|
516 | trivial.
|
---|
517 |
|
---|
518 | use Mojolicious::Lite;
|
---|
519 |
|
---|
520 | get '/' => {template => 'foo/bar'};
|
---|
521 |
|
---|
522 | app->start;
|
---|
523 | __DATA__
|
---|
524 |
|
---|
525 | @@ foo/bar.html.ep
|
---|
526 | % layout 'mylayout';
|
---|
527 | Hello World!
|
---|
528 |
|
---|
529 | @@ layouts/mylayout.html.ep
|
---|
530 | <!DOCTYPE html>
|
---|
531 | <html>
|
---|
532 | <head><title>MyApp</title></head>
|
---|
533 | <body><%= content %></body>
|
---|
534 | </html>
|
---|
535 |
|
---|
536 | You just select the right layout template with the helper
|
---|
537 | L<Mojolicious::Plugin::DefaultHelpers/"layout"> and place the result of the
|
---|
538 | current template with the helper
|
---|
539 | L<Mojolicious::Plugin::DefaultHelpers/"content">. You can also pass along
|
---|
540 | normal stash values to the C<layout> helper.
|
---|
541 |
|
---|
542 | use Mojolicious::Lite;
|
---|
543 |
|
---|
544 | get '/' => {template => 'foo/bar'};
|
---|
545 |
|
---|
546 | app->start;
|
---|
547 | __DATA__
|
---|
548 |
|
---|
549 | @@ foo/bar.html.ep
|
---|
550 | % layout 'mylayout', title => 'Hi there';
|
---|
551 | Hello World!
|
---|
552 |
|
---|
553 | @@ layouts/mylayout.html.ep
|
---|
554 | <!DOCTYPE html>
|
---|
555 | <html>
|
---|
556 | <head><title><%= $title %></title></head>
|
---|
557 | <body><%= content %></body>
|
---|
558 | </html>
|
---|
559 |
|
---|
560 | Instead of the C<layout> helper you could also just use the C<layout> stash
|
---|
561 | value, or call L<Mojolicious::Controller/"render"> with the C<layout> argument.
|
---|
562 |
|
---|
563 | $c->render(template => 'mytemplate', layout => 'mylayout');
|
---|
564 |
|
---|
565 | To set a C<layout> stash value application-wide you can use
|
---|
566 | L<Mojolicious/"defaults">.
|
---|
567 |
|
---|
568 | $app->defaults(layout => 'mylayout');
|
---|
569 |
|
---|
570 | Layouts can also be used with L<Mojolicious::Controller/"render_to_string">,
|
---|
571 | but the C<layout> value needs to be passed as a render argument (not a stash
|
---|
572 | value).
|
---|
573 |
|
---|
574 | my $html = $c->render_to_string('reminder', layout => 'mail');
|
---|
575 |
|
---|
576 | =head2 Partial templates
|
---|
577 |
|
---|
578 | You can break up bigger templates into smaller, more manageable chunks. These
|
---|
579 | partial templates can also be shared with other templates. Just use the helper
|
---|
580 | L<Mojolicious::Plugin::DefaultHelpers/"include"> to include one template into
|
---|
581 | another.
|
---|
582 |
|
---|
583 | use Mojolicious::Lite;
|
---|
584 |
|
---|
585 | get '/' => {template => 'foo/bar'};
|
---|
586 |
|
---|
587 | app->start;
|
---|
588 | __DATA__
|
---|
589 |
|
---|
590 | @@ foo/bar.html.ep
|
---|
591 | <!DOCTYPE html>
|
---|
592 | <html>
|
---|
593 | %= include '_header', title => 'Howdy'
|
---|
594 | <body>Bar</body>
|
---|
595 | </html>
|
---|
596 |
|
---|
597 | @@ _header.html.ep
|
---|
598 | <head><title><%= $title %></title></head>
|
---|
599 |
|
---|
600 | You can name partial templates however you like, but a leading underscore is a
|
---|
601 | commonly used naming convention.
|
---|
602 |
|
---|
603 | =head2 Reusable template blocks
|
---|
604 |
|
---|
605 | It's never fun to repeat yourself, that's why you can build reusable template
|
---|
606 | blocks in C<ep> that work very similar to normal Perl functions, with the
|
---|
607 | C<begin> and C<end> keywords. Just be aware that both keywords are part of the
|
---|
608 | surrounding tag and not actual Perl code, so there can only be whitespace after
|
---|
609 | C<begin> and before C<end>.
|
---|
610 |
|
---|
611 | use Mojolicious::Lite;
|
---|
612 |
|
---|
613 | get '/' => 'welcome';
|
---|
614 |
|
---|
615 | app->start;
|
---|
616 | __DATA__
|
---|
617 |
|
---|
618 | @@ welcome.html.ep
|
---|
619 | <% my $block = begin %>
|
---|
620 | % my $name = shift;
|
---|
621 | Hello <%= $name %>.
|
---|
622 | <% end %>
|
---|
623 | <%= $block->('Wolfgang') %>
|
---|
624 | <%= $block->('Baerbel') %>
|
---|
625 |
|
---|
626 | A naive translation of the template to Perl code could look like this.
|
---|
627 |
|
---|
628 | my $output = '';
|
---|
629 | my $block = sub {
|
---|
630 | my $name = shift;
|
---|
631 | my $output = '';
|
---|
632 | $output .= 'Hello ';
|
---|
633 | $output .= xml_escape scalar + $name;
|
---|
634 | $output .= '.';
|
---|
635 | return Mojo::ByteStream->new($output);
|
---|
636 | };
|
---|
637 | $output .= xml_escape scalar + $block->('Wolfgang');
|
---|
638 | $output .= xml_escape scalar + $block->('Baerbel');
|
---|
639 | return $output;
|
---|
640 |
|
---|
641 | While template blocks cannot be shared between templates, they are most
|
---|
642 | commonly used to pass parts of a template to helpers.
|
---|
643 |
|
---|
644 | =head2 Adding helpers
|
---|
645 |
|
---|
646 | You should always try to keep your actions small and reuse as much code as
|
---|
647 | possible. Helpers make this very easy, they get passed the current controller
|
---|
648 | object as first argument, and you can use them to do pretty much anything an
|
---|
649 | action could do.
|
---|
650 |
|
---|
651 | use Mojolicious::Lite;
|
---|
652 |
|
---|
653 | helper debug => sub {
|
---|
654 | my ($c, $str) = @_;
|
---|
655 | $c->app->log->debug($str);
|
---|
656 | };
|
---|
657 |
|
---|
658 | get '/' => sub {
|
---|
659 | my $c = shift;
|
---|
660 | $c->debug('Hello from an action!');
|
---|
661 | } => 'index';
|
---|
662 |
|
---|
663 | app->start;
|
---|
664 | __DATA__
|
---|
665 |
|
---|
666 | @@ index.html.ep
|
---|
667 | % debug 'Hello from a template!';
|
---|
668 |
|
---|
669 | Helpers can also accept template blocks as last argument, this for example,
|
---|
670 | allows very pleasant to use tag helpers and filters. Wrapping the helper result
|
---|
671 | into a L<Mojo::ByteStream> object can prevent accidental double escaping.
|
---|
672 |
|
---|
673 | use Mojolicious::Lite;
|
---|
674 | use Mojo::ByteStream;
|
---|
675 |
|
---|
676 | helper trim_newline => sub {
|
---|
677 | my ($c, $block) = @_;
|
---|
678 | my $result = $block->();
|
---|
679 | $result =~ s/\n//g;
|
---|
680 | return Mojo::ByteStream->new($result);
|
---|
681 | };
|
---|
682 |
|
---|
683 | get '/' => 'index';
|
---|
684 |
|
---|
685 | app->start;
|
---|
686 | __DATA__
|
---|
687 |
|
---|
688 | @@ index.html.ep
|
---|
689 | %= trim_newline begin
|
---|
690 | Some text.
|
---|
691 | %= 1 + 1
|
---|
692 | More text.
|
---|
693 | % end
|
---|
694 |
|
---|
695 | Similar to stash values, you can use a prefix like C<myapp.*> to keep helpers
|
---|
696 | from getting exposed in templates as functions, and to organize them into
|
---|
697 | namespaces as your application grows. Every prefix automatically becomes a
|
---|
698 | helper that returns a proxy object containing the current controller object and
|
---|
699 | on which you can call the nested helpers.
|
---|
700 |
|
---|
701 | use Mojolicious::Lite;
|
---|
702 |
|
---|
703 | helper 'cache_control.no_caching' => sub {
|
---|
704 | my $c = shift;
|
---|
705 | $c->res->headers->cache_control('private, max-age=0, no-cache');
|
---|
706 | };
|
---|
707 |
|
---|
708 | helper 'cache_control.five_minutes' => sub {
|
---|
709 | my $c = shift;
|
---|
710 | $c->res->headers->cache_control('public, max-age=300');
|
---|
711 | };
|
---|
712 |
|
---|
713 | get '/news' => sub {
|
---|
714 | my $c = shift;
|
---|
715 | $c->cache_control->no_caching;
|
---|
716 | $c->render(text => 'Always up to date.');
|
---|
717 | };
|
---|
718 |
|
---|
719 | get '/some_older_story' => sub {
|
---|
720 | my $c = shift;
|
---|
721 | $c->cache_control->five_minutes;
|
---|
722 | $c->render(text => 'This one can be cached for a bit.');
|
---|
723 | };
|
---|
724 |
|
---|
725 | app->start;
|
---|
726 |
|
---|
727 | While helpers can also be redefined, this should only be done very carefully to
|
---|
728 | avoid conflicts.
|
---|
729 |
|
---|
730 | =head2 Content blocks
|
---|
731 |
|
---|
732 | The helper L<Mojolicious::Plugin::DefaultHelpers/"content_for"> allows you to
|
---|
733 | pass whole blocks of content from one template to another. This can be very
|
---|
734 | useful when your layout has distinct sections, such as sidebars, where content
|
---|
735 | should be inserted by the template.
|
---|
736 |
|
---|
737 | use Mojolicious::Lite;
|
---|
738 |
|
---|
739 | get '/' => 'foo';
|
---|
740 |
|
---|
741 | app->start;
|
---|
742 | __DATA__
|
---|
743 |
|
---|
744 | @@ foo.html.ep
|
---|
745 | % layout 'mylayout';
|
---|
746 | % content_for header => begin
|
---|
747 | <meta http-equiv="Content-Type" content="text/html">
|
---|
748 | % end
|
---|
749 | <div>Hello World!</div>
|
---|
750 | % content_for header => begin
|
---|
751 | <meta http-equiv="Pragma" content="no-cache">
|
---|
752 | % end
|
---|
753 |
|
---|
754 | @@ layouts/mylayout.html.ep
|
---|
755 | <!DOCTYPE html>
|
---|
756 | <html>
|
---|
757 | <head><%= content 'header' %></head>
|
---|
758 | <body><%= content %></body>
|
---|
759 | </html>
|
---|
760 |
|
---|
761 | =head2 Forms
|
---|
762 |
|
---|
763 | To build HTML forms more efficiently you can use tag helpers like
|
---|
764 | L<Mojolicious::Plugin::TagHelpers/"form_for">, which can automatically select a
|
---|
765 | request method for you if a route name is provided. And since most browsers
|
---|
766 | only allow forms to be submitted with C<GET> and C<POST>, but not request
|
---|
767 | methods like C<PUT> or C<DELETE>, they are spoofed with an C<_method> query
|
---|
768 | parameter.
|
---|
769 |
|
---|
770 | use Mojolicious::Lite;
|
---|
771 |
|
---|
772 | get '/' => 'form';
|
---|
773 |
|
---|
774 | # PUT /nothing
|
---|
775 | # POST /nothing?_method=PUT
|
---|
776 | put '/nothing' => sub {
|
---|
777 | my $c = shift;
|
---|
778 |
|
---|
779 | # Prevent double form submission with redirect
|
---|
780 | my $value = $c->param('whatever');
|
---|
781 | $c->flash(confirmation => "We did nothing with your value ($value).");
|
---|
782 | $c->redirect_to('form');
|
---|
783 | };
|
---|
784 |
|
---|
785 | app->start;
|
---|
786 | __DATA__
|
---|
787 |
|
---|
788 | @@ form.html.ep
|
---|
789 | <!DOCTYPE html>
|
---|
790 | <html>
|
---|
791 | <body>
|
---|
792 | % if (my $confirmation = flash 'confirmation') {
|
---|
793 | <p><%= $confirmation %></p>
|
---|
794 | % }
|
---|
795 | %= form_for nothing => begin
|
---|
796 | %= text_field whatever => 'I ⥠Mojolicious!'
|
---|
797 | %= submit_button
|
---|
798 | % end
|
---|
799 | </body>
|
---|
800 | </html>
|
---|
801 |
|
---|
802 | The methods L<Mojolicious::Controller/"flash"> and
|
---|
803 | L<Mojolicious::Controller/"redirect_to"> are often used together to prevent
|
---|
804 | double form submission, allowing users to receive a confirmation message that
|
---|
805 | will vanish if they decide to reload the page they've been redirected to.
|
---|
806 |
|
---|
807 | =head2 Form validation
|
---|
808 |
|
---|
809 | You can use L<Mojolicious::Controller/"validation"> to validate C<GET> and
|
---|
810 | C<POST> parameters submitted to your application. All unknown fields will be
|
---|
811 | ignored by default, so you have to decide which should be required or optional
|
---|
812 | before you can perform checks on their values. Every check is performed right
|
---|
813 | away, so you can use the results immediately to build more advanced validation
|
---|
814 | logic with methods like L<Mojolicious::Validator::Validation/"is_valid">.
|
---|
815 |
|
---|
816 | use Mojolicious::Lite;
|
---|
817 |
|
---|
818 | get '/' => sub {
|
---|
819 | my $c = shift;
|
---|
820 |
|
---|
821 | # Check if parameters have been submitted
|
---|
822 | my $v = $c->validation;
|
---|
823 | return $c->render unless $v->has_data;
|
---|
824 |
|
---|
825 | # Validate parameters ("pass_again" depends on "pass")
|
---|
826 | $v->required('user')->size(1, 20)->like(qr/^[a-z0-9]+$/);
|
---|
827 | $v->required('pass_again')->equal_to('pass')
|
---|
828 | if $v->optional('pass')->size(7, 500)->is_valid;
|
---|
829 |
|
---|
830 | # Render confirmation if validation was successful
|
---|
831 | $c->render('thanks') unless $v->has_error;
|
---|
832 | } => 'index';
|
---|
833 |
|
---|
834 | app->start;
|
---|
835 | __DATA__
|
---|
836 |
|
---|
837 | @@ index.html.ep
|
---|
838 | <!DOCTYPE html>
|
---|
839 | <html>
|
---|
840 | <head>
|
---|
841 | <style>
|
---|
842 | label.field-with-error { color: #dd7e5e }
|
---|
843 | input.field-with-error { background-color: #fd9e7e }
|
---|
844 | </style>
|
---|
845 | </head>
|
---|
846 | <body>
|
---|
847 | %= form_for index => begin
|
---|
848 | %= label_for user => 'Username (required, 1-20 characters, a-z/0-9)'
|
---|
849 | <br>
|
---|
850 | %= text_field 'user', id => 'user'
|
---|
851 | %= submit_button
|
---|
852 | <br>
|
---|
853 | %= label_for pass => 'Password (optional, 7-500 characters)'
|
---|
854 | <br>
|
---|
855 | %= password_field 'pass', id => 'pass'
|
---|
856 | <br>
|
---|
857 | %= label_for pass_again => 'Password again (equal to the value above)'
|
---|
858 | <br>
|
---|
859 | %= password_field 'pass_again', id => 'pass_again'
|
---|
860 | % end
|
---|
861 | </body>
|
---|
862 | </html>
|
---|
863 |
|
---|
864 | @@ thanks.html.ep
|
---|
865 | <!DOCTYPE html>
|
---|
866 | <html><body>Thank you <%= validation->param('user') %>.</body></html>
|
---|
867 |
|
---|
868 | Form elements generated with tag helpers from
|
---|
869 | L<Mojolicious::Plugin::TagHelpers> will automatically remember their previous
|
---|
870 | values and add the class C<field-with-error> for fields that failed validation
|
---|
871 | to make styling with CSS easier.
|
---|
872 |
|
---|
873 | <label class="field-with-error" for="user">
|
---|
874 | Username (required, only characters e-t)
|
---|
875 | </label>
|
---|
876 | <input class="field-with-error" type="text" name="user" value="sri">
|
---|
877 |
|
---|
878 | For a full list of available checks see also
|
---|
879 | L<Mojolicious::Validator/"CHECKS">.
|
---|
880 |
|
---|
881 | =head2 Adding form validation checks
|
---|
882 |
|
---|
883 | Validation checks can be registered with L<Mojolicious::Validator/"add_check">
|
---|
884 | and return a false value if they were successful. A true value may be used to
|
---|
885 | pass along additional information which can then be retrieved with
|
---|
886 | L<Mojolicious::Validator::Validation/"error">.
|
---|
887 |
|
---|
888 | use Mojolicious::Lite;
|
---|
889 |
|
---|
890 | # Add "range" check
|
---|
891 | app->validator->add_check(range => sub {
|
---|
892 | my ($v, $name, $value, $min, $max) = @_;
|
---|
893 | return $value < $min || $value > $max;
|
---|
894 | });
|
---|
895 |
|
---|
896 | get '/' => 'form';
|
---|
897 |
|
---|
898 | post '/test' => sub {
|
---|
899 | my $c = shift;
|
---|
900 |
|
---|
901 | # Validate parameters with custom check
|
---|
902 | my $v = $c->validation;
|
---|
903 | $v->required('number')->range(3, 23);
|
---|
904 |
|
---|
905 | # Render form again if validation failed
|
---|
906 | return $c->render('form') if $v->has_error;
|
---|
907 |
|
---|
908 | # Prevent double form submission with redirect
|
---|
909 | $c->flash(number => $v->param('number'));
|
---|
910 | $c->redirect_to('form');
|
---|
911 | };
|
---|
912 |
|
---|
913 | app->start;
|
---|
914 | __DATA__
|
---|
915 |
|
---|
916 | @@ form.html.ep
|
---|
917 | <!DOCTYPE html>
|
---|
918 | <html>
|
---|
919 | <body>
|
---|
920 | % if (my $number = flash 'number') {
|
---|
921 | <p>Thanks, the number <%= $number %> was valid.</p>
|
---|
922 | % }
|
---|
923 | %= form_for test => begin
|
---|
924 | % if (my $err = validation->error('number')) {
|
---|
925 | <p>
|
---|
926 | %= 'Value is required.' if $err->[0] eq 'required'
|
---|
927 | %= 'Value needs to be between 3 and 23.' if $err->[0] eq 'range'
|
---|
928 | </p>
|
---|
929 | % }
|
---|
930 | %= text_field 'number'
|
---|
931 | %= submit_button
|
---|
932 | % end
|
---|
933 | </body>
|
---|
934 | </html>
|
---|
935 |
|
---|
936 | =head2 Cross-site request forgery
|
---|
937 |
|
---|
938 | CSRF is a very common attack on web applications that trick your logged in
|
---|
939 | users to submit forms they did not intend to send, with something as mundane as
|
---|
940 | a link. All you have to do, to protect your users from this, is to add an
|
---|
941 | additional hidden field to your forms with
|
---|
942 | L<Mojolicious::Plugin::TagHelpers/"csrf_field">, and validate it with
|
---|
943 | L<Mojolicious::Validator::Validation/"csrf_protect">.
|
---|
944 |
|
---|
945 | use Mojolicious::Lite;
|
---|
946 |
|
---|
947 | get '/' => {template => 'target'};
|
---|
948 |
|
---|
949 | post '/' => sub {
|
---|
950 | my $c = shift;
|
---|
951 |
|
---|
952 | # Check CSRF token
|
---|
953 | my $v = $c->validation;
|
---|
954 | return $c->render(text => 'Bad CSRF token!', status => 403)
|
---|
955 | if $v->csrf_protect->has_error('csrf_token');
|
---|
956 |
|
---|
957 | my $city = $v->required('city')->param('city');
|
---|
958 | $c->render(text => "Low orbit ion cannon pointed at $city!")
|
---|
959 | unless $v->has_error;
|
---|
960 | } => 'target';
|
---|
961 |
|
---|
962 | app->start;
|
---|
963 | __DATA__
|
---|
964 |
|
---|
965 | @@ target.html.ep
|
---|
966 | <!DOCTYPE html>
|
---|
967 | <html>
|
---|
968 | <body>
|
---|
969 | %= form_for target => begin
|
---|
970 | %= csrf_field
|
---|
971 | %= label_for city => 'Which city to point low orbit ion cannon at?'
|
---|
972 | %= text_field 'city', id => 'city'
|
---|
973 | %= submit_button
|
---|
974 | %= end
|
---|
975 | </body>
|
---|
976 | </html>
|
---|
977 |
|
---|
978 | For Ajax requests and the like, you can also generate a token directly with the
|
---|
979 | helper L<Mojolicious::Plugin::DefaultHelpers/"csrf_token">, and then pass it
|
---|
980 | along with the C<X-CSRF-Token> request header.
|
---|
981 |
|
---|
982 | =head1 ADVANCED
|
---|
983 |
|
---|
984 | Less commonly used and more powerful features.
|
---|
985 |
|
---|
986 | =head2 Template inheritance
|
---|
987 |
|
---|
988 | Inheritance takes the layout concept above one step further, the helpers
|
---|
989 | L<Mojolicious::Plugin::DefaultHelpers/"content"> and
|
---|
990 | L<Mojolicious::Plugin::DefaultHelpers/"extends"> allow you to build skeleton
|
---|
991 | templates with named blocks that child templates can override.
|
---|
992 |
|
---|
993 | use Mojolicious::Lite;
|
---|
994 |
|
---|
995 | # first > mylayout
|
---|
996 | get '/first' => {template => 'first', layout => 'mylayout'};
|
---|
997 |
|
---|
998 | # third > second > first > mylayout
|
---|
999 | get '/third' => {template => 'third', layout => 'mylayout'};
|
---|
1000 |
|
---|
1001 | app->start;
|
---|
1002 | __DATA__
|
---|
1003 |
|
---|
1004 | @@ layouts/mylayout.html.ep
|
---|
1005 | <!DOCTYPE html>
|
---|
1006 | <html>
|
---|
1007 | <head><title>Hello</title></head>
|
---|
1008 | <body><%= content %></body>
|
---|
1009 | </html>
|
---|
1010 |
|
---|
1011 | @@ first.html.ep
|
---|
1012 | %= content header => begin
|
---|
1013 | Default header
|
---|
1014 | % end
|
---|
1015 | <div>Hello World!</div>
|
---|
1016 | %= content footer => begin
|
---|
1017 | Default footer
|
---|
1018 | % end
|
---|
1019 |
|
---|
1020 | @@ second.html.ep
|
---|
1021 | % extends 'first';
|
---|
1022 | % content header => begin
|
---|
1023 | New header
|
---|
1024 | % end
|
---|
1025 |
|
---|
1026 | @@ third.html.ep
|
---|
1027 | % extends 'second';
|
---|
1028 | % content footer => begin
|
---|
1029 | New footer
|
---|
1030 | % end
|
---|
1031 |
|
---|
1032 | This chain could go on and on to allow a very high level of template reuse.
|
---|
1033 |
|
---|
1034 | =head2 Serving static files
|
---|
1035 |
|
---|
1036 | Static files are automatically served from the C<public> directories of the
|
---|
1037 | application, which can be customized with L<Mojolicious::Static/"paths">, or one
|
---|
1038 | of the C<DATA> sections from L<Mojolicious::Static/"classes">. And if that's not
|
---|
1039 | enough you can also serve them manually with
|
---|
1040 | L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>static"> and
|
---|
1041 | L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>file">.
|
---|
1042 |
|
---|
1043 | use Mojolicious::Lite;
|
---|
1044 |
|
---|
1045 | get '/' => sub {
|
---|
1046 | my $c = shift;
|
---|
1047 | $c->reply->static('index.html');
|
---|
1048 | };
|
---|
1049 |
|
---|
1050 | get '/some_download' => sub {
|
---|
1051 | my $c = shift;
|
---|
1052 | $c->res->headers->content_disposition('attachment; filename=bar.png;');
|
---|
1053 | $c->reply->static('foo/bar.png');
|
---|
1054 | };
|
---|
1055 |
|
---|
1056 | get '/leak' => sub {
|
---|
1057 | my $c = shift;
|
---|
1058 | $c->reply->file('/etc/passwd');
|
---|
1059 | };
|
---|
1060 |
|
---|
1061 | app->start;
|
---|
1062 |
|
---|
1063 | =head2 Custom responses
|
---|
1064 |
|
---|
1065 | Most response content, static as well as dynamic, gets served through
|
---|
1066 | L<Mojo::Asset::File> and L<Mojo::Asset::Memory> objects. For somewhat static
|
---|
1067 | content, like cached JSON data or temporary files, you can create your own and
|
---|
1068 | use the helper L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>asset"> to
|
---|
1069 | serve them while allowing content negotiation to be performed with C<Range>,
|
---|
1070 | C<If-Modified-Since> and C<If-None-Match> headers.
|
---|
1071 |
|
---|
1072 | use Mojolicious::Lite;
|
---|
1073 | use Mojo::Asset::File;
|
---|
1074 |
|
---|
1075 | get '/leak' => sub {
|
---|
1076 | my $c = shift;
|
---|
1077 | $c->res->headers->content_type('text/plain');
|
---|
1078 | $c->reply->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
|
---|
1079 | };
|
---|
1080 |
|
---|
1081 | app->start;
|
---|
1082 |
|
---|
1083 | For even more control you can also just skip the helper and use
|
---|
1084 | L<Mojolicious::Controller/"rendered"> to tell the renderer when you're done
|
---|
1085 | generating a response.
|
---|
1086 |
|
---|
1087 | use Mojolicious::Lite;
|
---|
1088 | use Mojo::Asset::File;
|
---|
1089 |
|
---|
1090 | get '/leak' => sub {
|
---|
1091 | my $c = shift;
|
---|
1092 | $c->res->headers->content_type('text/plain');
|
---|
1093 | $c->res->content->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
|
---|
1094 | $c->rendered(200);
|
---|
1095 | };
|
---|
1096 |
|
---|
1097 | app->start;
|
---|
1098 |
|
---|
1099 | =head2 Helper plugins
|
---|
1100 |
|
---|
1101 | Some helpers might be useful enough for you to share them between multiple
|
---|
1102 | applications, plugins make that very simple.
|
---|
1103 |
|
---|
1104 | package Mojolicious::Plugin::DebugHelper;
|
---|
1105 | use Mojo::Base 'Mojolicious::Plugin';
|
---|
1106 |
|
---|
1107 | sub register {
|
---|
1108 | my ($self, $app) = @_;
|
---|
1109 | $app->helper(debug => sub {
|
---|
1110 | my ($c, $str) = @_;
|
---|
1111 | $c->app->log->debug($str);
|
---|
1112 | });
|
---|
1113 | }
|
---|
1114 |
|
---|
1115 | 1;
|
---|
1116 |
|
---|
1117 | The C<register> method will be called when you load the plugin. And to add your
|
---|
1118 | helper to the application, you can use L<Mojolicious/"helper">.
|
---|
1119 |
|
---|
1120 | use Mojolicious::Lite;
|
---|
1121 |
|
---|
1122 | plugin 'DebugHelper';
|
---|
1123 |
|
---|
1124 | get '/' => sub {
|
---|
1125 | my $c = shift;
|
---|
1126 | $c->debug('It works!');
|
---|
1127 | $c->render(text => 'Hello!');
|
---|
1128 | };
|
---|
1129 |
|
---|
1130 | app->start;
|
---|
1131 |
|
---|
1132 | A skeleton for a full CPAN compatible plugin distribution can be automatically
|
---|
1133 | generated.
|
---|
1134 |
|
---|
1135 | $ mojo generate plugin DebugHelper
|
---|
1136 |
|
---|
1137 | And if you have a C<PAUSE> account (which can be requested at
|
---|
1138 | L<http://pause.perl.org>), you are only a few commands away from releasing it
|
---|
1139 | to CPAN.
|
---|
1140 |
|
---|
1141 | $ perl Makefile.PL
|
---|
1142 | $ make test
|
---|
1143 | $ make manifest
|
---|
1144 | $ make dist
|
---|
1145 | $ mojo cpanify -u USER -p PASS Mojolicious-Plugin-DebugHelper-0.01.tar.gz
|
---|
1146 |
|
---|
1147 | =head2 Bundling assets with plugins
|
---|
1148 |
|
---|
1149 | Assets such as templates and static files can be easily bundled with your
|
---|
1150 | plugins, even if you plan to release them to CPAN.
|
---|
1151 |
|
---|
1152 | $ mojo generate plugin AlertAssets
|
---|
1153 | $ mkdir Mojolicious-Plugin-AlertAssets/lib/Mojolicious/Plugin/AlertAssets
|
---|
1154 | $ cd Mojolicious-Plugin-AlertAssets/lib/Mojolicious/Plugin/AlertAssets
|
---|
1155 | $ mkdir public
|
---|
1156 | $ echo 'alert("Hello World!");' > public/alertassets.js
|
---|
1157 | $ mkdir templates
|
---|
1158 | $ echo '%= javascript "/alertassets.js"' > templates/alertassets.html.ep
|
---|
1159 |
|
---|
1160 | Just give them reasonably unique names, ideally based on the name of your
|
---|
1161 | plugin, and append their respective directories to the list of search paths
|
---|
1162 | when C<register> is called.
|
---|
1163 |
|
---|
1164 | package Mojolicious::Plugin::AlertAssets;
|
---|
1165 | use Mojo::Base 'Mojolicious::Plugin';
|
---|
1166 |
|
---|
1167 | use Mojo::File 'path';
|
---|
1168 |
|
---|
1169 | sub register {
|
---|
1170 | my ($self, $app) = @_;
|
---|
1171 |
|
---|
1172 | # Append "templates" and "public" directories
|
---|
1173 | my $base = path(__FILE__)->sibling('AlertAssets');
|
---|
1174 | push @{$app->renderer->paths}, $base->child('templates')->to_string;
|
---|
1175 | push @{$app->static->paths}, $base->child('public')->to_string;
|
---|
1176 | }
|
---|
1177 |
|
---|
1178 | 1;
|
---|
1179 |
|
---|
1180 | Both will work just like normal C<templates> and C<public> directories once
|
---|
1181 | you've installed and loaded the plugin, with slightly lower precedence.
|
---|
1182 |
|
---|
1183 | use Mojolicious::Lite;
|
---|
1184 |
|
---|
1185 | plugin 'AlertAssets';
|
---|
1186 |
|
---|
1187 | get '/alert_me';
|
---|
1188 |
|
---|
1189 | app->start;
|
---|
1190 | __DATA__
|
---|
1191 |
|
---|
1192 | @@ alert_me.html.ep
|
---|
1193 | <!DOCTYPE html>
|
---|
1194 | <html>
|
---|
1195 | <head>
|
---|
1196 | <title>Alert me!</title>
|
---|
1197 | %= include 'alertassets'
|
---|
1198 | </head>
|
---|
1199 | <body>You've been alerted.</body>
|
---|
1200 | </html>
|
---|
1201 |
|
---|
1202 | And it works just the same for assets bundled in the C<DATA> section of your
|
---|
1203 | plugin.
|
---|
1204 |
|
---|
1205 | package Mojolicious::Plugin::AlertAssets;
|
---|
1206 | use Mojo::Base 'Mojolicious::Plugin';
|
---|
1207 |
|
---|
1208 | sub register {
|
---|
1209 | my ($self, $app) = @_;
|
---|
1210 |
|
---|
1211 | # Append class
|
---|
1212 | push @{$app->renderer->classes}, __PACKAGE__;
|
---|
1213 | push @{$app->static->classes}, __PACKAGE__;
|
---|
1214 | }
|
---|
1215 |
|
---|
1216 | 1;
|
---|
1217 | __DATA__
|
---|
1218 |
|
---|
1219 | @@ alertassets.js
|
---|
1220 | alert("Hello World!");
|
---|
1221 |
|
---|
1222 | @@ alertassets.html.ep
|
---|
1223 | %= javascript "/alertassets.js"
|
---|
1224 |
|
---|
1225 | =head2 Post-processing dynamic content
|
---|
1226 |
|
---|
1227 | While post-processing tasks are generally very easy with the hook
|
---|
1228 | L<Mojolicious/"after_dispatch">, for content generated by the renderer it is a
|
---|
1229 | lot more efficient to use L<Mojolicious/"after_render">.
|
---|
1230 |
|
---|
1231 | use Mojolicious::Lite;
|
---|
1232 | use IO::Compress::Gzip 'gzip';
|
---|
1233 |
|
---|
1234 | hook after_render => sub {
|
---|
1235 | my ($c, $output, $format) = @_;
|
---|
1236 |
|
---|
1237 | # Check if "gzip => 1" has been set in the stash
|
---|
1238 | return unless $c->stash->{gzip};
|
---|
1239 |
|
---|
1240 | # Check if user agent accepts gzip compression
|
---|
1241 | return unless ($c->req->headers->accept_encoding // '') =~ /gzip/i;
|
---|
1242 | $c->res->headers->append(Vary => 'Accept-Encoding');
|
---|
1243 |
|
---|
1244 | # Compress content with gzip
|
---|
1245 | $c->res->headers->content_encoding('gzip');
|
---|
1246 | gzip $output, \my $compressed;
|
---|
1247 | $$output = $compressed;
|
---|
1248 | };
|
---|
1249 |
|
---|
1250 | get '/' => {template => 'hello', title => 'Hello', gzip => 1};
|
---|
1251 |
|
---|
1252 | app->start;
|
---|
1253 | __DATA__
|
---|
1254 |
|
---|
1255 | @@ hello.html.ep
|
---|
1256 | <!DOCTYPE html>
|
---|
1257 | <html>
|
---|
1258 | <head><title><%= title %></title></head>
|
---|
1259 | <body>Compressed content.</body>
|
---|
1260 | </html>
|
---|
1261 |
|
---|
1262 | =head2 Streaming
|
---|
1263 |
|
---|
1264 | You don't have to render all content at once, the method
|
---|
1265 | L<Mojolicious::Controller/"write"> can also be used to stream a series of
|
---|
1266 | smaller chunks.
|
---|
1267 |
|
---|
1268 | use Mojolicious::Lite;
|
---|
1269 |
|
---|
1270 | get '/' => sub {
|
---|
1271 | my $c = shift;
|
---|
1272 |
|
---|
1273 | # Prepare body
|
---|
1274 | my $body = 'Hello World!';
|
---|
1275 | $c->res->headers->content_length(length $body);
|
---|
1276 |
|
---|
1277 | # Start writing directly with a drain callback
|
---|
1278 | my $drain;
|
---|
1279 | $drain = sub {
|
---|
1280 | my $c = shift;
|
---|
1281 | my $chunk = substr $body, 0, 1, '';
|
---|
1282 | $drain = undef unless length $body;
|
---|
1283 | $c->write($chunk, $drain);
|
---|
1284 | };
|
---|
1285 | $c->$drain;
|
---|
1286 | };
|
---|
1287 |
|
---|
1288 | app->start;
|
---|
1289 |
|
---|
1290 | The drain callback will be executed whenever the entire previous chunk of data
|
---|
1291 | has actually been written.
|
---|
1292 |
|
---|
1293 | HTTP/1.1 200 OK
|
---|
1294 | Date: Sat, 13 Sep 2014 16:48:29 GMT
|
---|
1295 | Content-Length: 12
|
---|
1296 | Server: Mojolicious (Perl)
|
---|
1297 |
|
---|
1298 | Hello World!
|
---|
1299 |
|
---|
1300 | Instead of providing a C<Content-Length> header you can also call
|
---|
1301 | L<Mojolicious::Controller/"finish"> and close the connection manually once you
|
---|
1302 | are done.
|
---|
1303 |
|
---|
1304 | use Mojolicious::Lite;
|
---|
1305 |
|
---|
1306 | get '/' => sub {
|
---|
1307 | my $c = shift;
|
---|
1308 |
|
---|
1309 | # Prepare body
|
---|
1310 | my $body = 'Hello World!';
|
---|
1311 |
|
---|
1312 | # Start writing directly with a drain callback
|
---|
1313 | my $drain;
|
---|
1314 | $drain = sub {
|
---|
1315 | my $c = shift;
|
---|
1316 | my $chunk = substr $body, 0, 1, '';
|
---|
1317 | length $chunk ? $c->write($chunk, $drain) : $c->finish;
|
---|
1318 | };
|
---|
1319 | $c->$drain;
|
---|
1320 | };
|
---|
1321 |
|
---|
1322 | app->start;
|
---|
1323 |
|
---|
1324 | While this is rather inefficient, as it prevents keep-alive, it is sometimes
|
---|
1325 | necessary for EventSource and similar applications.
|
---|
1326 |
|
---|
1327 | HTTP/1.1 200 OK
|
---|
1328 | Date: Sat, 13 Sep 2014 16:48:29 GMT
|
---|
1329 | Connection: close
|
---|
1330 | Server: Mojolicious (Perl)
|
---|
1331 |
|
---|
1332 | Hello World!
|
---|
1333 |
|
---|
1334 | =head2 Chunked transfer encoding
|
---|
1335 |
|
---|
1336 | For very dynamic content you might not know the response content length in
|
---|
1337 | advance, that's where the chunked transfer encoding and
|
---|
1338 | L<Mojolicious::Controller/"write_chunk"> come in handy. A common use would be
|
---|
1339 | to send the C<head> section of an HTML document to the browser in advance and
|
---|
1340 | speed up preloading of referenced images and stylesheets.
|
---|
1341 |
|
---|
1342 | use Mojolicious::Lite;
|
---|
1343 |
|
---|
1344 | get '/' => sub {
|
---|
1345 | my $c = shift;
|
---|
1346 | $c->write_chunk('<html><head><title>Example</title></head>' => sub {
|
---|
1347 | my $c = shift;
|
---|
1348 | $c->finish('<body>Example</body></html>');
|
---|
1349 | });
|
---|
1350 | };
|
---|
1351 |
|
---|
1352 | app->start;
|
---|
1353 |
|
---|
1354 | The optional drain callback ensures that all previous chunks have been written
|
---|
1355 | before processing continues. To end the stream you can call
|
---|
1356 | L<Mojolicious::Controller/"finish"> or write an empty chunk of data.
|
---|
1357 |
|
---|
1358 | HTTP/1.1 200 OK
|
---|
1359 | Date: Sat, 13 Sep 2014 16:48:29 GMT
|
---|
1360 | Transfer-Encoding: chunked
|
---|
1361 | Server: Mojolicious (Perl)
|
---|
1362 |
|
---|
1363 | 29
|
---|
1364 | <html><head><title>Example</title></head>
|
---|
1365 | 1b
|
---|
1366 | <body>Example</body></html>
|
---|
1367 | 0
|
---|
1368 |
|
---|
1369 | Especially in combination with long inactivity timeouts this can be very useful
|
---|
1370 | for Comet (long polling). Due to limitations in some web servers this might not
|
---|
1371 | work perfectly in all deployment environments.
|
---|
1372 |
|
---|
1373 | =head2 Encoding
|
---|
1374 |
|
---|
1375 | Templates stored in files are expected to be C<UTF-8> by default, but that can
|
---|
1376 | be easily changed with L<Mojolicious::Renderer/"encoding">.
|
---|
1377 |
|
---|
1378 | $app->renderer->encoding('koi8-r');
|
---|
1379 |
|
---|
1380 | All templates from the C<DATA> section are bound to the encoding of the Perl
|
---|
1381 | script.
|
---|
1382 |
|
---|
1383 | use Mojolicious::Lite;
|
---|
1384 |
|
---|
1385 | get '/heart';
|
---|
1386 |
|
---|
1387 | app->start;
|
---|
1388 | __DATA__
|
---|
1389 |
|
---|
1390 | @@ heart.html.ep
|
---|
1391 | I ⥠Mojolicious!
|
---|
1392 |
|
---|
1393 | =head2 Base64 encoded DATA files
|
---|
1394 |
|
---|
1395 | Base64 encoded static files such as images can be easily stored in the C<DATA>
|
---|
1396 | section of your application, similar to templates.
|
---|
1397 |
|
---|
1398 | use Mojolicious::Lite;
|
---|
1399 |
|
---|
1400 | get '/' => {text => 'I ⥠Mojolicious!'};
|
---|
1401 |
|
---|
1402 | app->start;
|
---|
1403 | __DATA__
|
---|
1404 |
|
---|
1405 | @@ favicon.ico (base64)
|
---|
1406 | ...base64 encoded image...
|
---|
1407 |
|
---|
1408 | =head2 Inflating DATA templates
|
---|
1409 |
|
---|
1410 | Templates stored in files get preferred over files from the C<DATA> section,
|
---|
1411 | this allows you to include a default set of templates in your application that
|
---|
1412 | the user can later customize. The command L<Mojolicious::Command::inflate> will
|
---|
1413 | write all templates and static files from the C<DATA> section into actual files
|
---|
1414 | in the C<templates> and C<public> directories.
|
---|
1415 |
|
---|
1416 | $ ./myapp.pl inflate
|
---|
1417 |
|
---|
1418 | =head2 Customizing the template syntax
|
---|
1419 |
|
---|
1420 | You can easily change the whole template syntax by loading
|
---|
1421 | L<Mojolicious::Plugin::EPRenderer> with a custom configuration.
|
---|
1422 |
|
---|
1423 | use Mojolicious::Lite;
|
---|
1424 |
|
---|
1425 | plugin EPRenderer => {
|
---|
1426 | name => 'mustache',
|
---|
1427 | template => {
|
---|
1428 | tag_start => '{{',
|
---|
1429 | tag_end => '}}'
|
---|
1430 | }
|
---|
1431 | };
|
---|
1432 |
|
---|
1433 | get '/:name' => {name => 'Anonymous'} => 'index';
|
---|
1434 |
|
---|
1435 | app->start;
|
---|
1436 | __DATA__
|
---|
1437 |
|
---|
1438 | @@ index.html.mustache
|
---|
1439 | Hello {{= $name }}.
|
---|
1440 |
|
---|
1441 | L<Mojo::Template> contains the whole list of available options.
|
---|
1442 |
|
---|
1443 | =head2 Adding your favorite template system
|
---|
1444 |
|
---|
1445 | Maybe you would prefer a different template system than C<ep>, which is provided
|
---|
1446 | by L<Mojolicious::Plugin::EPRenderer>, and there is not already a plugin on CPAN
|
---|
1447 | for your favorite one. All you have to do, is to add a new C<handler> with
|
---|
1448 | L<Mojolicious::Renderer/"add_handler"> when C<register> is called.
|
---|
1449 |
|
---|
1450 | package Mojolicious::Plugin::MyRenderer;
|
---|
1451 | use Mojo::Base 'Mojolicious::Plugin';
|
---|
1452 |
|
---|
1453 | sub register {
|
---|
1454 | my ($self, $app) = @_;
|
---|
1455 |
|
---|
1456 | # Add "mine" handler
|
---|
1457 | $app->renderer->add_handler(mine => sub {
|
---|
1458 | my ($renderer, $c, $output, $options) = @_;
|
---|
1459 |
|
---|
1460 | # Check for one-time use inline template
|
---|
1461 | my $inline_template = $options->{inline};
|
---|
1462 |
|
---|
1463 | # Check for appropriate template in "templates" directories
|
---|
1464 | my $template_path = $renderer->template_path($options);
|
---|
1465 |
|
---|
1466 | # Check for appropriate template in DATA sections
|
---|
1467 | my $data_template = $renderer->get_data_template($options);
|
---|
1468 |
|
---|
1469 | # This part is up to you and your template system :)
|
---|
1470 | ...
|
---|
1471 |
|
---|
1472 | # Pass the rendered result back to the renderer
|
---|
1473 | $$output = 'Hello World!';
|
---|
1474 |
|
---|
1475 | # Or just die if an error occurs
|
---|
1476 | die 'Something went wrong with the template';
|
---|
1477 | });
|
---|
1478 | }
|
---|
1479 |
|
---|
1480 | 1;
|
---|
1481 |
|
---|
1482 | An C<inline> template, if provided by the user, will be passed along with the
|
---|
1483 | options. You can use L<Mojolicious::Renderer/"template_path"> to search the
|
---|
1484 | C<templates> directories of the application, and
|
---|
1485 | L<Mojolicious::Renderer/"get_data_template"> to search the C<DATA> sections.
|
---|
1486 |
|
---|
1487 | use Mojolicious::Lite;
|
---|
1488 |
|
---|
1489 | plugin 'MyRenderer';
|
---|
1490 |
|
---|
1491 | # Render an inline template
|
---|
1492 | get '/inline' => {inline => '...', handler => 'mine'};
|
---|
1493 |
|
---|
1494 | # Render a template from the DATA section
|
---|
1495 | get '/data' => {template => 'test'};
|
---|
1496 |
|
---|
1497 | app->start;
|
---|
1498 | __DATA__
|
---|
1499 |
|
---|
1500 | @@ test.html.mine
|
---|
1501 | ...
|
---|
1502 |
|
---|
1503 | =head2 Adding a handler to generate binary data
|
---|
1504 |
|
---|
1505 | By default the renderer assumes that every C<handler> generates characters that
|
---|
1506 | need to be automatically encoded, but this can be easily disabled if you're
|
---|
1507 | generating bytes instead.
|
---|
1508 |
|
---|
1509 | use Mojolicious::Lite;
|
---|
1510 | use Storable 'nfreeze';
|
---|
1511 |
|
---|
1512 | # Add "storable" handler
|
---|
1513 | app->renderer->add_handler(storable => sub {
|
---|
1514 | my ($renderer, $c, $output, $options) = @_;
|
---|
1515 |
|
---|
1516 | # Disable automatic encoding
|
---|
1517 | delete $options->{encoding};
|
---|
1518 |
|
---|
1519 | # Encode data from stash value
|
---|
1520 | $$output = nfreeze delete $c->stash->{storable};
|
---|
1521 | });
|
---|
1522 |
|
---|
1523 | # Set "handler" value automatically if "storable" value is set already
|
---|
1524 | app->hook(before_render => sub {
|
---|
1525 | my ($c, $args) = @_;
|
---|
1526 | $args->{handler} = 'storable'
|
---|
1527 | if exists $args->{storable} || exists $c->stash->{storable};
|
---|
1528 | });
|
---|
1529 |
|
---|
1530 | get '/' => {storable => {i => '⥠mojolicious'}};
|
---|
1531 |
|
---|
1532 | app->start;
|
---|
1533 |
|
---|
1534 | The hook L<Mojolicious/"before_render"> can be used to make stash values like
|
---|
1535 | C<storable> special, so that they no longer require a C<handler> value to be set
|
---|
1536 | explicitly.
|
---|
1537 |
|
---|
1538 | # Explicit "handler" value
|
---|
1539 | $c->render(storable => {i => '⥠mojolicious'}, handler => 'storable');
|
---|
1540 |
|
---|
1541 | # Implicit "handler" value (with "before_render" hook)
|
---|
1542 | $c->render(storable => {i => '⥠mojolicious'});
|
---|
1543 |
|
---|
1544 | =head1 MORE
|
---|
1545 |
|
---|
1546 | You can continue with L<Mojolicious::Guides> now or take a look at the
|
---|
1547 | L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
|
---|
1548 | documentation and examples by many different authors.
|
---|
1549 |
|
---|
1550 | =head1 SUPPORT
|
---|
1551 |
|
---|
1552 | If you have any questions the documentation might not yet answer, don't
|
---|
1553 | hesitate to ask on the
|
---|
1554 | L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
|
---|
1555 | channel C<#mojo> on C<irc.perl.org>
|
---|
1556 | (L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
|
---|
1557 |
|
---|
1558 | =cut
|
---|