1Dancer2::Cookbook(3)  User Contributed Perl Documentation Dancer2::Cookbook(3)
2
3
4

NAME

6       Dancer2::Cookbook - Example-driven quick-start to the Dancer2 web
7       framework
8

VERSION

10       version 0.301004
11

DESCRIPTION

13       A quick-start guide with examples to get you up and running with the
14       Dancer2 web framework. This document will be twice as useful if you
15       finish reading the manual (Dancer2::Manual) first, but that is not
16       required... :-)
17

BEGINNER'S DANCE

19   A simple Dancer2 web app
20       Dancer2 has been designed to be easy to work with - it's trivial to
21       write a simple web app, but still has the power to work with larger
22       projects. To start with, let's make an incredibly simple "Hello World"
23       example:
24
25           #!/usr/bin/env perl
26
27           use Dancer2;
28
29           get '/hello/:name' => sub {
30               return "Why, hello there " . route_parameters->get('name');
31           };
32
33           dance;
34
35       Yes - the above is a fully-functioning web app; running that script
36       will launch a webserver listening on the default port (3000). Now you
37       can make a request:
38
39           $ curl http://localhost:3000/hello/Bob
40           Why, hello there Bob
41
42       and it will say hello. The ":name" part is a named parameter within the
43       route specification, whose value is made available through
44       "route_parameters".
45
46       Note that you don't need to use the "strict" and "warnings" pragmas;
47       they are already loaded by Dancer2.
48
49   Default Route
50       In case you want to avoid a 404 error, or handle multiple routes in the
51       same way and you don't feel like configuring all of them, you can set
52       up a default route handler.
53
54       The default route handler will handle any request that doesn't get
55       served by any other route.
56
57       All you need to do is set up the following route as the last route:
58
59           any qr{.*} => sub {
60               status 'not_found';
61               template 'special_404', { path => request->path };
62           };
63
64       Then you can set up the template like so:
65
66           You tried to reach [% path %], but it is unavailable at the moment.
67
68           Please try again or contact us at <contact@example.com>.
69
70   Using the "auto_page" feature for automatic route creation
71       For simple "static" pages you can simply enable the "auto_page" config
72       setting; this means you don't need to declare a route handler for those
73       pages; if a request is for "/foo/bar", Dancer2 will check for a
74       matching view (e.g. "/foo/bar.tt") and render it with the default
75       layout, if found. For full details, see the documentation for the
76       auto_page setting.
77
78   Enabling and disabling routes with config and Module::Runtime
79       For various reasons you may want to be able to turn routes on and off
80       in your app without having to comment out sections of code. This is
81       easily accomplished if you encapsulate each route handler (or group of
82       related handlers) in a separate module, and load the wanted routes at
83       runtime.
84
85       In "MyApp::Route::Foo":
86
87           package MyApp::Route::Foo;
88
89           use Dancer2 appname => 'MyApp';
90
91           get '/foo' => sub { return "bar" };
92
93       In "MyApp::Route::Baz":
94
95           package MyApp::Route::Baz;
96
97           use Dancer2 appname => 'MyApp';
98
99           get '/baz' => sub { return "qux" };
100
101       In your "config.yaml":
102
103           route_modules :
104               Foo : 1
105               Baz : 0
106
107       In your main route controller:
108
109           use Dancer2;
110           use Module::Runtime 'require_module';
111
112           my $module_base = 'MyApp::Route::';
113
114           my %modules = %{ config->{route_modules} };
115
116           my @required_modules = grep { $modules{$_} } keys %modules;
117
118           require_module( $module_base . $_ ) for @required_modules;
119
120       Now your app will expose "/foo" but requests to "/baz" will get a 404
121       response.
122
123   Simplifying AJAX queries with the Ajax plugin
124       As an AJAX query is just an HTTP query, it's similar to a GET or POST
125       route.  You may ask yourself why you may want to use the "ajax" keyword
126       (from the Dancer2::Plugin::Ajax plugin) instead of a simple "get".
127
128       Let's say you have a path like "/user/:user" in your application. You
129       may want to be able to serve this page with a layout and HTML content.
130       But you may also want to be able to call this same url from a
131       javascript query using AJAX.
132
133       So, instead of having the following code:
134
135           get '/user/:user' => sub {
136                if ( request->is_ajax ) {
137                    # create xml, set headers to text/xml, blablabla
138                     header( 'Content-Type'  => 'text/xml' );
139                     header( 'Cache-Control' =>  'no-store, no-cache, must-revalidate' );
140                     to_xml({...})
141                } else {
142                    template users => {...}
143                }
144           };
145
146       you can have
147
148           ajax '/user/:user' => sub {
149                to_xml( {...}, RootName => undef );
150           }
151
152       and
153
154           get '/user/:user' => sub {
155               template users => {...}
156           }
157
158       Because it's an AJAX query, you know you need to return XML content, so
159       the content type of the response is set for you.
160
161       Example: Feeding graph data through AJAX
162
163       Let us assume we are building an application that uses a plotting
164       library to generate a graph and expects to get its data, which is in
165       the form of word count from an AJAX call.
166
167       For the graph, we need the url /data to return a JSON representation of
168       the word count data. Dancer in fact has a "encode_json()" function that
169       takes care of the JSON encapsulation.
170
171            get '/data' => sub {
172                open my $fh, '<', $count_file;
173
174                my %contestant;
175                while (<$fh>) {
176                    chomp;
177                    my ( $date, $who, $count ) = split '\s*,\s*';
178
179                    my $epoch = DateTime::Format::Flexible->parse_datetime($date)->epoch;
180                    my $time = 1000 * $epoch;
181                    $contestant{$who}{$time} = $count;
182                }
183
184                my @json;  # data structure that is going to be JSONified
185
186                while ( my ( $peep, $data ) = each %contestant ) {
187                    push @json, {
188                        label     => $peep,
189                        hoverable => \1,    # so that it becomes JavaScript's 'true'
190                        data => [ map  { [ $_, $data->{$_} ] }
191                                sort { $a <=> $b }
192                                keys %$data ],
193                    };
194                }
195
196                my $beginning = DateTime::Format::Flexible->parse_datetime( "2010-11-01")->epoch;
197                my $end       = DateTime::Format::Flexible->parse_datetime( "2010-12-01")->epoch;
198
199                push @json, {
200                    label => 'de par',
201                    data => [
202                        [$beginning * 1000, 0],
203                        [   DateTime->now->epoch * 1_000,
204                            50_000
205                              * (DateTime->now->epoch - $beginning)
206                              / ($end - $beginning)
207                        ]
208                      ],
209
210                };
211
212                encode_json( \@json );
213            };
214
215       For more serious AJAX interaction, there's also Dancer2::Plugin::Ajax
216       that adds an ajax route handler to the mix.
217
218       Because it's an AJAX query, you know you need to return XML content, so
219       the content type of the response is set for you.
220
221   Using the prefix feature to split your application
222       For better maintainability, you may want to separate some of your
223       application components into different packages. Let's say we have a
224       simple web app with an admin section and want to maintain this in a
225       different package:
226
227           package myapp;
228           use Dancer2;
229           use myapp::admin;
230
231           prefix undef;
232
233           get '/' => sub {...};
234
235           1;
236
237           package myapp::admin;
238           use Dancer2 appname => 'myapp';
239
240           prefix '/admin';
241
242           get '/' => sub {...};
243
244           1;
245
246       The following routes will be generated for us:
247
248           - get /
249           - get /admin/
250           - head /
251           - head /admin/
252
253       By default, a separate application is created for every package that
254       uses Dancer2. The "appname" tag is used to collect routes and hooks
255       into a single Dancer2 application. In the above example, "appname =>
256       'myapp'" adds the routes from "myapp::admin" to the routes of the app
257       "myapp".
258
259       When using multiple applications please ensure that your path
260       definitions do not overlap. For example, if using a default route as
261       described above, once a request is matched to the default route then no
262       further routes (or applications) would be reached.
263
264   Delivering custom error pages
265       At the Core
266
267       In Dancer2, creating new errors is done by creating a new
268       Dancer2::Core::Error
269
270            my $oopsie = Dancer2::Core::Error->new(
271                status  => 418,
272                message => "This is the Holidays. Tea not acceptable. We want eggnog.",
273                app     => $app,
274            )
275
276       If not given, the status code defaults to a 500, there is no need for a
277       message if we feel taciturn, and while the $app (which is a
278       Dancer2::Core::App object holding all the pieces of information related
279       to the current request) is needed if we want to take advantage of the
280       templates, we can also do without.
281
282       However, to be seen by the end user, we have to populate the
283       Dancer2::Core::Response object with the error's data. This is done via:
284
285            $oopsie->throw($response);
286
287       Or, if we want to use the response object already present in the $app
288       (which is usually the case):
289
290            $oopsie->throw;
291
292       This populates the status code of the response, sets its content, and
293       throws a halt() in the dispatch process.
294
295       What it will look like
296
297       The error object has quite a few ways to generate its content.
298
299       First, it can be explicitly given
300
301            my $oopsie = Dancer2::Core::Error->new(
302                content => '<html><body><h1>OMG</h1></body></html>',
303            );
304
305       If the $context was given, the error will check if there is a template
306       by the name of the status code (so, say you're using Template Toolkit,
307       418.tt) and will use it to generate the content, passing it the error's
308       $message, $status code and $title (which, if not specified, will be the
309       standard http error definition for the status code).
310
311       If there is no template, the error will then look for a static page (to
312       continue with our example, 418.html) in the public/ directory.
313
314       And finally, if all of that failed, the error object will fall back on
315       an internal template.
316
317       Errors in Routes
318
319       The simplest way to use errors in routes is:
320
321            get '/xmas/gift/:gift' => sub {
322                die "sorry, we're all out of ponies\n"
323                    if route_parameters->get('gift') eq 'pony';
324            };
325
326       The die will be intercepted by Dancer, converted into an error (status
327       code 500, message set to the dying words) and passed to the response.
328
329       In the cases where more control is required, "send_error()" is the way
330       to go:
331
332            get '/glass/eggnog' => sub {
333                send_error "Sorry, no eggnog here", 418;
334            };
335
336       And if total control is needed:
337
338            get '/xmas/wishlist' => sub {
339                Dancer2::Core::Error->new(
340                    response => response(),
341                    status   => 406,
342                    message  => "nothing but coal for you, I'm afraid",
343                    template => 'naughty/index',
344                )->throw unless user_was_nice();
345
346                ...;
347            };
348
349   Template Toolkit's WRAPPER directive in Dancer2
350       Dancer2 already provides a WRAPPER-like ability, which we call a
351       "layout".  The reason we don't use Template Toolkit's WRAPPER (which
352       also makes us incompatible with it) is because not all template systems
353       support it.  In fact, most don't.
354
355       However, you might want to use it, and be able to define META variables
356       and regular Template::Toolkit variables.
357
358       These few steps will get you there:
359
360       •   Disable the layout in Dancer2
361
362           You can do this by simply commenting (or removing) the "layout"
363           configuration in the config file.
364
365       •   Use the Template Toolkit template engine
366
367           Change the configuration of the template to Template Toolkit:
368
369               # in config.yml
370               template: "template_toolkit"
371
372       •   Tell the Template Toolkit engine which wrapper to use
373
374               # in config.yml
375               # ...
376               engines:
377                   template:
378                       template_toolkit:
379                           WRAPPER: layouts/main.tt
380
381       Done! Everything will work fine out of the box, including variables and
382       META variables.
383
384       However, disabling the internal layout it will also disable the hooks
385       "before_layout_render" and "after_layout_render".
386
387   Customizing Template Toolkit in Dancer2
388       Please see Dancer2::Template::TemplateToolkit for more details.
389
390   Accessing configuration information from a separate script
391       You may want to access your webapp's configuration from outside your
392       webapp. You could, of course, use the YAML module of your choice and
393       load your webapps's "config.yml", but chances are that this is not
394       convenient.
395
396       Use Dancer2 instead. You can simply use the values from "config.yml"
397       and some additional default values:
398
399           # bin/show_app_config.pl
400           use Dancer2;
401           printf "template: %s\n", config->{'template'}; # simple
402           printf "log: %s\n",      config->{'log'};      # undef
403
404       Note that "config->{log}" should result in an uninitialized warning on
405       a default scaffold since the environment isn't loaded and log is
406       defined in the environment and not in "config.yml". Hence "undef".
407
408       Dancer2 will load your "config.yml" configuration file along with the
409       correct environment file located in your "environments" directory.
410
411       The environment is determined by two environment variables in the
412       following order:
413
414       •   DANCER_ENVIRONMENT
415
416       •   PLACK_ENV
417
418       If neither of those is set, it will default to loading the development
419       environment (typically "$webapp/environment/development.yml").
420
421       If you wish to load a different environment, you need to override these
422       variables.
423
424       You can call your script with the environment changed:
425
426           $ PLACK_ENV=production perl bin/show_app_config.pl
427
428       Or you can override them directly in the script (less recommended):
429
430           BEGIN { $ENV{'DANCER_ENVIRONMENT'} = 'production' }
431           use Dancer2;
432
433           ...
434
435   Using DBIx::Class
436       DBIx::Class, also known as DBIC, is one of the many Perl ORM (Object
437       Relational Mapper). It is easy to use DBIC in Dancer2 using the
438       Dancer2::Plugin::DBIC.
439
440       An example
441
442       This example demonstrates a simple Dancer2 application that allows one
443       to search for authors or books. The application is connected to a
444       database, that contains authors, and their books. The website will have
445       one single page with a form, that allows one to query books or authors,
446       and display the results.
447
448       Creating the application
449
450           $ dancer2 -a bookstore
451
452       To use the Template Toolkit as the template engine, we specify it in
453       the configuration file:
454
455           # add in bookstore/config.yml
456           template: template_toolkit
457
458       Creating the view
459
460       We need a view to display the search form, and below, the results, if
461       any. The results will be fed by the route to the view as an arrayref of
462       results. Each result is a hashref, with a author key containing the
463       name of the author, and a books key containing an arrayref of strings :
464       the books names.
465
466           # example of a list of results
467           [ { author => 'author 1',
468               books => [ 'book 1', 'book 2' ],
469             },
470             { author => 'author 2',
471               books => [ 'book 3', 'book 4' ],
472             }
473           ]
474
475
476           # bookstore/views/search.tt
477           <p>
478           <form action="/search">
479           Search query: <input type="text" name="query" />
480           </form>
481           </p>
482           <br>
483
484       An example of the view, displaying the search form, and the results, if
485       any:
486
487           <% IF query.length %>
488             <p>Search query was : <% query %>.</p>
489             <% IF results.size %>
490               Results:
491               <ul>
492               <% FOREACH result IN results %>
493                 <li>Author: <% result.author.replace("((?i)$query)", '<b>$1</b>') %>
494                 <ul>
495                 <% FOREACH book IN result.books %>
496                   <li><% book.replace("((?i)$query)", '<b>$1</b>') %>
497                 <% END %>
498                 </ul>
499               <% END %>
500             <% ELSE %>
501               No result
502             <% END %>
503           <% END %>
504
505       Creating a Route
506
507       A simple route, to be added in the bookstore.pm module:
508
509           # add in bookstore/lib/bookstore.pm
510           get '/search' => sub {
511               my $query   = query_parameters->get('query');
512               my @results = ();
513
514               if ( length $query ) {
515                   @results = _perform_search($query);
516               }
517
518               template search => {
519                   query   => $query,
520                   results => \@results,
521               };
522           };
523
524       Creating a database
525
526       We create a SQLite file database:
527
528           $ sqlite3 bookstore.db
529           CREATE TABLE author(
530             id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
531             firstname text default '' not null,
532             lastname text not null);
533
534           CREATE TABLE book(
535             id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
536             author INTEGER REFERENCES author (id),
537             title text default '' not null );
538
539       Now, to populate the database with some data, we use DBIx::Class:
540
541           # populate_database.pl
542           package My::Bookstore::Schema;
543           use base qw(DBIx::Class::Schema::Loader);
544           package main;
545           my $schema = My::Bookstore::Schema->connect('dbi:SQLite:dbname=bookstore.db');
546           $schema->populate('Author', [
547             [ 'firstname', 'lastname'],
548             [ 'Ian M.',    'Banks'   ],
549             [ 'Richard',   'Matheson'],
550             [ 'Frank',     'Herbert' ],
551           ]);
552           my @books_list = (
553             [ 'Consider Phlebas',    'Banks'    ],
554             [ 'The Player of Games', 'Banks'    ],
555             [ 'Use of Weapons',      'Banks'    ],
556             [ 'Dune',                'Herbert'  ],
557             [ 'Dune Messiah',        'Herbert'  ],
558             [ 'Children of Dune',    'Herbert'  ],
559             [ 'The Night Stalker',   'Matheson' ],
560             [ 'The Night Strangler', 'Matheson' ],
561           );
562           # transform author names into ids
563           $_->[1] = $schema->resultset('Author')->find({ lastname => $_->[1] })->id
564             foreach (@books_list);
565           $schema->populate('Book', [
566             [ 'title', 'author' ],
567             @books_list,
568           ]);
569
570       Then run it in the directory where bookstore.db sits:
571
572           perl populate_database.db
573
574       Using Dancer2::Plugin::DBIC
575
576       There are 2 ways of configuring DBIC to understand how the data is
577       organized in your database:
578
579       •   Use auto-detection
580
581           The configuration file needs to be updated to indicate the use of
582           the Dancer2::Plugin::DBIC plugin, define a new DBIC schema called
583           bookstore and to indicate that this schema is connected to the
584           SQLite database we created.
585
586               # add in bookstore/config.yml
587               plugins:
588                 DBIC:
589                   bookstore:
590                     dsn:  "dbi:SQLite:dbname=bookstore.db"
591
592           Now, "_perform_search" can be implemented using
593           Dancer2::Plugin::DBIC. The plugin gives you access to an additional
594           keyword called schema, which you give the name of schema you want
595           to retrieve. It returns a "DBIx::Class::Schema::Loader" which can
596           be used to get a resultset and perform searches, as per standard
597           usage of DBIX::Class.
598
599               # add in bookstore/lib/bookstore.pm
600               sub _perform_search {
601                   my ($query) = @_;
602                   my $bookstore_schema = schema 'bookstore';
603                   my @results;
604                   # search in authors
605                   my @authors = $bookstore_schema->resultset('Author')->search({
606                     -or => [
607                       firstname => { like => "%$query%" },
608                       lastname  => { like => "%$query%" },
609                     ]
610                   });
611                   push @results, map {
612                       { author => join(' ', $_->firstname, $_->lastname),
613                         books => [],
614                       }
615                   } @authors;
616                   my %book_results;
617                   # search in books
618                   my @books = $bookstore_schema->resultset('Book')->search({
619                       title => { like => "%$query%" },
620                   });
621                   foreach my $book (@books) {
622                       my $author_name = join(' ', $book->author->firstname, $book->author->lastname);
623                       push @{$book_results{$author_name}}, $book->title;
624                   }
625                   push @results, map {
626                       { author => $_,
627                         books => $book_results{$_},
628                       }
629                   } keys %book_results;
630                   return @results;
631               }
632
633       •   Use home made schema classes
634
635           The DBIx::Class::MooseColumns lets you write the DBIC schema
636           classes using Moose. The schema classes should be put in a place
637           that Dancer2 will find. A good place is in bookstore/lib/.
638
639           Once your schema classes are in place, all you need to do is modify
640           config.yml to specify that you want to use them, instead of the
641           default auto-detection method:
642
643               # change in bookstore/config.yml
644               plugins:
645                 DBIC:
646                   bookstore:
647                     schema_class: My::Bookstore::Schema
648                     dsn: "dbi:SQLite:dbname=bookstore.db"
649
650           Starting the application: Our bookstore lookup application can now
651           be started using the built-in server:
652
653               # start the web application
654               plackup bin/app.psgi
655
656   Authentication
657       Writing a form for authentication is simple: we check the user
658       credentials on a request and decide whether to continue or redirect
659       them to a form.  The form allows them to submit their username and
660       password and we save that and create a session for them so when they
661       now try the original request, we recognize them and allow them in.
662
663       Basic Application
664
665       The application is fairly simple. We have a route that needs
666       authentication, we have a route for showing the login page, and we have
667       a route for posting login information and creating a session.
668
669            package MyApp;
670            use Dancer2;
671
672            get '/' => sub {
673                session('user')
674                    or redirect('/login');
675
676                template index => {};
677            };
678
679            get '/login' => sub {
680                template login => {};
681            };
682
683            post '/login' => sub {
684                my $username  = query_parameters->get('username');
685                my $password  = query_parameters->get('password');
686                my $redir_url = query_parameters->get('redirect_url') || '/login';
687
688                $username eq 'john' && $password eq 'correcthorsebatterystaple'
689                    or redirect $redir_url;
690
691                session user => $username;
692                redirect $redir_url;
693            };
694
695       Tiny Authentication Helper
696
697       Dancer2::Plugin::Auth::Tiny allows you to abstract away not only the
698       part that checks whether the session exists, but to also generate a
699       redirect with the right path and return URL.
700
701       We simply have to define what routes needs a login using Auth::Tiny's
702       "needs" keyword.
703
704            get '/' => needs login => sub {
705                template index => {};
706            };
707
708       It creates a proper return URL using "uri_for" and the address from
709       which the user arrived.
710
711       We can thus decorate all of our private routes to require
712       authentication in this manner. If a user does not have a session, it
713       will automatically forward it to /login, in which we would render a
714       form for the user to send a login request.
715
716       Auth::Tiny even provides a new parameter, "return_url", which can be
717       used to send the user back to their original requested path.
718
719       Password Hashing
720
721       Dancer2::Plugin::Passphrase provides a simple passwords-as-objects
722       interface with sane defaults for hashed passwords which you can use in
723       your web application. It uses bcrypt as the default but supports
724       anything the Digest interface does.
725
726       Assuming we have the original user-creation form submitting a username
727       and password:
728
729            package MyApp;
730            use Dancer2;
731            use Dancer2::Plugin::Passphrase;
732            post '/register' => sub {
733                my $username = query_parameters->get('username');
734                my $password = passphrase(
735                    query_parameters->get('password')
736                )->generate;
737
738                # $password is now a hashed password object
739                save_user_in_db( $username, $password->rfc2307 );
740
741                template registered => { success => 1 };
742            };
743
744       We can now add the POST method for verifying that username and
745       password:
746
747            post '/login' => sub {
748                my $username   = query_parameters->get('username');
749                my $password   = query_parameters->get('password');
750                my $saved_pass = fetch_password_from_db($username);
751
752                if ( passphrase($password)->matches($saved_pass) ) {
753                    session user => $username;
754                    redirect query_parameters->get('return_url') || '/';
755                }
756
757                # let's render instead of redirect...
758                template login => { error => 'Invalid username or password' };
759            };
760
761   Writing a REST application
762       With Dancer2, it's easy to write REST applications. Dancer2 provides
763       helpers to serialize and deserialize for the following data formats:
764
765       JSON
766       YAML
767       XML
768       Data::Dumper
769
770       To activate this feature, you only have to set the "serializer" setting
771       to the format you require, for instance in your config file:
772
773          serializer: JSON
774
775       Or directly in your code:
776
777          set serializer => 'JSON';
778
779       From now, all hashrefs or arrayrefs returned by a route will be
780       serialized to the format you chose, and all data received from POST or
781       PUT requests will be automatically deserialized.
782
783           get '/hello/:name' => sub {
784               # this structure will be returned to the client as
785               # {"name":"$name"}
786               return { name => query_parameters->get('name') };
787           };
788
789       It's possible to let the client choose which serializer to use. For
790       this, use the "mutable" serializer, and an appropriate serializer will
791       be chosen from the "Content-Type" header.
792
793       It's also possible to return a custom error using the send_error
794       keyword. When you don't use a serializer, the "send_error" function
795       will take a string as first parameter (the message), and an optional
796       HTTP code. When using a serializer, the message can be a string, an
797       arrayref or a hashref:
798
799           get '/hello/:name' => sub {
800               if (...) {
801                  send_error("you can't do that");
802                  # or
803                  send_error({reason => 'access denied', message => "no"});
804               }
805           };
806
807       The content of the error will be serialized using the appropriate
808       serializer.
809
810   Using the serializer
811       Serializers essentially do two things:
812
813       •   Deserialize incoming requests
814
815           When a user makes a request with serialized input, the serializer
816           automatically deserializes it into actual input parameters.
817
818       •   Serialize outgoing responses
819
820           When you return a data structure from a route, it will
821           automatically serialize it for you before returning it to the user.
822
823       Configuring
824
825       In order to configure a serializer, you just need to pick which format
826       you want for encoding/decoding (e.g. JSON) and set it up using the
827       "serializer" configuration keyword.
828
829       It is recommended to explicitly add it in the actual code instead of
830       the configuration file so it doesn't apply automatically to every app
831       that reads the configuration file (unless that's what you want):
832
833            package MyApp;
834            use Dancer2;
835            set serializer => 'JSON'; # Dancer2::Serializer::JSON
836
837            ...
838
839       Using
840
841       Now that we have a serializer set up, we can just return data
842       structures:
843
844            get '/' => sub {
845                return { resources => \%resources };
846            };
847
848       When we return this data structure, it will automatically be serialized
849       into JSON. No other code is necessary.
850
851       We also now receive requests in JSON:
852
853            post '/:entity/:id' => sub {
854                my $entity = route_parameters->get('entity');
855                my $id     = route_parameters->get('id');
856
857                # input which was sent serialized
858                my $user = body_parameters->get('user');
859
860                ...
861            };
862
863       We can now make a serialized request:
864
865            $ curl -X POST http://ourdomain/person/16 -d '{"user":"sawyer_x"}'
866
867       App-specific feature
868
869       Serializers are engines. They affect a Dancer Application, which means
870       that once you've set a serializer, all routes within that package will
871       be serialized and deserialized. This is how the feature works.
872
873       As suggested above, if you would like to have both, you need to create
874       another application which will not be serialized.
875
876       A common usage for this is an API providing serialized endpoints (and
877       receiving serialized requests) and providing rendered pages.
878
879            # MyApp.pm
880            package MyApp;
881            use Dancer2;
882
883            # another useful feature:
884            set auto_page => 1;
885
886            get '/' => sub { template 'index' => {...} };
887
888            # MyApp/API.pm
889            package MyApp::API;
890            use Dancer2;
891            set serializer => 'JSON'; # or any other serializer
892
893            get '/' => sub { +{ resources => \%resources, ... } };
894
895            # user-specific routes, for example
896            prefix '/users' => sub {
897                get '/view'     => sub {...};
898                get '/view/:id' => sub {...};
899                put '/add'      => sub {...}; # automatically deserialized params
900            };
901
902            ...
903
904       Then those will be mounted together for a single app:
905
906            # handler: app.pl:
907            use MyApp;
908            use MyApp::API;
909            use Plack::Builder;
910
911            builder {
912                mount '/'    => MyApp->to_app;
913                mount '/api' => MyApp::API->to_app;
914            };
915
916       If you want use redirect from a mounted package to the application's
917       root URI, Dancer2::Plugin::RootURIFor makes this possible:
918
919           package OurWiki;
920           use Dancer;
921           use Dancer2::Plugin::RootURIFor;
922
923           get '/:some_path' => sub {
924               redirect root_uri_for('/');
925           }
926
927       An example: Writing API interfaces
928
929       This example demonstrates an app that makes a request to a weather API
930       and then displays it dynamically in a web page.
931
932       Other than Dancer2 for defining routes, we will use HTTP::Tiny to make
933       the weather API request, JSON to decode it from JSON format, and
934       finally File::Spec to provide a fully-qualified path to our template
935       engine.
936
937            use JSON;
938            use Dancer2;
939            use HTTP::Tiny;
940            use File::Spec;
941
942       Configuration
943
944       We use the Template::Toolkit template system for this app.  Dancer
945       searches for our templates in our views directory, which defaults to
946       views directory in our current directory. Since we want to put our
947       template in our current directory, we will configure that. However,
948       Template::Toolkit does not want us to provide a relative path without
949       configuring it to allow it. This is a security issue. So, we're using
950       File::Spec to create a full path to where we are.
951
952       We also unset the default layout, so Dancer won't try to wrap our
953       template with another one. This is a feature in Dancer to allow you to
954       wrap your templates with a layout when your templating system doesn't
955       support it. Since we're not using a layout here, we don't need it.
956
957            set template => 'template_toolkit';       # set template engine
958            set layout   => undef;                    # disable layout
959            set views    => File::Spec->rel2abs('.'); # full path to views
960
961       Now, we define our URL:
962
963            my $url = 'http://api.openweathermap.org/data/2.5/weather?id=5110629&units=imperial';
964
965       Route
966
967       We will define a main route which, upon a request, will fetch the
968       information from the weather API, decode it, and then display it to the
969       user.
970
971       Route definition:
972
973            get '/' => sub {
974                ...
975            };
976
977       Editing the stub of route dispatching code, we start by making the
978       request and decoding it:
979
980            # fetch data
981            my $res = HTTP::Tiny->new->get($url);
982
983            # decode request
984            my $data = decode_json $res->{'content'};
985
986       The data is not just a flat hash. It's a deep structure. In this
987       example, we will filter it for only the simple keys in the retrieved
988       data:
989
990            my $metrics = { map +(
991                ref $data->{$_} ? () : ( $_ => $data->{$_} )
992            ), keys %{$data} };
993
994       All that is left now is to render it:
995
996            template index => { metrics => $metrics };
997

NON-STANDARD STEPS

999   Turning off warnings
1000       The "warnings" pragma is already used when one loads Dancer2. However,
1001       if you really do not want the "warnings" pragma (for example, due to an
1002       undesired warning about use of undef values), add a "no warnings"
1003       pragma to the appropriate block in your module or psgi file.
1004

AUTHOR

1006       Dancer Core Developers
1007
1009       This software is copyright (c) 2021 by Alexis Sukrieh.
1010
1011       This is free software; you can redistribute it and/or modify it under
1012       the same terms as the Perl 5 programming language system itself.
1013
1014
1015
1016perl v5.34.0                      2022-01-21              Dancer2::Cookbook(3)
Impressum