David E. Wheeler > Router-Resource-0.11 > Router::Resource

Download:
Router-Resource-0.11.tar.gz

Dependencies

Annotate this POD

Website

View/Report Bugs
Module Version: 0.11   Source  

Name ^

Router::Resource - Build REST-inspired routing tables

Synopsis ^

  use Router::Resource;
  use Plack::Builder;
  use namespace::autoclean;

  sub app {
      # Create a routing table.
      my $router = router {
          resource '/' => sub {
              GET  { $template->render('home') };
          };

          resource '/blog/{year}/{month}' => sub {
              GET  { [200, [], [ $template->render({ posts => \@posts }) ] };
              POST { push @posts, new_post(shift); [200, [], ['ok']] };
          };
      };

      # Build the Plack app to use it.
      builder {
          sub { $router->dispatch(shift) };
      };
  }

Description ^

There are a bunch of path routers on CPAN, but they tend not to be very RESTy. A basic idea of a RESTful API is that URIs point to resources and the standard HTTP methods indicate the actions to be taken on those resources. So to encourage you to think about it that way, Router::Resource requires that you declare resources and then the HTTP methods that are implemented for those resources.

The rules for matching paths are defined by Router::Simple's routing rules, which offer quite a lot of flexibility.

Interface

You create a router in a router block. Within that block, define resources understood by the router with the resource keyword, which takes a resource path and a block defining its interface:

  my $router = {
      resource '/'    => sub { [[200, [], ['ok']] };
      resource '/foo' => sub { [[200, [], ['ok']] };
  };

Within a resource block, declare the HTTP methods that the resource responds to by using one or more of the following keywords:

GET
HEAD
POST
PUT
DELETE
OPTIONS
TRACE
CONNECT

Note that if you define a GET method but not a HEAD method, the GET method will respond to HEAD requests.

These methods should expect two arguments: the matched request (generally a PSGI $env hash) and a hash of the matched data as created by Router::Simple. For example, in a Plack-powered Wiki app you might do something like this:

  resource '/wiki/{name}' => sub {
      GET {
          my $req    = Plack::Request->new(shift);
          my $params = shift;
          my $wiki   = Wiki->lookup( $parmas->{name} );
          my $res    = $req->new_response;
          $res->content_type('text/html; charset=UTF-8');
          $res->body($wiki);
          return $res->finalize;
      };
  };

But of course you can abstract that into a controller or other code that the HTTP method simply dispatches to.

Dispatching

Use the dispatch method to have the router dispatch HTTP requests. For a Plack app, it looks something like this:

  sub { $router->dispatch(shift) };

The assumption is that the methods you've defined will return a PSGI-compatible array reference. When the router finds no matching resource or method, such an array is precisely what it will return. When a resource cannot be found, it will return

  [404, [], ['not found']]

If the resource is found but the requested method is not defined, it returns something like:

  [405, [Allow => 'GET, HEAD'], ['not allowed']]

The "Allow" header will list the methods that the requested resource does respond to.

Of course you may not want something so simple for your app. So use the missing keyword to specify a code block to handle this situation. The code block should expect two arguments: the unmatched request $env hash and a hash describing the failure. For an unfound resource, that hash will contain:

  { code => 404, message => 'not found', headers => [] }

If a resource was found but it does not define the requested method, the hash will look something like this:

  { code => 405, message => 'not allowed', headers => [Allow => 'GET, HEAD'] }

This is designed to make it relatively easy to create a custom response to unfound resources and missing methods. Something like:

  missing {
      my $req    = Plack::Request->new(shift);
      my $params = shift;
      my $res    = $req->new_response($params->{code});
      $res->headers(@{ $params->{headers} });
      $res->content_type('text/html; charset=UTF-8');
      $res->body($template->show('not_found', $params));
      return $res->finalize;
  };

See Also ^

Support ^

This module is stored in an open GitHub repository. Feel free to fork and contribute!

Please file bug reports via GitHub Issues or by sending mail to bug-Router-Resource@rt.cpan.org.

Author ^

David E. Wheeler <david@kineticode.com>

Acknowledgements ^

My thanks to the denizens of #plack for their feedback and advice on this module, including:

Copyright and License ^

Copyright (c) 2010 David E. Wheeler. Some Rights Reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

syntax highlighting: