send_as BEER => 'Christmas Cheer'
One difference between Dancer and Dancer2 is that serializers are "Always or never". Sawyer X's 2014 advent article explains why this change was important.
However one question that arose several times during 2015 from keen Dancer2 users was
"How do you return HTML from a specific route when we have a serialization engine configured?"
Back then, the best advice was to "split" your app into two distinct
applications and use Plack::Builder
to glue them back together.
# bin/app.psgi use Plack::Builder; use MyApp; use MyApp::API; builder { mount '/api' => MyApp::API->to_app; mount '/' => MyApp->to_app; };
However, if you have a small number of routes that required a template engine (or a small number that needed a serialization engine), the effort involved was onerous.
Other suboptimal solutions (including the use of eldritch horrors) were
proposed in issues on github prior to a suggestion to use a combination of
Dancer2's send_file
and to_json
using an in-memory file handle:
set serializer => 'JSON'; set template => 'template_toolkit'; get '/html/route' => sub { my $html = encode( 'UTF-8', template('awesome_template') ); content_type => ''text/html; charset=UTF-8'; send_file \$html; # sends html };
Which just works; but involves excessive boilerplate and one needs to take care with text encodings. That's not the Dancer way!
A prototype solution: Dancer2::Plugin::SendAs
Conferences have many benefits! There is the awesome talks, the hallway track, the benefits of getting a group together and discussing a problem face-to-face, or just having some time away to think about a particular problem.
During some conference related downtime in 2015, Dancer2::Plugin::SendAs was spec'd and implemented to encapsulate the previous patten into a simple to use c<send_as> keyword:
use Dancer2::Plugin::SendAs; get '/api/thing' => sub { send_as JSON => { thing => { to => [ 'return' ] } }; };
Any Dancer2 serializer was supported, as well as HTML output:
get '/api' => sub { send_as HTML => template( 'instructions' ); };
Simple. Easy. Neat. Now that's the Dancer way!
From Plugin to Core
The SendAs
plugin solved a problem many Dancers' encountered. The core
team decided to move the plugins' functionality into core in early 2016.
(Did you know the to_json
and from_json
keywords started as a plugin too?)
After cleaning up some edge cases and tweaking features, c<send_as> became a core keyword in the c<0.200000> release.
The key differences in the core implemantation are
- Serializers must use the appropriate casing of their names. eg.
JSON
orSereal
.HTML
is special cased for returning HTML content, and must be in upper case. - Serializers will load settings from the applications configuration.
Like send_file
, the send_as
keyword returns immediately from a route,
allowing send_as
to short-circuit route logic, even when a
serializer is defined:
set serializer => 'JSON'; set template => 'template_toolkit'; post '/api/:id' => sub { my $id = request->route_parameter->get('id'); if ( $id !~ m/^[0-9]+$/ ) { # send HTML error page status 500; send_as HTML => template('error'); } # Continue with the rest of route logic if id was an integer # Returns JSON content ... };
Dancer2::Plugin::SendAs
remains on the CPAN (deprecated) for
posterity.
Author
This article has been written by Russell (veryrusty) Jenkins for the Perl Dancer Advent Calendar 2016.
Copyright
No copyright retained. Enjoy.
2016 // Russell (veryrusty) Jenkins