morgon has asked for the wisdom of the Perl Monks concerning the following question:

Dear monk,

I am using CGI::Application and find it quite useful, but sometimes I have the feeling that I must be too stupid to properly use it as what I do just looks silly to me, yet I cannot find a better solution - which is why I hope for some enlightenment here.

One of my problems is exception-handling in cgi_prerun.

Assume I have a error-runmode configured that gets called whenever an exception occurs in a normal runmode. Very handy.

But now I also have a cgiapp_prerun method that handles some generic stuff that also may throw an exception.

Unfortunately CGI::App does not do exception handling for you in the prerun so you have to catch any possible exception yourself and the question is now how to transfer control to the error-runmode and how to pass the exception on.

Calling the error-runmode directly does not work so my workaround looks something like this (I am also using CGI::Application::Plugin::Stash):

sub cgiapp_prerun { my($this, $rm)=@_; eval { ... }; if($@) { $this->stash->{exception} = $@; $this->prerun_mode("prerun_error") } } sub prerun_error { my($this)=@_; $this->error($this->stash->{exception}); } sub error { # error-runmode my($this, $exception)=@_; ... }
This works but it looks ridiculous...

I pass the exception via the stash (basically creating a global variable) and need the prerun_error runmode only to pass control on the error-runmode, because the only way out from cgiapp_prerun seems to be via prerun_mode How would you do this?

Many thanks!

Replies are listed 'Best First'.
Re: CGI::Application cgi_prerun exception handling
by Anonymous Monk on Jan 02, 2011 at 07:14 UTC
    Create a base class for your applications and override run, then inherit from this base class
    package MyBase::CGIapp; use parent qw' CGI::Application '; sub run { my( $self ) = shift; ## pseudo code my $ret = eval { $self->SUPER::run(@_); }; while( $@ ){ $ret = eval { $self->handle_exeption }; last if $ret ; } return $ret; }
    Run calls all the other methods, cgiapp_prerun....
Re: CGI::Application cgi_prerun exception handling
by Khen1950fx (Canon) on Jan 02, 2011 at 04:36 UTC
    I've never used CGI::Application before, so this is my first take on it:
    package My::App; use strict; use warnings; use base qw(CGI::Application); use CGI::Application::Plugin::DebugMessage; $_ = My::App->new; $_->debug('Abandon Ship!'); $_->run; sub cgiapp_init { my $self = shift; $self->mode_param('rm'); $self->run_modes([ 'login', 'AUTOLOAD' ]); } sub cgiapp_prerun { my ( $this, $rm ) = @_; eval { do { (); } }; if ($@) { $_->debug('Abandon Ship!'); } }
      Thanks for your reply but unfortunately I don't understand your point.

      Maybe I did not make clear what my problem is.

      I have runmodes that may throw exceptions and a generic pre-run that also potentially throws exceptions.

      Whenever an exception occurs I want it to be handled in one central location (the error-runmode) where I will then generate different responses for the client, depending on the exception thrown (I deal with an application that generates XML for machine-clients).

      The question is know how to pass an exception that occurs in the pre-run on to the error-runmode in an elegant way.

      Sorry if you provided an answer to that in your posting that I failed to understand.

Re: CGI::Application cgi_prerun exception handling
by scorpio17 (Canon) on Jan 04, 2011 at 20:10 UTC

    You could do something like this:

    sub cgiapp_prerun { my $this = shift; my $rm = shift; eval { ... }; if($@) { $this->stash->{exception} = "Error in prerun: $@"; $this->prerun_mode("error"); } } sub error { # error-runmode my $this = shift; my $exception = shift || $this->stash->{exception} || "default error message goes here"; ... }

    Admittedly, this isn't that different... but it eliminates the prerun_error runmode. It also gives you the ability to call your error run mode either with an argument:

    $this->error("danger! red alert!");

    or without. If no argument is given, then the error run mode looks to see if there's anything in the stash, and if so it uses it - otherwise it spits out a generic default. You could use the stash to accumulate multiple errors. For example, if you were validating a form with lots of inputs, you could do something like:

    # check 'name' entry unless ($name) { $this->stash->{exception} .= "bad name\n"; } # check 'address' entry unless ($address) { $this->stash->{exception} .= "bad address\n"; } # check 'phone' entry unless ($phone) { $this->stash->{exception} .= "bad phone\n"; } return $this->error( $this->stash->{exception} ) if $this->stash->{exception}; ...