1Dancer2::Manual(3) User Contributed Perl Documentation Dancer2::Manual(3)
2
3
4
6 Dancer2::Manual - A gentle introduction to Dancer2
7
9 version 0.400000
10
12 Dancer2 is a free and open source web application framework written in
13 Perl.
14
15 It's a complete rewrite of Dancer, based on Moo and using a more robust
16 and extensible fully-OO design.
17
18 It's designed to be powerful and flexible, but also easy to use -
19 getting up and running with your web app is trivial, and an ecosystem
20 of adaptors for common template engines, session storage, logging
21 methods, serializers, and plugins to make common tasks easy means you
22 can do what you want to do, your way, easily.
23
25 Installation of Dancer2 is simple, using your favourite method to
26 install from CPAN, e.g.:
27
28 perl -MCPAN -e 'install Dancer2'
29
30 Thanks to the magic of cpanminus, if you do not have CPAN.pm
31 configured, or just want a quickfire way to get running, the following
32 should work, at least on Unix-like systems:
33
34 wget -O - http://cpanmin.us | sudo perl - Dancer2
35
36 (If you don't have root access, omit the 'sudo', and cpanminus will
37 install Dancer2 and prereqs into "~/perl5".)
38
39 Dancer2 is also available as a package from the package repository of
40 several distributions, for example on Debian/Ubuntu you should be able
41 to just:
42
43 apt-get install libdancer2-perl
44
45 Do be aware, though, that distribution-packaged versions sometimes lag
46 behind the most recent version on CPAN.
47
49 Create a web application using the dancer script:
50
51 $ dancer2 -a MyApp && cd MyApp
52 + MyApp
53 + MyApp/config.yml
54 + MyApp/Makefile.PL
55 + MyApp/MANIFEST.SKIP
56 + MyApp/.dancer
57 + MyApp/cpanfile
58 + MyApp/bin
59 + MyApp/bin/app.psgi
60 + MyApp/environments
61 + MyApp/environments/development.yml
62 + MyApp/environments/production.yml
63 + MyApp/lib
64 + MyApp/lib/MyApp.pm
65 + MyApp/public
66 + MyApp/public/favicon.ico
67 + MyApp/public/500.html
68 + MyApp/public/dispatch.cgi
69 + MyApp/public/404.html
70 + MyApp/public/dispatch.fcgi
71 + MyApp/public/css
72 + MyApp/public/css/error.css
73 + MyApp/public/css/style.css
74 + MyApp/public/images
75 + MyApp/public/images/perldancer.jpg
76 + MyApp/public/images/perldancer-bg.jpg
77 + MyApp/public/javascripts
78 + MyApp/public/javascripts/jquery.js
79 + MyApp/t
80 + MyApp/t/001_base.t
81 + MyApp/t/002_index_route.t
82 + MyApp/views
83 + MyApp/views/index.tt
84 + MyApp/views/layouts
85 + MyApp/views/layouts/main.tt
86
87 It creates a directory named after the name of the app, along with a
88 configuration file, a views directory (where your templates and layouts
89 will live), an environments directory (where environment-specific
90 settings live), a module containing the actual guts of your
91 application, and a script to start it. A default skeleton is used to
92 bootstrap the new application, but you can use the "-s" option to
93 provide another skeleton. For example:
94
95 $ dancer2 -a MyApp -s ~/mydancerskel
96
97 For an example of a skeleton directory check the default one available
98 in the "share/" directory of your Dancer2 distribution.
99
100 (In what follows we will refer to the directory in which you have
101 created your Dancer2 application -- e.g., what "MyApp" was above -- as
102 the "appdir".)
103
104 Because Dancer2 is a PSGI web application framework, you can use the
105 "plackup" tool (provided by Plack) for launching the application:
106
107 plackup -p 5000 bin/app.psgi
108
109 View the web application at:
110
111 http://localhost:5000
112
114 When Dancer2 is imported to a script, that script becomes a webapp, and
115 at this point, all the script has to do is declare a list of routes. A
116 route handler is composed by an HTTP method, a path pattern and a code
117 block. "strict", "warnings" and "utf8" pragmas are also imported with
118 Dancer2.
119
120 The code block given to the route handler has to return a string which
121 will be used as the content to render to the client.
122
123 Routes are defined for a given HTTP method. For each method supported,
124 a keyword is exported by the module.
125
126 HTTP Methods
127 Here are some of the standard HTTP methods which you can use to define
128 your route handlers.
129
130 • GET The GET method retrieves information, and is the most common
131
132 GET requests should be used for typical "fetch" requests -
133 retrieving information. They should not be used for requests which
134 change data on the server or have other effects.
135
136 When defining a route handler for the GET method, Dancer2
137 automatically defines a route handler for the HEAD method (in order
138 to honour HEAD requests for each of your GET route handlers).
139
140 To define a GET action, use the get keyword.
141
142 • POST The POST method is used to create a resource on the server.
143
144 To define a POST action, use the post keyword.
145
146 • PUT The PUT method is used to replace an existing resource.
147
148 To define a PUT action, use the put keyword.
149
150 a PUT request should replace the existing resource with that
151 specified - for instance - if you wanted to just update an email
152 address for a user, you'd have to specify all attributes of the
153 user again; to make a partial update, a PATCH request is used.
154
155 • PATCH The PATCH method updates some attributes of an existing
156 resource.
157
158 To define a PATCH action, use the patch keyword.
159
160 • DELETE The DELETE method requests that the origin server delete the
161 resource identified by the Request-URI.
162
163 To define a DELETE action, use the del keyword.
164
165 Handling multiple HTTP request methods
166
167 Routes can use "any" to match all, or a specified list of HTTP methods.
168
169 The following will match any HTTP request to the path "/myaction":
170
171 any '/myaction' => sub {
172 # code
173 }
174
175 The following will match GET or POST requests to "/myaction":
176
177 any ['get', 'post'] => '/myaction' => sub {
178 # code
179 };
180
181 For convenience, any route which matches GET requests will also match
182 HEAD requests.
183
184 Route Handlers
185 The route action is the code reference declared. It can access
186 parameters through the specific route_parameters, query_parameters, and
187 body_parameters keywords, which return a Hash::MultiValue object. This
188 hashref is a merge of the route pattern matches and the request params.
189
190 You can find more details about how params are built and how to access
191 them in the Dancer2::Core::Request documentation.
192
193 Declaring Routes
194
195 To control what happens when a web request is received by your webapp,
196 you'll need to declare "routes". A route declaration indicates which
197 HTTP method(s) it is valid for, the path it matches (e.g. "/foo/bar"),
198 and a coderef to execute, which returns the response.
199
200 get '/hello/:name' => sub {
201 return "Hi there " . route_parameters->get('name');
202 };
203
204 The above route specifies that, for GET requests to "/hello/...", the
205 code block provided should be executed.
206
207 Retrieving request parameters
208
209 The query_parameters, route_parameters, and body_parameters keywords
210 provide a Hash::MultiValue result from the three different parameters.
211
212 Named matching
213
214 A route pattern can contain one or more tokens (a word prefixed with
215 ':'). Each token found in a route pattern is used as a named-pattern
216 match. Any match will be set in the route parameters.
217
218 get '/hello/:name' => sub {
219 return "Hey " . route_parameters->get('name') . ", welcome here!";
220 };
221
222 Tokens can be optional, for example:
223
224 get '/hello/:name?' => sub {
225 my $name = route_parameters->get('name') // 'Whoever you are';
226 return "Hello there, $name";
227 };
228
229 Named matching with type constraints
230
231 Type constraints can be added to tokens.
232
233 get '/user/:id[Int]' => sub {
234 # matches /user/34 but not /user/jamesdean
235 my $user_id = route_parameters->get('id');
236 };
237
238 get '/user/:username[Str]' => sub {
239 # matches /user/jamesdean but not /user/34 since that is caught
240 # by previous route
241 my $username = route_parameters->get('username');
242 };
243
244 You can even use type constraints to add a regexp check:
245
246 get '/book/:date[StrMatch[qr{\d\d\d\d-\d\d-\d\d}]]' => sub {
247 # matches /book/2014-02-04
248 my $date = route_parameters->get('date');
249 };
250
251 The default type library is Dancer2::Core::Types but any type library
252 built using Type::Tiny's Type::Library can be used instead. If you'd
253 like to use a different default type library you must declare it in the
254 configuration file, for example:
255
256 type_library: My::Type::Library
257
258 Alternatively you can specify the type library in which the type is
259 defined as part of the route definition:
260
261 get '/user/:username[My::Type::Library::Username]' => sub {
262 my $username = route_parameters->get('username');
263 };
264
265 This will load "My::Type::Library" and from it use the type "Username".
266 This allows types to be used that are not part of the type library
267 defined by config's "type_library".
268
269 More complex constructs are allowed such as:
270
271 get '/some/:thing[Int|MyDate]' => sub {
272 ...;
273 };
274
275 See "lookup($name)" in Type::Registry for more details.
276
277 Wildcard Matching
278
279 A route can contain a wildcard (represented by a "*"). Each wildcard
280 match will be placed in a list, which the "splat" keyword returns.
281
282 get '/download/*.*' => sub {
283 my ($file, $ext) = splat;
284 # do something with $file.$ext here
285 };
286
287 An extensive, greedier wildcard represented by "**" (A.K.A.
288 "megasplat") can be used to define a route. The additional path is
289 broken down and returned as an arrayref:
290
291 get '/entry/*/tags/**' => sub {
292 my ( $entry_id, $tags ) = splat;
293 my @tags = @{$tags};
294 };
295
296 The "splat" keyword in the above example for the route
297 /entry/1/tags/one/two would set $entry_id to 1 and $tags to "['one',
298 'two']".
299
300 Mixed named and wildcard matching
301
302 A route can combine named (token) matching and wildcard matching. This
303 is useful when chaining actions:
304
305 get '/team/:team/**' => sub {
306 var team => route_parameters->get('team');
307 pass;
308 };
309
310 prefix '/team/:team';
311
312 get '/player/*' => sub {
313 my ($player) = splat;
314
315 # etc...
316 };
317
318 get '/score' => sub {
319 return score_for( vars->{'team'} );
320 };
321
322 Regular Expression Matching
323
324 A route can be defined with a Perl regular expression.
325
326 In order to tell Dancer2 to consider the route as a real regexp, the
327 route must be defined explicitly with "qr{}", like the following:
328
329 get qr{/hello/([\w]+)} => sub {
330 my ($name) = splat;
331 return "Hello $name";
332 };
333
334 A route regex may use named capture groups. The "captures" keyword will
335 return a reference to a copy of "%+".
336
337 Conditional Matching
338
339 Routes may include some matching conditions (on content_type, agent,
340 user_agent, content_length and path_info):
341
342 get '/foo', {agent => 'Songbird (\d\.\d)[\d\/]*?'} => sub {
343 'foo method for songbird'
344 }
345
346 get '/foo' => sub {
347 'all browsers except songbird'
348 }
349
350 Prefix
351 A prefix can be defined for each route handler, like this:
352
353 prefix '/home';
354
355 From here, any route handler is defined to /home/*
356
357 get '/page1' => sub {}; # will match '/home/page1'
358
359 You can unset the prefix value
360
361 prefix '/'; # or: prefix undef;
362 get '/page1' => sub {}; # will match /page1
363
364 Alternatively, to prevent you from ever forgetting to undef the prefix,
365 you can use lexical prefix like this:
366
367 prefix '/home' => sub {
368 get '/page1' => sub {}; # will match '/home/page1'
369 }; ## prefix reset to previous value on exit
370
371 get '/page1' => sub {}; # will match /page1
372
373 Delayed responses (Async/Streaming)
374 Dancer2 can provide delayed (otherwise known as asynchronous) responses
375 using the "delayed" keyword. These responses are streamed, although you
376 can set the content all at once, if you prefer.
377
378 get '/status' => sub {
379 delayed {
380 response_header 'X-Foo' => 'Bar';
381
382 # flush headers (in case of streaming)
383 flush;
384
385 # send content to the user
386 content 'Hello, world!';
387
388 # you can write more content
389 # all streaming
390 content 'Hello, again!';
391
392 # when done, close the connection
393 done;
394
395 # do whatever you want else, asynchronously
396 # the user socket closed by now
397 ...
398 };
399 };
400
401 If you are streaming (calling "content" several times), you must call
402 "flush" first. If you're sending only once, you don't need to call
403 "flush".
404
405 Here is an example of using delayed responses with AnyEvent:
406
407 use Dancer2;
408 use AnyEvent;
409
410 my %timers;
411 my $count = 5;
412 get '/drums' => sub {
413 delayed {
414 print "Stretching...\n";
415 flush; # necessary, since we're streaming
416
417 $timers{'Snare'} = AE::timer 1, 1, delayed {
418 $timers{'HiHat'} ||= AE::timer 0, 0.5, delayed {
419 content "Tss...\n";
420 };
421
422 content "Bap!\n";
423
424 if ( $count-- == 0 ) {
425 %timers = ();
426 content "Tugu tugu tugu dum!\n";
427 done;
428
429 print "<enter sound of applause>\n\n";
430 $timers{'Applause'} = AE::timer 3, 0, sub {
431 # the DSL will not available here
432 # because we didn't call the "delayed" keyword
433 print "<applause dies out>\n";
434 };
435 }
436 };
437 };
438 };
439
440 If an error happens during a write operation, a warning will be issued
441 to the logger.
442
443 You can handle the error yourself by providing an "on_error" handler:
444
445 get '/' => sub {
446 delayed {
447 flush;
448 content "works";
449
450 # ... user disconnected here ...
451
452 content "fails";
453
454 # ... error triggered ...
455
456 done; # doesn't even get run
457 } on_error => sub {
458 # delayed{} not needed, DSL already available
459 my ($error) = @_;
460 # do something with $error
461 };
462 };
463
464 Here is an example that asynchronously streams the contents of a CSV
465 file:
466
467 use Dancer2;
468 use Text::CSV_XS qw< csv >;
469 use Path::Tiny qw< path >;
470 use JSON::MaybeXS qw< encode_json >;
471 # Create CSV parser
472 my $csv = Text::CSV_XS->new({
473 binary => 1,
474 auto_diag => 1,
475 });
476 get '/' => sub {
477 # delayed response:
478 delayed {
479 # streaming content
480 flush;
481 # Read each row and stream it in JSON
482 my $fh = path('filename.csv')->openr_utf8;
483 while ( my $row = $csv->getline($fh) ) {
484 content encode_json $row;
485 }
486 # close user connection
487 done;
488 } on_error => sub {
489 my ($error) = @_;
490 warning 'Failed to stream to user: ' . request->remote_address;
491 };
492 };
493
494 NOTE: If you just want to send a file's contents asynchronously, use
495 send_file($filename) instead of "delayed", as it will automatically
496 take advantage of any asynchronous capability.
497
498 Action Skipping
499 An action can choose not to serve the current request and ask Dancer2
500 to process the request with the next matching route.
501
502 This is done with the pass keyword, like in the following example
503
504 get '/say/:word' => sub {
505 pass if route_parameters->get('word') =~ /^\d+$/;
506 "I say a word: " . route_parameters->get('word');
507 };
508
509 get '/say/:number' => sub {
510 "I say a number: " . route_parameters->get('number');
511 };
512
514 Hooks are code references (or anonymous subroutines) that are triggered
515 at specific moments during the resolution of a request. They are set
516 up using the hook keyword.
517
518 Many of them are provided by Dancer2's core, but plugins and engines
519 can also define their own.
520
521 • "before" hooks
522
523 "before" hooks are evaluated before each request within the context
524 of the request and receives as argument the app (a
525 Dancer2::Core::App object).
526
527 It's possible to define variables which will be accessible in the
528 action blocks with the var keyword.
529
530 hook before => sub {
531 var note => 'Hi there';
532 };
533
534 get '/foo/*' => sub {
535 my ($match) = splat; # 'oversee';
536 vars->{note}; # 'Hi there'
537 };
538
539 For another example, this can be used along with session support to
540 easily give non-logged-in users a login page:
541
542 hook before => sub {
543 if (!session('user') && request->path !~ m{^/login}) {
544 # Pass the original path requested along to the handler:
545 forward '/login', { requested_path => request->path };
546 }
547 };
548
549 The request keyword returns the current Dancer2::Core::Request
550 object representing the incoming request.
551
552 • "after" hooks
553
554 "after" hooks are evaluated after the response has been built by a
555 route handler, and can alter the response itself, just before it's
556 sent to the client.
557
558 This hook runs after a request has been processed, but before the
559 response is sent.
560
561 It receives a Dancer2::Core::Response object, which it can modify
562 if it needs to make changes to the response which is about to be
563 sent.
564
565 The hook can use other keywords in order to do whatever it wants.
566
567 hook after => sub {
568 response->content(
569 q{The "after" hook can alter the response's content here!}
570 );
571 };
572
573 Templates
574 • "before_template_render"
575
576 "before_template_render" hooks are called whenever a template is
577 going to be processed, they are passed the tokens hash which they
578 can alter.
579
580 hook before_template_render => sub {
581 my $tokens = shift;
582 $tokens->{foo} = 'bar';
583 };
584
585 The tokens hash will then be passed to the template with all the
586 modifications performed by the hook. This is a good way to setup
587 some global vars you like to have in all your templates, like the
588 name of the user logged in or a section name.
589
590 • "after_template_render"
591
592 "after_template_render" hooks are called after the view has been
593 rendered. They receive as their first argument the reference to
594 the content that has been produced. This can be used to post-
595 process the content rendered by the template engine.
596
597 hook after_template_render => sub {
598 my $ref_content = shift;
599 my $content = ${$ref_content};
600
601 # do something with $content
602 ${$ref_content} = $content;
603 };
604
605 • "before_layout_render"
606
607 "before_layout_render" hooks are called whenever the layout is
608 going to be applied to the current content. The arguments received
609 by the hook are the current tokens hashref and a reference to the
610 current content.
611
612 hook before_layout_render => sub {
613 my ($tokens, $ref_content) = @_;
614 $tokens->{new_stuff} = 42;
615 $ref_content = \"new content";
616 };
617
618 • "after_layout_render"
619
620 "after_layout_render" hooks are called once the complete content of
621 the view has been produced, after the layout has been applied to
622 the content. The argument received by the hook is a reference to
623 the complete content string.
624
625 hook after_layout_render => sub {
626 my $ref_content = shift;
627 # do something with ${ $ref_content }, which reflects directly
628 # in the caller
629 };
630
631 Error Handling
632 Refer to Error Hooks for details about the following hooks:
633
634 • "init_error"
635
636 • "before_error"
637
638 • "after_error"
639
640 • "on_route_exception"
641
642 File Rendering
643 Refer to File Handler for details on the following hooks:
644
645 • "before_file_render"
646
647 • "after_file_render"
648
649 Serializers
650 • "before_serializer" is called before serializing the content, and
651 receives the content to serialize as an argument.
652
653 hook before_serializer => sub {
654 my $content = shift;
655 ...
656 };
657
658 • "after_serializer" is called after the payload has been serialized,
659 and receives the serialized content as an argument.
660
661 hook after_serializer => sub {
662 my $serialized_content = shift;
663 ...
664 };
665
667 File Handler
668 Whenever a content is produced out of the parsing of a static file, the
669 Dancer2::Handler::File component is used. This component provides two
670 hooks, "before_file_render" and "after_file_render".
671
672 "before_file_render" hooks are called just before starting to parse the
673 file, the hook receives as its first argument the file path that is
674 going to be processed.
675
676 hook before_file_render => sub {
677 my $path = shift;
678 };
679
680 "after_file_render" hooks are called after the file has been parsed and
681 the response content produced. It receives the response object
682 (Dancer2::Core::Response) produced.
683
684 hook after_file_render => sub {
685 my $response = shift;
686 };
687
688 Auto page
689 Whenever a page that matches an existing template needs to be served,
690 the Dancer2::Handler::AutoPage component is used.
691
692 Writing your own
693 A route handler is a class that consumes the
694 Dancer2::Core::Role::Handler role. The class must implement a set of
695 methods: "methods", "regexp" and "code" which will be used to declare
696 the route.
697
698 Let's look at Dancer2::Handler::AutoPage for example.
699
700 First, the matching methods are "get" and "head":
701
702 sub methods { qw(head get) }
703
704 Then, the "regexp" or the path we want to match:
705
706 sub regexp { '/:page' }
707
708 Anything will be matched by this route, since we want to check if
709 there's a view named with the value of the "page" token. If not, the
710 route needs to "pass", letting the dispatching flow to proceed further.
711
712 sub code {
713 sub {
714 my $app = shift;
715 my $prefix = shift;
716
717 my $template = $app->template_engine;
718 if ( !defined $template ) {
719 $app->response->has_passed(1);
720 return;
721 }
722
723 my $page = $app->request->path;
724 my $layout_dir = $template->layout_dir;
725 if ( $page =~ m{^/\Q$layout_dir\E/} ) {
726 $app->response->has_passed(1);
727 return;
728 }
729
730 # remove leading '/', ensuring paths relative to the view
731 $page =~ s{^/}{};
732 my $view_path = $template->view_pathname($page);
733
734 if ( ! $template->pathname_exists( $view_path ) ) {
735 $app->response->has_passed(1);
736 return;
737 }
738
739 my $ct = $template->process( $page );
740 return ( $app->request->method eq 'GET' ) ? $ct : '';
741 };
742 }
743
744 The "code" method passed the Dancer2::Core::App object which provides
745 access to anything needed to process the request.
746
747 A "register" is then implemented to add the route to the registry and
748 if the "auto_page setting" is off, it does nothing.
749
750 sub register {
751 my ($self, $app) = @_;
752
753 return unless $app->config->{auto_page};
754
755 $app->add_route(
756 method => $_,
757 regexp => $self->regexp,
758 code => $self->code,
759 ) for $self->methods;
760 }
761
762 The config parser looks for a "route_handlers" section and any handler
763 defined there is loaded. Thus, any random handler can be added to your
764 app. For example, the default config file for any Dancer2 application
765 is as follows:
766
767 route_handlers:
768 File:
769 public_dir: /path/to/public
770 AutoPage: 1
771
773 Error Pages
774 When an HTTP error occurs (i.e. the action responds with a status code
775 other than 200), this is how Dancer2 determines what page to display.
776
777 • Looks in the "views/" directory for a corresponding template file
778 matching the error code (e.g. "500.tt" or "404.tt"). If such a file
779 exists, it's used to report the error.
780
781 • Next, looks in the "public/" directory for a corresponding HTML
782 file matching the error code (e.g. "500.html" or "404.html"). If
783 such a file exists, it's used to report the error. (Note, however,
784 that if show_errors is set to true, in the case of a 500 error the
785 static HTML page will not be shown, but will be replaced with a
786 default error page containing more informative diagnostics. For
787 more information see Dancer2::Config.)
788
789 • As default, render a generic error page on the fly.
790
791 Execution Errors
792 When an error occurs during the route execution, Dancer2 will render an
793 error page with the HTTP status code 500.
794
795 It's possible either to display the content of the error message or to
796 hide it with a generic error page. This is a choice left to the end-
797 user and can be controlled with the show_errors setting (see above).
798
799 Error Hooks
800 When an error is caught by Dancer2's core, an exception object is built
801 (of the class Dancer2::Core::Error). This class provides a hook to let
802 the user alter the error workflow if needed.
803
804 "init_error" hooks are called whenever an error object is built, the
805 object is passed to the hook.
806
807 hook init_error => sub {
808 my $error = shift;
809 # do something with $error
810 };
811
812 This hook was named before_error_init in Dancer, both names currently
813 are synonyms for backward-compatibility.
814
815 "before_error" hooks are called whenever an error is going to be
816 thrown, it receives the error object as its sole argument.
817
818 hook before_error => sub {
819 my $error = shift;
820 # do something with $error
821 };
822
823 This hook was named before_error_render in Dancer, both names currently
824 are synonyms for backward-compatibility.
825
826 "after_error" hooks are called whenever an error object has been
827 thrown, it receives a Dancer2::Core::Response object as its sole
828 argument.
829
830 hook after_error => sub {
831 my $response = shift;
832 };
833
834 This hook was named after_error_render in Dancer, both names currently
835 are synonyms for backward-compatibility.
836
837 "on_route_exception" is called when an exception has been caught, at
838 the route level, just before rethrowing it higher. This hook receives a
839 Dancer2::Core::App and the error as arguments.
840
841 hook on_route_exception => sub {
842 my ($app, $error) = @_;
843 };
844
846 Handling sessions
847 It's common to want to use sessions to give your web applications
848 state; for instance, allowing a user to log in, creating a session, and
849 checking that session on subsequent requests.
850
851 By default Dancer 2 has Simple sessions enabled. It implements a very
852 simple in-memory session storage. This will be fast and useful for
853 testing, but such sessions will not persist between restarts of your
854 app.
855
856 If you'd like to use a different session engine you must declare it in
857 the configuration file.
858
859 For example to use YAML file base sessions you need to add the
860 following to your config.yml:
861
862 session: YAML
863
864 Or, to enable session support from within your code,
865
866 set session => 'YAML';
867
868 (However, controlling settings is best done from your config file.)
869
870 The Dancer2::Session::YAML backend implements a file-based YAML session
871 storage to help with debugging, but shouldn't be used on production
872 systems.
873
874 There are other session backends, such as Dancer2::Session::Memcached,
875 which are recommended for production use.
876
877 You can then use the session keyword to manipulate the session:
878
879 Storing data in the session
880
881 Storing data in the session is as easy as:
882
883 session varname => 'value';
884
885 Retrieving data from the session
886
887 Retrieving data from the session is as easy as:
888
889 session('varname')
890
891 Or, alternatively,
892
893 session->read("varname")
894
895 Controlling where sessions are stored
896
897 For disc-based session backends like Dancer2::Session::YAML, session
898 files are written to the session dir specified by the "session_dir"
899 setting, which defaults to "./sessions" if not specifically set.
900
901 If you need to control where session files are created, you can do so
902 quickly and easily within your config file, for example:
903
904 session: YAML
905 engines:
906 session:
907 YAML:
908 session_dir: /tmp/dancer-sessions
909
910 If the directory you specify does not exist, Dancer2 will attempt to
911 create it for you.
912
913 Changing session ID
914
915 If you wish to change the session ID (for example on privilege level
916 change):
917
918 my $new_session_id = app->change_session_id
919
920 Destroying a session
921
922 When you're done with your session, you can destroy it:
923
924 app->destroy_session
925
926 Sessions and logging in
927 A common requirement is to check the user is logged in, and, if not,
928 require them to log in before continuing.
929
930 This can easily be handled using a before hook to check their session:
931
932 use Dancer2;
933 set session => "Simple";
934
935 hook before => sub {
936 if (!session('user') && request->path !~ m{^/login}) {
937 forward '/login', { requested_path => request->path };
938 }
939 };
940
941 get '/' => sub { return "Home Page"; };
942
943 get '/secret' => sub { return "Top Secret Stuff here"; };
944
945 get '/login' => sub {
946 # Display a login page; the original URL they requested is available as
947 # query_parameters->get('requested_path'), so could be put in a hidden field in the form
948 template 'login', { path => query_parameters->get('requested_path') };
949 };
950
951 post '/login' => sub {
952 # Validate the username and password they supplied
953 if (body_parameters->get('user') eq 'bob' && body_parameters->get('pass') eq 'letmein') {
954 session user => body_parameters->get('user');
955 redirect body_parameters->get('path') || '/';
956 } else {
957 redirect '/login?failed=1';
958 }
959 };
960
961 dance();
962
963 Here is what the corresponding "login.tt" file should look like. You
964 should place it in a directory called "views/":
965
966 <html>
967 <head>
968 <title>Session and logging in</title>
969 </head>
970 <body>
971 <form action='/login' method='POST'>
972 User Name : <input type='text' name='user'/>
973 Password: <input type='password' name='pass' />
974
975 <!-- Put the original path requested into a hidden
976 field so it's sent back in the POST and can be
977 used to redirect to the right page after login -->
978 <input type='hidden' name='path' value='<% path %>'/>
979
980 <input type='submit' value='Login' />
981 </form>
982 </body>
983 </html>
984
985 Of course, you'll probably want to validate your users against a
986 database table, or maybe via IMAP/LDAP/SSH/POP3/local system accounts
987 via PAM etc. Authen::Simple is probably a good starting point here!
988
989 A simple working example of handling authentication against a database
990 table yourself (using Dancer2::Plugin::Database which provides the
991 "database" keyword, and Crypt::SaltedHash to handle salted hashed
992 passwords (well, you wouldn't store your users passwords in the clear,
993 would you?)) follows:
994
995 post '/login' => sub {
996 my $user_value = body_parameters->get('user');
997 my $pass_value = body_parameters->get('pass');
998
999 my $user = database->quick_select('users',
1000 { username => $user_value }
1001 );
1002 if (!$user) {
1003 warning "Failed login for unrecognised user $user_value";
1004 redirect '/login?failed=1';
1005 } else {
1006 if (Crypt::SaltedHash->validate($user->{password}, $pass_value))
1007 {
1008 debug "Password correct";
1009 # Logged in successfully
1010 session user => $user;
1011 redirect body_parameters->get('path') || '/';
1012 } else {
1013 debug("Login failed - password incorrect for " . $user_value);
1014 redirect '/login?failed=1';
1015 }
1016 }
1017 };
1018
1019 Retrieve complete hash stored in session
1020
1021 Get complete hash stored in session:
1022
1023 my $hash = session;
1024
1025 Writing a session engine
1026 In Dancer 2, a session backend consumes the role
1027 Dancer2::Core::Role::SessionFactory.
1028
1029 The following example using the Redis session demonstrates how session
1030 engines are written in Dancer 2.
1031
1032 First thing to do is to create the class for the session engine, we'll
1033 name it "Dancer2::Session::Redis":
1034
1035 package Dancer2::Session::Redis;
1036 use Moo;
1037 with 'Dancer2::Core::Role::SessionFactory';
1038
1039 we want our backend to have a handle over a Redis connection. To do
1040 that, we'll create an attribute "redis"
1041
1042 use JSON;
1043 use Redis;
1044 use Dancer2::Core::Types; # brings helper for types
1045
1046 has redis => (
1047 is => 'rw',
1048 isa => InstanceOf['Redis'],
1049 lazy => 1,
1050 builder => '_build_redis',
1051 );
1052
1053 The lazy attribute says to Moo that this attribute will be built
1054 (initialized) only when called the first time. It means that the
1055 connection to Redis won't be opened until necessary.
1056
1057 sub _build_redis {
1058 my ($self) = @_;
1059 Redis->new(
1060 server => $self->server,
1061 password => $self->password,
1062 encoding => undef,
1063 );
1064 }
1065
1066 Two more attributes, "server" and "password" need to be created. We do
1067 this by defining them in the config file. Dancer2 passes anything
1068 defined in the config to the engine creation.
1069
1070 # config.yml
1071 ...
1072 engines:
1073 session:
1074 Redis:
1075 server: foo.mydomain.com
1076 password: S3Cr3t
1077
1078 The server and password entries are now passed to the constructor of
1079 the Redis session engine and can be accessed from there.
1080
1081 has server => (is => 'ro', required => 1);
1082 has password => (is => 'ro');
1083
1084 Next, we define the subroutine "_retrieve" which will return a session
1085 object for a session ID it has passed. Since in this case, sessions are
1086 going to be stored in Redis, the session ID will be the key, the
1087 session the value. So retrieving is as easy as doing a get and
1088 decoding the JSON string returned:
1089
1090 sub _retrieve {
1091 my ($self, $session_id) = @_;
1092 my $json = $self->redis->get($session_id);
1093 my $hash = from_json( $json );
1094 return bless $hash, 'Dancer2::Core::Session';
1095 }
1096
1097 The "_flush" method is called by Dancer when the session needs to be
1098 stored in the backend. That is actually a write to Redis. The method
1099 receives a "Dancer2::Core::Session" object and is supposed to store it.
1100
1101 sub _flush {
1102 my ($self, $session) = @_;
1103 my $json = encode_json( { %{ $session } } );
1104 $self->redis->set($session->id, $json);
1105 }
1106
1107 For the "_destroy" method which is supposed to remove a session from
1108 the backend, deleting the key from Redis is enough.
1109
1110 sub _destroy {
1111 my ($self, $session_id) = @_;
1112 $self->redis->del($session_id);
1113 }
1114
1115 The "_sessions" method which is supposed to list all the session IDs
1116 currently stored in the backend is done by listing all the keys that
1117 Redis has.
1118
1119 sub _sessions {
1120 my ($self) = @_;
1121 my @keys = $self->redis->keys('*');
1122 return \@keys;
1123 }
1124
1125 The session engine is now ready.
1126
1127 The Session keyword
1128
1129 Dancer2 maintains two session layers.
1130
1131 The first layer, Dancer2::Core::Session provides a session object which
1132 represents the current session. You can read from it as many times as
1133 you want, and write to it as many times as you want.
1134
1135 The second layer is the session engine (Dancer2::Session::Simple is one
1136 example), which is used in order to implement the reading and writing
1137 from the actual storage. This is read only once, when a request comes
1138 in (using a cookie whose value is "dancer.session" by default). At the
1139 end of a request, all the data you've written will be flushed to the
1140 engine itself, which will do the actual write to the storage (whether
1141 it's in a hash in memory, in Memcache, or in a database).
1142
1144 Returning plain content is all well and good for examples or trivial
1145 apps, but soon you'll want to use templates to maintain separation
1146 between your code and your content. Dancer2 makes this easy.
1147
1148 Your route handlers can use the template keyword to render templates.
1149
1150 Views
1151 In Dancer2, a file which holds a template is called a view. Views are
1152 located in the "appdir/views" directory.
1153
1154 You can change this location by changing the setting 'views'. For
1155 instance if your templates are located in the 'templates' directory, do
1156 the following:
1157
1158 set views => path( app->location , 'templates' );
1159
1160 By default, the internal template engine Dancer2::Template::Simple is
1161 used, but you may want to upgrade to Template Toolkit
1162 <http://www.template-toolkit.org/>. If you do so, you have to enable
1163 this engine in your settings as explained in
1164 Dancer2::Template::TemplateToolkit and you'll also have to install the
1165 Template module.
1166
1167 In order to render a view, just call the template keyword at the end of
1168 the action by giving the view name and the HASHREF of tokens to
1169 interpolate in the view (note that for convenience, the request,
1170 session, params and vars are automatically accessible in the view,
1171 named "request", "session", "params", and "vars") - for example:
1172
1173 hook before => sub { var time => scalar(localtime) };
1174
1175 get '/hello/:name' => sub {
1176 my $name = route_parameters->get('name');
1177 template 'hello.tt', { name => $name };
1178 };
1179
1180 The template "hello.tt" could contain, for example:
1181
1182 <p>Hi there, [% name %]!</p>
1183 <p>You're using [% request.user_agent %]</p>
1184 [% IF session.username %]
1185 <p>You're logged in as [% session.username %]</p>
1186 [% END %]
1187 It's currently [% vars.time %]
1188
1189 For a full list of the tokens automatically added to your template
1190 (like "session", "request", and "vars", refer to
1191 Dancer2::Core::Role::Template).
1192
1193 By default, views use a .tt extension. This can be overridden by
1194 setting the "extension" attribute in the template engine configuration:
1195
1196 set engines => {
1197 template => {
1198 template_toolkit => {
1199 extension => 'foo',
1200 },
1201 },
1202 };
1203
1204 Layouts
1205 A layout is a special view, located in the layouts directory (inside
1206 the views directory) which must have a token named "content". That
1207 token marks the place where to render the action view. This lets you
1208 define a global layout for your actions, and have each individual view
1209 contain only specific content. This is a good thing and helps avoid
1210 lots of needless duplication of HTML. :)
1211
1212 For example, the layout views/layouts/main.tt:
1213
1214 <html>
1215 <head>...</head>
1216 <body>
1217 <div id="header">
1218 ...
1219 </div>
1220
1221 <div id="content">
1222 [% content %]
1223 </div>
1224
1225 </body>
1226 </html>
1227
1228 You can tell your app which layout to use with "layout: name" in the
1229 config file, or within your code:
1230
1231 set layout => 'main';
1232
1233 You can control which layout to use (or whether to use a layout at all)
1234 for a specific request without altering the layout setting by passing
1235 an options hashref as the third param to the template keyword:
1236
1237 template 'index.tt', {}, { layout => undef };
1238
1239 If your application is not mounted under root ("/"), you can use a
1240 "before_template_render" hook instead of hardcoding the path into your
1241 application for your CSS, images and JavaScript:
1242
1243 hook before_template_render => sub {
1244 my $tokens = shift;
1245 $tokens->{uri_base} = request->base->path;
1246 };
1247
1248 Then in your layout, modify your CSS inclusion as follows:
1249
1250 <link rel="stylesheet" href="[% uri_base %]/css/style.css" />
1251
1252 From now on you can mount your application wherever you want, without
1253 any further modification of the CSS inclusion.
1254
1255 Encoding
1256 If you use Plack and have a Unicode problem with your Dancer2
1257 application, don't forget to check if you have set your template engine
1258 to use Unicode, and set the default charset to UTF-8. So, if you are
1259 using template toolkit, your config file will look like this:
1260
1261 charset: UTF-8
1262 engines:
1263 template:
1264 template_toolkit:
1265 ENCODING: utf8
1266
1267 Default Template Variables
1268 Every template knows about the following variables, which are provided
1269 by Dancer2::Core::Role::Template. Some are similar to the keywords you
1270 can use in the Perl part of your Dancer2 application.
1271
1272 • perl_version
1273
1274 Current version of perl, effectively $^V
1275 <http://perldoc.perl.org/perlvar.html#%24%5eV>.
1276
1277 • dancer_version
1278
1279 Current version of Dancer2, effectively "Dancer2->VERSION".
1280
1281 • settings
1282
1283 A hash of the application configuration. This is like the config
1284 keyword.
1285
1286 • request
1287
1288 The current request object. This is like the request keyword.
1289
1290 • params
1291
1292 A hash reference of all the parameters.
1293
1294 Currently the equivalent of "$request->params", and like the params
1295 keyword.
1296
1297 • vars
1298
1299 The list of request variables, which is what you would get if you
1300 called the vars keyword.
1301
1302 • session
1303
1304 The current session data, if a session exists. This is like the
1305 session keyword.
1306
1308 Static Directory
1309 Static files are served from the ./public directory. You can specify a
1310 different location by setting the "public_dir" option:
1311
1312 set public_dir => path( app->location , 'static' );
1313
1314 When you modify default public_dir you have to set "static_handler"
1315 option.
1316
1317 set static_handler => true;
1318
1319 Note that the public directory name is not included in the URL. A file
1320 ./public/css/style.css is made available as
1321 <http://example.com/css/style.css>.
1322
1323 Static File from a Route Handler
1324 It's possible for a route handler to send a static file, as follows:
1325
1326 get '/download/*' => sub {
1327 my ($file) = splat;
1328
1329 send_file $file;
1330 };
1331
1332 Or even if you want your index page to be a plain old index.html file,
1333 just do:
1334
1335 get '/' => sub {
1336 send_file '/index.html'
1337 };
1338
1340 Files are uploaded in Dancer2 using the class
1341 Dancer2::Core::Request::Upload. The objects are accessible within the
1342 route handlers using the "upload" keyword:
1343
1344 post '/upload' => sub {
1345 my $upload = upload('file_input_name'); # upload object
1346 $upload->copy_to('Uploads/');
1347 };
1348
1350 Configuration and environments
1351 Configuring a Dancer2 application can be done in many ways. The easiest
1352 one (and maybe the dirtiest) is to put all your settings statements at
1353 the top of your script, before calling the dance() method.
1354
1355 Other ways are possible: for example, you can define all your settings
1356 in the file "appdir/config.yml". For this, you must have installed the
1357 YAML module, and of course, write the config file in YAML.
1358
1359 That's better than the first option, but it's still not perfect as you
1360 can't switch easily from an environment to another without rewriting
1361 the config file.
1362
1363 A better solution is to have one config.yml file with default global
1364 settings, like the following:
1365
1366 # appdir/config.yml
1367 logger: 'file'
1368 layout: 'main'
1369
1370 And then write as many environment files as you like in
1371 "appdir/environments". That way, the appropriate environment config
1372 file will be loaded according to the running environment (if none is
1373 specified, it will be 'development').
1374
1375 You can change the running environment when starting your app using the
1376 "plackup" command's "--env" or "--E" switch:
1377
1378 plackup -E production bin/app.psgi
1379
1380 Altenatively, you can set the "DANCER_ENVIRONMENT"
1381 <https://metacpan.org/pod/Dancer2::Config#DANCER_ENVIRONMENT>
1382 environment variable in the shell or in your web server's configuration
1383 file.
1384
1385 Typically, you'll want to set the following values in a development
1386 config file:
1387
1388 # appdir/environments/development.yml
1389 log: 'debug'
1390 startup_info: 1
1391 show_errors: 1
1392
1393 And in a production one:
1394
1395 # appdir/environments/production.yml
1396 log: 'warning'
1397 startup_info: 0
1398 show_errors: 0
1399
1400 Please note that you are not limited to writing configuration files in
1401 YAML. Dancer2 supports any file format that is supported by
1402 Config::Any, such as JSON, XML, INI files, and Apache-style config
1403 files. See the Dancer2 configuration guide
1404 <https://metacpan.org/pod/Dancer2::Config#MANIPULATING-SETTINGS-VIA-
1405 CONFIGURATION-FILES> for more information.
1406
1407 Accessing configuration information
1408 A Dancer2 application can use the "config" keyword to easily access the
1409 settings within its config file, for instance:
1410
1411 get '/appname' => sub {
1412 return "This is " . config->{appname};
1413 };
1414
1415 This makes keeping your application's settings all in one place simple
1416 and easy - you shouldn't need to worry about implementing all that
1417 yourself. :)
1418
1419 Settings
1420 It's possible to change almost every parameter of the application via
1421 the settings mechanism.
1422
1423 A setting is a key/value pair assigned by the keyword set:
1424
1425 set setting_name => 'setting_value';
1426
1427 More usefully, settings can be defined in a configuration file.
1428 Environment-specific settings can also be defined in environment-
1429 specific files (for instance, you do not want to show error stacktraces
1430 in production, and might want extra logging in development).
1431
1432 Serializers
1433 When writing a webservice, data serialization/deserialization is a
1434 common issue to deal with. Dancer2 can automatically handle that for
1435 you, via a serializer.
1436
1437 When setting up a serializer, a new behaviour is authorized for any
1438 route handler you define: any non-scalar response will be rendered as a
1439 serialized string, via the current serializer.
1440
1441 Here is an example of a route handler that will return a hashref:
1442
1443 use Dancer2;
1444 set serializer => 'JSON';
1445
1446 get '/user/:id/' => sub {
1447 { foo => 42,
1448 number => 100234,
1449 list => [qw(one two three)],
1450 }
1451 };
1452
1453 Dancer2 will render the response via the current serializer.
1454
1455 Hence, with the JSON serializer set, the route handler above would
1456 result in a content like the following:
1457
1458 {"number":100234,"foo":42,"list":["one","two","three"]}
1459
1460 If you send a value which is validated serialized data, but is not in
1461 the form a key and value pair (such as a serialized string or a JSON
1462 array), the data will not be available in "params" but will be
1463 available in "request->data".
1464
1465 The following serializers are available, be aware they dynamically
1466 depend on Perl modules you may not have on your system.
1467
1468 • JSON
1469
1470 Requires JSON.
1471
1472 • YAML
1473
1474 Requires YAML,
1475
1476 • XML
1477
1478 Requires XML::Simple.
1479
1480 • Mutable
1481
1482 Will try to find the appropriate serializer using the Content-Type
1483 and Accept-type header of the request.
1484
1485 Importing using Appname
1486 An app in Dancer2 uses the class name (defined by the "package"
1487 function) to define the App name. Thus separating the App to multiple
1488 files, actually means creating multiple applications. This means that
1489 any engine defined in an application, because the application is a
1490 complete separate scope, will not be available to a different
1491 application:
1492
1493 package MyApp::User {
1494 use Dancer2;
1495 set serializer => 'JSON';
1496 get '/view' => sub {...};
1497 }
1498
1499 package MyApp::User::Edit {
1500 use Dancer2;
1501 get '/edit' => sub {...};
1502 }
1503
1504 These are two different Dancer2 Apps. They have different scopes,
1505 contexts, and thus different engines. While "MyApp::User" has a
1506 serializer defined, "MyApp::User::Edit" will not have that
1507 configuration.
1508
1509 By using the import option "appname", we can ask Dancer2 to extend an
1510 App without creating a new one:
1511
1512 package MyApp::User {
1513 use Dancer2;
1514 set serializer => 'JSON';
1515 get '/view' => sub {...};
1516 }
1517
1518 package MyApp::User::Edit {
1519 use Dancer2 appname => 'MyApp::User'; # extending MyApp::User
1520 get '/edit' => sub {...};
1521 }
1522
1523 The import option "appname" allows you to seamlessly extend Dancer2
1524 Apps without creating unnecessary additional applications or repeat any
1525 definitions. This allows you to spread your application routes across
1526 multiple files and allow ease of mind when developing it, and
1527 accommodate multiple developers working on the same codebase.
1528
1529 # app.pl
1530 use MyApp::User;
1531 use MyApp::User::Edit;
1532
1533 # single application composed of routes provided in multiple files
1534 MyApp::User->to_app;
1535
1536 This way only one class needs to be loaded while creating an app:
1537
1538 # app.pl:
1539 use MyApp::User;
1540 MyApp::User->to_app;
1541
1543 Configuring logging
1544 It's possible to log messages generated by the application and by
1545 Dancer2 itself.
1546
1547 To start logging, select the logging engine you wish to use with the
1548 "logger" setting; Dancer2 includes built-in log engines named "file"
1549 and "console", which log to a logfile and to the console respectively.
1550
1551 To enable logging to a file, add the following to your config file:
1552
1553 logger: 'file'
1554
1555 Then you can choose which kind of messages you want to actually log:
1556
1557 log: 'core' # will log debug, info, warnings, errors,
1558 # and messages from Dancer2 itself
1559 log: 'debug' # will log debug, info, warning and errors
1560 log: 'info' # will log info, warning and errors
1561 log: 'warning' # will log warning and errors
1562 log: 'error' # will log only errors
1563
1564 If you're using the "file" logging engine, a directory "appdir/logs"
1565 will be created and will host one logfile per environment. The log
1566 message contains the time it was written, the PID of the current
1567 process, the message and the caller information (file and line).
1568
1569 Logging your own messages
1570 Just call debug <https://metacpan.org/pod/Dancer2::Manual#debug>, info
1571 <https://metacpan.org/pod/Dancer2::Manual#info>, warning
1572 <https://metacpan.org/pod/Dancer2::Manual#warning> or error
1573 <https://metacpan.org/pod/Dancer2::Manual#error> with your message:
1574
1575 debug "This is a debug message from my app.";
1576
1578 Using Plack::Test
1579 Plack::Test receives a common web request (using standard HTTP::Request
1580 objects), fakes a web server in order to create a proper PSGI request,
1581 and sends it to the web application. When the web application returns a
1582 PSGI response (which Dancer applications do), it will then convert it
1583 to a common web response (as a standard HTTP::Response object).
1584
1585 This allows you to then create requests in your test, create the code
1586 reference for your web application, call them, and receive a response
1587 object, which can then be tested.
1588
1589 Basic Example
1590
1591 Assuming there is a web application:
1592
1593 # MyApp.pm
1594 package MyApp;
1595 use Dancer2;
1596 get '/' => sub {'OK'};
1597 1;
1598
1599 The following test base.t is created:
1600
1601 # base.t
1602 use strict;
1603 use warnings;
1604 use Test::More tests => 2;
1605 use Plack::Test;
1606 use HTTP::Request;
1607 use MyApp;
1608
1609 Creating a coderef for the application using the "to_app" keyword:
1610
1611 my $app = MyApp->to_app;
1612
1613 Creating a test object from Plack::Test for the application:
1614
1615 my $test = Plack::Test->create($app);
1616
1617 Creating the first request object and sending it to the test object to
1618 receive a response:
1619
1620 my $request = HTTP::Request->new( GET => '/' );
1621 my $response = $test->request($request);
1622
1623 It can now be tested:
1624
1625 ok( $response->is_success, '[GET /] Successful request' );
1626 is( $response->content, 'OK', '[GET /] Correct content' );
1627
1628 Putting it together
1629
1630 # base.t
1631 use strict;
1632 use warnings;
1633 use Test::More;
1634 use Plack::Test;
1635 use HTTP::Request::Common;
1636 use MyApp;
1637
1638 my $test = Plack::Test->create( MyApp->to_app );
1639 my $response = $test->request( GET '/' );
1640
1641 ok( $response->is_success, '[GET /] Successful request' );
1642 is( $response->content, 'OK', '[GET /] Correct content' );
1643
1644 done_testing();
1645
1646 Subtests
1647
1648 Tests can be separated using Test::More's "subtest" functionality, thus
1649 creating multiple self-contained tests that don't overwrite each other.
1650
1651 Assuming we have a different app that has two states we want to test:
1652
1653 # MyApp.pm
1654 package MyApp;
1655 use Dancer2;
1656 set serializer => 'JSON';
1657
1658 get '/:user' => sub {
1659 my $user = route_parameters->get('user');
1660
1661 $user and return { user => $user };
1662
1663 return {};
1664 };
1665
1666 1;
1667
1668 This is a contrived example of a route that checks for a user
1669 parameter. If it exists, it returns it in a hash with the key 'user'.
1670 If not, it returns an empty hash
1671
1672 # param.t
1673 use strict;
1674 use warnings;
1675 use Test::More;
1676 use Plack::Test;
1677 use HTTP::Request::Common;
1678 use MyApp;
1679
1680 my $test = Plack::Test->create( MyApp->to_app );
1681
1682 subtest 'A empty request' => sub {
1683 my $res = $test->request( GET '/' );
1684 ok( $res->is_success, 'Successful request' );
1685 is( $res->content '{}', 'Empty response back' );
1686 };
1687
1688 subtest 'Request with user' => sub {
1689 my $res = $test->request( GET '/?user=sawyer_x' );
1690 ok( $res->is_success, 'Successful request' );
1691 is( $res->content '{"user":"sawyer_x"}', 'Empty response back' );
1692 };
1693
1694 done_testing();
1695
1696 Cookies
1697
1698 To handle cookies, which are mostly used for maintaining sessions, the
1699 following modules can be used:
1700
1701 • Test::WWW::Mechanize::PSGI
1702
1703 • LWP::Protocol::PSGI
1704
1705 • HTTP::Cookies
1706
1707 Taking the previous test, assuming it actually creates and uses cookies
1708 for sessions:
1709
1710 # ... all the use statements
1711 use HTTP::Cookies;
1712
1713 my $jar = HTTP::Cookies->new;
1714 my $test = Plack::Test->create( MyApp->to_app );
1715
1716 subtest 'A empty request' => sub {
1717 my $res = $test->request( GET '/' );
1718 ok( $res->is_success, 'Successful request' );
1719 is( $res->content '{}', 'Empty response back' );
1720 $jar->extract_cookies($res);
1721 ok( $jar->as_string, 'We have cookies!' );
1722 };
1723
1724 subtest 'Request with user' => sub {
1725 my $req = GET '/?user=sawyer_x';
1726 $jar->add_cookie_header($req);
1727 my $res = $test->request($req);
1728 ok( $res->is_success, 'Successful request' );
1729 is( $res->content '{"user":"sawyer_x"}', 'Empty response back' );
1730 $jar->extract_cookies($res);
1731
1732 ok( ! $jar->as_string, 'All cookies deleted' );
1733 };
1734
1735 done_testing();
1736
1737 Here a cookie jar is created, all requests and responses, existing
1738 cookies, as well as cookies that were deleted by the response, are
1739 checked.
1740
1741 Accessing the configuration file
1742
1743 By importing Dancer2 in the command line scripts, there is full access
1744 to the configuration using the imported keywords:
1745
1746 use strict;
1747 use warnings;
1748 use Test::More;
1749 use Plack::Test;
1750 use HTTP::Request::Common;
1751 use MyApp;
1752 use Dancer2;
1753
1754 my $appname = config->{'appname'};
1755 diag "Testing $appname";
1756
1757 # ...
1758
1760 Carton
1761 What it does
1762
1763 Carton sets up a local copy of your project prerequisites. You only
1764 need to define them in a file and ask Carton to download all of them
1765 and set them up. When you want to deploy your app, you just carry the
1766 git clone and ask Carton to set up the environment again and you will
1767 then be able to run it.
1768
1769 The benefits are multifold:
1770
1771 • Local Directory copy
1772
1773 By putting all the dependencies in a local directory, you can make
1774 sure they aren't updated by someone else by accident and their
1775 versions locked to the version you picked.
1776
1777 • Sync versions
1778
1779 Deciding which versions of the dependent modules your project needs
1780 allows you to sync this with other developers as well. Now you're
1781 all using the same version and they don't change unless you want
1782 update the versions you want. When updated everyone again uses the
1783 same new version of everything.
1784
1785 • Carry only the requirement, not bundled modules
1786
1787 Instead of bundling the modules, you only actually bundle the
1788 requirements. Carton builds them for you when you need it.
1789
1790 Setting it up
1791
1792 First set up a new app:
1793
1794 $ dancer2 -a MyApp
1795 ...
1796
1797 Delete the files that are not needed:
1798
1799 $ rm -f Makefile.PL MANIFEST MANIFEST.SKIP
1800
1801 Create a git repo:
1802
1803 $ git init && git add . && git commit -m "initial commit"
1804
1805 Add a requirement using the cpanfile format:
1806
1807 $ cat > cpanfile
1808 requires 'Dancer2' => 0.155000;
1809 requires 'Template' => 0;
1810 recommends 'URL::Encode::XS' => 0;
1811 recommends 'CGI::Deurl::XS' => 0;
1812 recommends 'HTTP::Parser::XS' => 0;
1813
1814 Ask carton to set it up:
1815
1816 $ carton install
1817 Installing modules using [...]
1818 Successfully installed [...]
1819 ...
1820 Complete! Modules were install into [...]/local
1821
1822 Now we have two files: cpanfile and cpanfile.snapshot. We add both of
1823 them to our Git repository and we make sure we don't accidentally add
1824 the local/ directory Carton created which holds the modules it
1825 installed:
1826
1827 $ echo local/ >> .gitignore
1828 $ git add .gitignore cpanfile cpanfile.snapshot
1829 $ git commit -m "Start using carton"
1830
1831 When we want to update the versions on the production machine, we
1832 simply call:
1833
1834 $ carton install --deployment
1835
1836 By using --deployment we make sure we only install the modules we have
1837 in our cpanfile.snapshot file and do not fallback to querying the CPAN.
1838
1839 FatPacker
1840 App::FatPacker (using its command line interface, fatpack) packs
1841 dependencies into a single file, allowing you to carry a single file
1842 instead of a directory tree.
1843
1844 As long as your application is pure-Perl, you could create a single
1845 file with your application and all of Dancer2 in it.
1846
1847 The following example will demonstrate how this can be done:
1848
1849 Assuming we have an application in lib/MyApp.pm:
1850
1851 package MyApp;
1852 use Dancer2;
1853 get '/' => sub {'OK'};
1854 1;
1855
1856 And we have a handler in bin/app.pl:
1857
1858 use strict;
1859 use warnings;
1860 use FindBin;
1861 use lib "$FindBin::Bin/../lib";
1862 use MyApp;
1863
1864 MyApp->to_app;
1865
1866 To fatpack it, we begin by tracing the script:
1867
1868 $ fatpack trace bin/app.pl
1869
1870 This creates a fatpacker.trace file. From this we create the packlists:
1871
1872 $ fatpack packlists-for `cat fatpacker.trace` > packlists
1873
1874 The packlists are stored in a file called packlists.
1875
1876 Now we create the tree using the following command:
1877
1878 $ fatpack tree `cat packlists`
1879
1880 The tree is created under the directory fatlib.
1881
1882 Now we create a file containing the dependency tree, and add our script
1883 to it, using the following command:
1884
1885 $ (fatpack file; cat bin/app.pl) > myapp.pl
1886
1887 This creates a file called myapp.pl with everything in it. Dancer2 uses
1888 MIME::Types which has a database of all MIME types and helps translate
1889 those. The small database file containing all of these types is a
1890 binary and therefore cannot be fatpacked. Hence, it needs to be copied
1891 to the current directory so our script can find it:
1892
1893 $ cp fatlib/MIME/types.db .
1894
1896 Plack middlewares
1897 If you want to use Plack middlewares, you need to enable them using
1898 Plack::Builder as such:
1899
1900 # in app.psgi or any other handler
1901 use MyApp;
1902 use Plack::Builder;
1903
1904 builder {
1905 enable 'Deflater';
1906 enable 'Session', store => 'File';
1907 enable 'Debug', panels => [ qw<DBITrace Memory Timer> ];
1908 MyApp->to_app;
1909 };
1910
1911 The nice thing about this setup is that it will work seamlessly through
1912 Plack or through the internal web server.
1913
1914 # load dev web server (without middlewares)
1915 perl -Ilib app.psgi
1916
1917 # load plack web server (with middlewares)
1918 plackup -I lib app.psgi
1919
1920 You do not need to provide different files for either server.
1921
1922 Path-based middlewares
1923
1924 If you want to set up a middleware for a specific path, you can do that
1925 using Plack::Builder which uses Plack::App::URLMap:
1926
1927 # in your app.psgi or any other handler
1928 use MyApp;
1929 use Plack::Builder;
1930
1931 my $special_handler = sub { ... };
1932
1933 builder {
1934 mount '/special' => $special_handler;
1935 mount '/' => MyApp->to_app;
1936 };
1937
1938 Removing default middlewares
1939
1940 By default, a Dancer2 app is automatically wrapped with the following
1941 middleware
1942
1943 • Plack::Middleware::FixMissingBodyInRedirect
1944
1945 • Plack::Middleware::Head
1946
1947 You can configure the setting "no_default_middleware" to a true value
1948 to stop your Dancer2 app being wrapped with these default middleware
1949 layers.
1950
1951 # in you Dancer2 app or config.yml
1952 package MyApp;
1953 use Dancer2
1954
1955 set no_default_middleware => true;
1956
1957 This is necessary if you need to add eTag or ContentMD5 headers to
1958 "HEAD" requests, and you are encouraged to manually add those default
1959 middleware back into your PSGI stack.
1960
1961 Running on Perl web servers with plackup
1962
1963 A number of Perl web servers supporting PSGI are available on CPAN:
1964
1965 • Starman
1966
1967 "Starman" is a high performance web server, with support for
1968 preforking, signals, multiple interfaces, graceful restarts and
1969 dynamic worker pool configuration.
1970
1971 • Twiggy
1972
1973 "Twiggy" is an "AnyEvent" web server, it's light and fast.
1974
1975 • Corona
1976
1977 "Corona" is a "Coro" based web server.
1978
1979 To start your application, just run plackup (see Plack and specific
1980 servers above for all available options):
1981
1982 $ plackup bin/app.psgi
1983 $ plackup -E deployment -s Starman --workers=10 -p 5001 -a bin/app.psgi
1984
1985 As you can see, the scaffolded Perl script for your app can be used as
1986 a PSGI startup file.
1987
1988 Enabling content compression
1989
1990 Content compression (gzip, deflate) can be easily enabled via a Plack
1991 middleware (see "Plack::Middleware" in Plack):
1992 Plack::Middleware::Deflater. It's a middleware to encode the response
1993 body in gzip or deflate, based on the "Accept-Encoding" HTTP request
1994 header.
1995
1996 Enable it as you would enable any Plack middleware. First you need to
1997 install Plack::Middleware::Deflater, then in the handler (usually
1998 app.psgi) edit it to use Plack::Builder, as described above:
1999
2000 use Dancer2;
2001 use MyApp;
2002 use Plack::Builder;
2003
2004 builder {
2005 enable 'Deflater';
2006 MyApp->to_app;
2007 };
2008
2009 To test if content compression works, trace the HTTP request and
2010 response before and after enabling this middleware. Among other things,
2011 you should notice that the response is gzip or deflate encoded, and
2012 contains a header "Content-Encoding" set to "gzip" or "deflate".
2013
2014 Running multiple apps with Plack::Builder
2015
2016 You can use Plack::Builder to mount multiple Dancer2 applications on a
2017 PSGI webserver like Starman.
2018
2019 Start by creating a simple app.psgi file:
2020
2021 use OurWiki; # first app
2022 use OurForum; # second app
2023 use Plack::Builder;
2024
2025 builder {
2026 mount '/wiki' => OurWiki->to_app;
2027 mount '/forum' => OurForum->to_app;
2028 };
2029
2030 and now use Starman
2031
2032 plackup -a app.psgi -s Starman
2033
2034 Currently this still demands the same appdir for both (default
2035 circumstance) but in a future version this will be easier to change
2036 while staying very simple to mount.
2037
2038 Running from Apache with Plack
2039
2040 You can run your app from Apache using PSGI (Plack), with a config like
2041 the following:
2042
2043 <VirtualHost myapp.example.com>
2044 ServerName www.myapp.example.com
2045 ServerAlias myapp.example.com
2046 DocumentRoot /websites/myapp.example.com
2047
2048 <Directory /home/myapp/myapp>
2049 AllowOverride None
2050 Order allow,deny
2051 Allow from all
2052 </Directory>
2053
2054 <Location />
2055 SetHandler perl-script
2056 PerlResponseHandler Plack::Handler::Apache2
2057 PerlSetVar psgi_app /websites/myapp.example.com/app.psgi
2058 </Location>
2059
2060 ErrorLog /websites/myapp.example.com/logs/error_log
2061 CustomLog /websites/myapp.example.com/logs/access_log common
2062 </VirtualHost>
2063
2064 To set the environment you want to use for your application (production
2065 or development), you can set it this way:
2066
2067 <VirtualHost>
2068 ...
2069 SetEnv DANCER_ENVIRONMENT "production"
2070 ...
2071 </VirtualHost>
2072
2074 Writing a plugin
2075 See "Writing the plugin" in Dancer2::Plugin for information on how to
2076 author a new plugin for Dancer2.
2077
2079 By default, "use Dancer2" exports all the DSL keywords and sets up the
2080 webapp under the name of the current package. The following tags
2081 control exports and webapp namespace.
2082
2083 • !keyword
2084
2085 If you want to prevent Dancer2 from exporting specific keywords
2086 (perhaps you plan to implement them yourself in a different way, or
2087 they clash with another module you're loading), you can simply
2088 exclude them:
2089
2090 use Test::More;
2091 use Dancer2 qw(!pass);
2092
2093 The above would import all keywords as usual, with the exception of
2094 "pass".
2095
2096 • appname
2097
2098 A larger application may split its source between several packages
2099 to aid maintainability. Dancer2 will create a separate application
2100 for each package, each having separate hooks, config and/or
2101 engines. You can force Dancer2 to collect the route and hooks into
2102 a single application with the "appname" tag; e.g.
2103
2104 package MyApp;
2105 use Dancer2;
2106 get '/foo' => sub {...};
2107
2108 package MyApp::Private;
2109 use Dancer2 appname => MyApp;
2110 get '/bar' => sub {...};
2111
2112 The above would add the "bar" route to the MyApp application.
2113 Dancer2 will not create an application with the name
2114 "MyApp::Private".
2115
2116 • :nopragmas
2117
2118 By default Dancer2 will import three pragmas: strict, warnings, and
2119 utf8. If you require control over the imported pragmas, you can add
2120 :nopragmas to the importing flags, in which case Dancer2 will not
2121 import any pragmas:
2122
2123 use strict;
2124 use warnings;
2125 no warnings 'experimental::smartmatch'; # for example...
2126 use Dancer2 ':nopragmas'; # do not touch the existing pragmas
2127
2128 This way importing "Dancer2" does not change the existing pragmas
2129 setup you have.
2130
2131 When you "use Dancer2", you get an "import" method added into the
2132 current package. This will override previously declared import methods
2133 from other sources, such as Exporter. Dancer2 applications support the
2134 following tags on import:
2135
2136 • with
2137
2138 The "with" tag allows an app to pass one or more config entries to
2139 another app, when it "use"s it.
2140
2141 package MyApp;
2142 use Dancer2;
2143
2144 BEGIN { set session => 'YAML' };
2145 use Blog with => { session => engine('session') };
2146
2147 In this example, the session engine is passed to the "Blog" app.
2148 That way, anything done in the session will be shared between both
2149 apps.
2150
2151 Anything that is defined in the config entry can be passed that
2152 way. If we want to pass the whole config object, it can be done
2153 like so:
2154
2155 use SomeApp with => { %{config()} };
2156
2158 Dancer2 provides you with a DSL (Domain-Specific Language) which makes
2159 implementing your web application trivial.
2160
2161 For example, take the following example:
2162
2163 use Dancer2;
2164
2165 get '/hello/:name' => sub {
2166 my $name = route_parameters->get('name');
2167 };
2168 dance;
2169
2170 "get" and "route_parameters" are keywords provided by Dancer2.
2171
2172 See "DSL Keywords manual" in Dancer2::Manual::Keywords for a complete
2173 list of keywords provided by Dancer2.
2174
2176 Dancer Core Developers
2177
2179 This software is copyright (c) 2022 by Alexis Sukrieh.
2180
2181 This is free software; you can redistribute it and/or modify it under
2182 the same terms as the Perl 5 programming language system itself.
2183
2184
2185
2186perl v5.36.0 2023-01-20 Dancer2::Manual(3)