[Catalyst] uri_for() behaviour

Peter Karman peter at peknet.com
Wed Oct 3 15:44:26 GMT 2007


The refactor of uri_for() in recent Catalyst releases shows a big speed
improvement. Nice.

However, it revealed some abuses of uri_for() in my existing apps, which worked
previously ostensibly because uri_for() used URI internally.

For example, I often have this idiom in my code:

 $c->uri_for('/', 'foo', 'bar');

In order to get 'http://myapp/foo/bar' returned. Now it needs to be:

 $c->uri_for('/foo', 'bar');

to get the same return result.

Likewise, for external URIs I often did:

 $c->uri_for('http://someplace', { foo => 'bar' });

to get 'http://someplace/?foo=bar' returned. Only now it returns
'http://myapp/http://someplace?foo=bar' which obviously is Not What I Meant.

The main reason I used uri_for() for external URIs was for the nice
uri-escaping features when dealing with params.

So now I have uri_for() overriden in my base class to catch these abuses and
carp about them so I can fix them. In the meantime, I mung the input values
before passing on to the core uri_for() so that my apps don't break.

Additionally, I realized I often do this:

 $c->res->redirect( $c->uri_for( 'foo' ) );

Which is ok. But I realized it'd be nicer to do this:

 $c->redirect( 'foo' );

in a way analogous to detach() or forward(), only using the external redirect
instead of the internal one.

So here's the code below that I am currently using. I wonder if redirect() has
a place in the core Catalyst API? I offer this only as a usability suggestion /
prompt for discussion. The code could probably be improved.

=head2 redirect( I<path>, I<params> )

Wrapper around the common idiom of response->redirect()
and uri_for().


sub redirect {
    my ( $c, $path, @arg ) = @_;

    my $uri;

    if ( $path =~ m!^\w+://! ) {
        $c->log->debug("external redirect: $path");
        $uri = $c->external_uri_for( $path, @arg );
    elsif ( $path eq '/' && @arg ) {
        $c->log->debug("munging lone leading slash in redirect");
        $arg[0] = join( '', $path, $arg[0] ) unless ref( $arg[0] );
        $uri = $c->uri_for(@arg);
    else {
        $uri = $c->uri_for( $path, @arg );


=head2 external_uri_for( I<uri>, I<args> )

Like uri_for() but for URIs outside the current application
(i.e., beginning with C<\w+://>).


sub external_uri_for {
    my ( $c, $path, @arg ) = @_;
    my $uri = $c->uri_for( $c, $path, @arg );

    # remove the pre-pended base uri
    my $base = join( '', $c->req->base, $c->namespace, '/' );
    $c->log->debug("base URI appears to be $base") if $c->debug;
    $uri =~ s,^$base,,;

    return $uri;

Peter Karman  .  peter at peknet.com  .  http://peknet.com/

More information about the Catalyst mailing list