NAME
CGI::Application::Plugin::REST - Helps implement RESTful architecture in CGI applications
SYNOPSIS
use CGI::Application::Plugin::REST qw( :all );
ABSTRACT
If you use the CGI::Application framework, this plugin will help you create a RESTful (that's the common term for "using REST") architecture by abstracting out a lot of the busy work needed to make it happen.
VERSION
This document describes CGI::Application::Plugin::REST Version 0.1
DESCRIPTION
REST stands for REpresentational State Transfer. It is an architecture for web applications that tries to leverage the existing infrastructure of the World Wide Web such as URIs, MIME media types, and HTTP instead of building up protocols and functions on top of them.
This plugin contains a number of functions to support the various REST concepts. They try to use existing CGI::Application functionality wherever possible.
use
'ing this plugin will intercept CGI::Application's standard dispatch mechanism. Instead of being selected based on a query parameter like rm
, the run mode will be determined by the PATH_INFO
information in the request URI. (Referred from here on, as a "route".) This is done via overriding CGI::Application's mode_param()
function so it should be compatible with other CGI::Application plugins.
FUNCTIONS
The following functions are available. None of them are exported by default. You can use the :all
tag to import all public functions.
rest_error_mode()
This function gets or sets the run mode which is called if an error occurs during the dispatch process. In this run mode, you can do whatever error processing or clean up is needed by your application.
If no error mode is defined, the start mode will be returned.
Example 1:
$self->rest_error_mode('my_error_mode');
my $em = $self->rest_error_mode; # $em equals 'my_error_mode'.
rest_param()
The rest_param
function is used to retrieve or set named parameters defined by the rest_route() function. it can be called in three ways.
- with no arguments.
-
Returns a sorted list of the defined parameters in list context or the number of defined parameters in scalar context.
my @params = $self->rest_param(); my $num_params = $self->rest_param();
- with a single scalar argument.
-
The value of the parameter with the name of the argument will be returned.
my $color = $self->rest_param('color');
- with named arguments
-
Although you will mostly use this function to retrieve parameters, they can also be set for one or more sets of keys and values.
$self->rest_param(filename => 'logo.jpg', height => 50, width => 100);
You could also use a hashref.
my $arg_ref = { filename => 'logo.jpg', height => 50, width => 100 }; $self->rest_param($arg_ref);
The value of a parameter need not be a scalar, it could be any any sort of reference even a coderef.
$self->rest_param(number => &pick_a_random_number);
In this case, the function does not return anything.
rest_route()
When this function is given a hash or hashref, it configures the mapping of routes to handlers (run modes within your CGI::Application).
It returns the map of routes and handlers.
Routes
Assume for the purpose of the following examples that your instance script has a base URI of http://localhost/
HINT: Your web server might not execute CGI scripts unless they have an extension of .cgi so your actual script might be http://localhost/app.cgi
. However it is considered unRESTful to include infrastructural details in your URLs. Use your web servers URL rewriting features (i.e. mod_rewrite in Apache) to hide the extension.
A route looks like a URI with segments seperated by /'s.
Example 1: a simple route
/foo
A segment in a route is matched literally. So if a request URI matches http://localhost/foo, the run mode that handles the route in example 1 will be used.
Routes can have more complex specifications.
Example 2: a more complex route
/bar/:name/:id?/:email
If a segment of a route is prefixed with a :, it is not matched literally but treated as a parameter name. The value of the parameter is whatever actually got matched. If the segment ends with a ?, it is optional otherwise it is required. The values of these named parameters can be retrieved with the rest_param() method.
In example 2, http://localhost/bar/jaldhar/76/jaldhar@braincells.com would match. rest_param('name')
would return 'jaldhar', rest_param('id')
would return 76, and rest_param('email')
would return 'jaldhar@braincells.com'.
If the request URI was http://localhost/bar/jaldhar/jaldhar@braincells.com/, rest_param('email')
would return 'jaldhar@braincells.com' and rest_param('name')
would return 'jaldhar'. rest_param('id')
would return undef.
If the request URI was http://localhost/bar/jaldhar/76 or http://localhost/jaldhar/, there would be no match at all because the required parameter ':email' is missing.
Note: Each named parameter is returned as a scalar. If you want ':email' to actually be an email address, it is up to your code to validate it before use.
Example 3: a wild card route
/baz/string/*
If the route specification contains /*, everything from then on will be put into the special parameter 'dispatch_uri_remainder' which you can retrieve with rest_param() just like any other parameter. Only one wildcard can be specified per route. Given the request URI http://localhost/baz/string/good, rest_param('dispatch_uri_remainder')
would return 'good', with http://localhost/baz/string/evil it would return 'evil' and with http://localhost/baz/string/lawful/neutral/ it would return 'lawful/neutral/'.
Handlers
The most basic handler is a scalar or coderef.
Example 4: Basic Handlers
my $routes = {
'/foo' => 'wibble',
'/bar/:name/:id?/:email' => \&wobble,
'/baz/string/*/' => 'woop',
};
$self->rest_route($routes);
In example 4, a request to http://localhost/app/foo
will be dispatched to wibble()
. (It is upto you to make sure such a method exists.) A request to http://localhost/app/bar/jaldhar/76/jaldhar@braincells.com
will dispatch to wobble()
. A request to http://localhost/login
will raise an error.
Example 5: More complex handlers
$self->rest_route(
'/quux' => {
'GET' => 'ptang',
'DELETE' => 'krrang',
},
'/edna' => {
'POST' => 'blip',
'*' => 'blop',
},
'/grudnuk' => {
'GET' => {
'application/xml' => 'zip',
'*/*' => 'zap',
},
PUT => {
'application/xml' => 'zoom',
},
},
}
If the handler is a hashref, the keys of the second-level hash are HTTP methods and the values if scalars or coderefs, are run modes. The key can also be * which matches all methods not explicitly specified. If a valid method cannot be matched, an error is raised and the HTTP status of the response is set to 405. (See "DIAGNOSTICS")
In example 5, a GET
request to http://localhost/quux will be dispatched to ptang()
. A DELETE
to http://localhost/quux will dispatch to krrang()
. A POST
, PUT
or HEAD
will cause an error.
A POST
request to http://localhost/edna will dispatch to zip()
while any other type of request to that URL will dispatch to blop()
The values of the second-level hash can also be hashes. In this case the keys of the third-level hash represent MIME media types. The values are run modes. The best possible match is made use best_match()
from REST::Utils. according to the HTTP Accept header sent in the request. If a valid MIME media type cannot be matched an error is raised and the HTTP status of the response is set to 415. (See "DIAGNOSTICS")
In example 5, a GET
request to http://localhost/grudnuk with MIME media type application/xml will dispatch to zip()
. If the same request is made with any other MIME media type, the method zap()
will be called instead. A PUT
request made to the same URL with MIME media type application/xml will dispatch to zoom()
. Any other combination of HTTP methods or MIME media types will cause an error to be raised.
If no URI can be matched, an error is raised and the HTTP status of the response is set to 404 (See "DIAGNOSTICS".)
rest_route_prefix()
Use this function to set a prefix for routes to avoid unnecessary repetition when you have a number of similar ones.
Example 1:
# matches requests to /zing
$self->rest_route(
'/zing' => {
'GET' => 'zap',
},
);
$self->rest_route_prefix('/app')
# from now on requests to /app/zing will match instead of /zing
my $prefix = $self->rest_route_prefix # $prefix equals '/app'
DIAGNOSTICS
During the dispatch process, errors can occur in certain circumstances. If an error occurs the appropriate HTTP status is set and execution passes to the run mode set by "rest_error_mode()". Here is a list of status codes and messages.
400 No Dispatch Table
This error can occur if rest_route() was not called.
404 No Route Found
None of the specified routes matched the request URI.
405 Method '$method' Not Allowed
The route you specified with rest_route() does not allow this HTTP request method. An HTTP
Allow
header is added to the response specifying which methods can be used.415 Unsupported Media Type
None of the MIME media types requested by the client can be returned by this route.
500 Application Error
The function that was called for this run_mode
die
'd somewhere.501 Function Doesn't Exist
The function that you wanted to call from rest_route() for this run_mode doesn't exist in your application.
BUGS AND LIMITATIONS
There are no known problems with this module.
Please report any bugs or feature requests to bug-cgi-application-plugin-rest at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CGI-Application-Plugin-REST. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SEE ALSO
-
The application framework this module plugs into.
-
CGI::Application::Plugin::REST uses my REST::Utils module behind the scenes.
-
This module by Matthew O'Connor gave me some good ideas.
http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm:
Roy Fieldings' doctoral thesis in which the term REST was first defined.
-
"The Restful Web" columns by Joe Gregorio have been very useful to me in understanding the ins and outs of REST.
THANKS
Much of the code in this module is based on CGI::Application::Plugin:Routes by Julián Porta who in turn credits Michael Peter's CGI::Application::Dispatch.
AUTHOR
Jaldhar H. Vyas, <jaldhar at braincells.com>
LICENSE AND COPYRIGHT
Copyright (c) 2010 Consolidated Braincells Inc., all rights reserved.
This distribution is free software; you can redistribute it and/or modify it under the terms of either:
a) the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version, or
b) the Artistic License version 2.0.
The full text of the license can be found in the LICENSE file included with this distribution.