[Catalyst] Validating single arg id

iain iainhubbard at googlemail.com
Fri Oct 16 15:24:54 GMT 2009

Bill Moseley wrote:
> I have a number of methods that start something like this:
> sub view : Local Args(1) {
>     my ( $self, $c, $id ) = @_;
>     my $obj = $c->model( 'DB::Foo' )->find( $id )
>        || return $c->res->status( 404 );
> If $id is not valid then I might, as in that example, return with a 
> 404 status.
> Of course, if $id is suppose to be an integer and a non-integer or an 
> integer out of range is provided then the the database will throw an 
> exception, which I want to prevent.  I want valid ids to return an 
> object and *anything* else to return undef before hitting the database.
> This is pretty low-level validation -- just validating primary key.  
> For more complex validation I use a form validation module.
> Obviously, I could do something like
> return $c->res->status(404) unless $c->model('DB::Foo')->is_valid_id( 
> $id )
> in every method, but that's not very DRY.
> What I've done in the past is override the find() or search() method 
> in my model base class so that whatever $id is passed it is 
> validated.  Specific model classes can override the is_valid_id()  
> method if they use keys that are not a common key format (i.e. 
> different integer range or non-integer key).
> What's you approach to validating that $id in situations like this 
> where there's a single id?
> Do you just let the database throw the exception?  I prefer to return 
> 404s for invalid ids, regardless of their format (and likewise for ids 
> that point to valid object, but are not owned by the current user 
> instead of a 403).

We did this sort of thing until we started to use chained actions.
Now we validate once at the start of the chain e.g.

sub start : Chained('/') CaptureArgs(0) {
    my ( $self, $c ) = @_;
    # your validation here
    my $obj = $c->model( 'DB::Foo' )->find( $id )
       || return $c->res->status( 404 );

    $c->stash->{obj} = $obj;

    return 1;

sub view : Chained('start') Args(0) {
    my ( $self, $c ) = @_;
    # do something with $c->stash->{obj}
    return 1;

until we did this we had boilerplate validation at the top of all the 
local actions.


More information about the Catalyst mailing list