The Errors of Our Way
This is a preview of the error throwing mechanism that is coming with Dancer 2. Things have been tidied a little bit, objectified in a classy manner (or classified in an objectionable manner? Anyway, turned into nice little Moo classes), and made (or so we hope) both easy to use and pleasantly versatile.
At the Core
In Dancer 2, Dancer::Core::Error
is the ringleader as far as error
handling is concerned. Its mission is fairly simple: take in booboos, and
report on them in the most sensible way possible.
Within Dancer's code, creating a new error is done by, well, creating a new
Dancer::Core::Error
:
my $oopsie = Dancer::Core::Error->new( status => 418, message => "This is the Holidays. Tea not acceptable. We want eggnog.", context => $context, );
If that was not simple enough, all those attributes are actually optional. If
not given, the
status code defaults to a good old 500, there is no need for
a message if we feel taciturn, and while the $context (which is a
Dancer::Core::Context
object holding all the pieces of information
related to the current request) is needed if we want to take advantage of the
templates, we can also do without.
The important thing to know, however, is that creating an error doesn't mean that it's automatically seen by the end-user. For that to happen, you have to...
Throw It Around
Or, put another way, we have to populate the Dancer::Core::Response
object
with the error's data. That is done via
$oopsie->throw($response);
Or, if we want to use the response object already present in the $context (which is usually the case):
$oopsie->throw;
This populates the status code of the response, sets its content, and throws a
halt()
in the dispatch process.
What The Error Going To Look Like?
Quick answer: whatever you want it to. The error object has quite a few ways to generate its content.
First, it can be explicitly given
my $oopsie = Dancer::Core::Error->new( content => '<html><body><h1>OMG</h1></body></html>', );
but that's boring.
If the $context was given, the error will check if there's a template by the name of the status code (so, say you're using Template Toolkit, 418.tt) and will use it to generate the content, passing it the error's $message, $status code and $title (which, if not specified, will be the standard http error definition for the status code).
If there is no template, the error will then look for a static page (to
continue with our example, 418.html) in the
public/
directory.
And finally, if all of that failed, the error object will fall back on an internal template.
Throwing Errors In Routes
Now, how do we use all that stuff in our routes? In the simpliest way:
get '/xmas/gift/:gift' => sub { die "sorry, we're all out of ponies\n" if param('gift') eq 'pony'; };
The die
will be intercepted by Dancer's inner magic, converted into an
error (status code 500, message set to the dying words) and shoved into
the response.
In the cases where more control is required, send_error()
is the way to go:
get '/glass/eggnog' => sub { send_error "Sorry, no eggnog here", 418; };
And if total control is needed:
get '/xmas/wishlist' => sub { Dancer::Core::Error->new( response => response(), status => 406, message => "nothing but coal for you, I'm afraid", template => 'naughty/index', )->throw unless user_was_nice(); ...; };
In Conclusion
At last news, Dancer::Core::Error
is in a working state. Some of its
details are likely to change a little bit, but it's already nicely settling
into a pattern that should make everyone happy. Well, as happy as anybody can
be while errors are being thrown, anyway.
AUTHOR
Yanick Champoux (YANICK)