[Catalyst] Chained and exceptions

Lukas Thiemeier spamcatcher at thiemeier.net
Thu May 9 14:25:56 GMT 2013


On 05/09/2013 03:25 PM, Bill Moseley wrote:
> I have a feeling I asked this before, but cannot find the post.
> 
> [info] Exception powered by Catalyst 5.90030
> 
> What's the reasoning that chained actions continue to run after an
> earlier exception?
> 
> 
> sub start : Chained( '/' ) : CaptureArgs(0) {
>     warn "in start\n";
> }
> 
> sub middle : Chained( 'start' ) : CaptureArgs(0) {
>     warn "in middle\n";
>     die "died in middle\n";  # or e.g. throw access violation
> }
> 
> sub lastpart : Chained( 'middle' ) : Args(0) {
>     my ( $self, $c ) = @_;
>     $c->res->body( "finished\n" );
>     warn "in lastpart\n";
> }
> 
> $ script/exception_test.pl <http://exception_test.pl> /start/middle/lastpart
> in start
> in middle
> *in lastpart*
> [error] Caught exception in Exception::Controller::Root->middle "died in
> middle"
> 

Hi Bill,

This is because you don't want Catalyst to die. Imagine you are running
a fastcgi server and you accidentally created an action which dies on
certain user input.
If Catalyst would die, the fastcgi server would die and your whole
application would not be available anymore. Instead, you want to report
the incident and keep the fastcgi server (Catalyst) running.

Because of this, every action is wrapped in an "eval{...}". Potential
errors are logged, but the server keeps running.

See "execute" in Catalyst.pm for implementation details.


To solve your problem, you can wrap your unsafe code in an eval or use
Try::Tiny (or whatever you prefer) and detach if the unsafe code dies.

Your Example would look like this:

use Try::Tiny;

sub start : Chained( '/' ) : CaptureArgs(0) {
    warn "in start\n";
}

sub middle : Chained( 'start' ) : CaptureArgs(0) {
    my ($self, $c) = @_;
    warn "in middle\n";
    try{
    die "died in middle\n";  # or e.g. throw access violation
    }
    catch{ $c->detach };
}

sub lastpart : Chained( 'middle' ) : Args(0) {
    my ( $self, $c ) = @_;
    $c->res->body( "finished\n" );
    warn "in lastpart\n";
}

If you do it like this, actions chained to the unsafe action will not
get executed if the unsafe action dies. In your case, "lastpart" will
never be executed, because "middle" always dies.

Lukas



More information about the Catalyst mailing list