2012 Feed

Routes and Path::Router

OX - 2012-12-03

Routing

Routing is the step that takes an HTTP request and decides what code should actually be run. Although any part of the request can be used here, in practice routing happens by only looking at the path (and maybe the HTTP method).

Path::Router is a module which can take an incoming path and return which of a set of route definitions it matched. OX uses this as the basis for its routing. Path::Router handles solely the path aspect of routing - routing based on other things (such as HTTP methods) must be handled via route builders.

Basic routes


1: 
2: 
3: 
4: 
5: 

 

router as {
    route '/' => 'root.index';
    route '/archive' => 'posts.archive';
    route '/auth/login' => 'auth.login';
};

 

The simplest pattern for matching paths consists of just a literal string, to be matched against the incoming path. This only looks at the path itself (to be specific, the PSGI PATH_INFO variable), so any query parameters are ignored. If the path in the incoming request is equal to any of the paths defined in the router, that path is chosen, and its associated code is run to create the response. If no path matches, a 404 response is returned.

Route variables

Some routes need to contain variable components. These can be specified by a leading colon:


1: 
2: 
3: 

 

router as {
    route '/:id' => sub { my ($r, $id) = @_; return $id };
};

 

In this case, this route doesn't match just the path /view/:id, but any path that starts with /view/ and contains exactly one additional path component: /view/24, /view/c9625979-976a-4cf1-b557-7cf5bd591d0d, etc. When the code for the route is called, the variable path components will be passed as additional arguments, in order, after the request object.

Variable routes do make routing more complicated, however, since paths can now potentially match multiple different routes. If a path is given which does match multiple routes, the route with the fewest variable path components is chosen. For instance, given this router:


1: 
2: 
3: 
4: 

 

router as {
    route '/view/:id' => 'posts.view_specific_post';
    route '/view/default' => 'posts.view_default_post';
};

 

a request for /view/default will match the view_default_post route. If there are multiple routes with the fewest variable path components, an error will be thrown (so don't do that).

Optional routes

Sometimes the path component isn't actually necessary at all. In this case, you can prefix the path variable with a question mark to indicate that including it is not actually necessary (path components must otherwise be non-empty strings). For instance:


1: 
2: 
3: 

 

router as {
    route '/view/?:id' => 'posts.view_specific_post';
};

 

In this case, /view is also a valid path, and the argument passed to the route's code will be undef.

Validations

Sometimes you want a bit more control than just "does the path component exist?". In this case, you can specify constraints to validate the path variables. For instance:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 

 

router as {
    route '/view/:id' => 'posts.view_by_id', (
        id => { isa => 'Int' },
    );
    route '/view/:search' => 'posts.search', (
        search => { isa => qr/^\D+$/ },
    );
};

 

This will route /view/123 to view_by_id, and /view/foobar to search. Validations must be specified by a hash following the action spec, where the keys are names of variable path components and the values are hashrefs with an isa entry. Each validation can be either a Moose type constraint (a type name or a type constraint object) or a regular expression.

Gravatar Image This article contributed by: Jesse Luehrs <jesse.luehrs@iinteractive.com>