X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FController%2FREST.pm;h=6016c656d3a5a9c141e02cc900ad76523753050c;hb=21d3f6aeb5d5150d2709f9a3d0647d21e9d74d9b;hp=6123010a6e7953696b825775740aa50cdee8043b;hpb=faf5c20bff09c91f18b46a3d3d8349379aa36192;p=catagits%2FCatalyst-Action-REST.git diff --git a/lib/Catalyst/Controller/REST.pm b/lib/Catalyst/Controller/REST.pm index 6123010..6016c65 100644 --- a/lib/Catalyst/Controller/REST.pm +++ b/lib/Catalyst/Controller/REST.pm @@ -1,8 +1,10 @@ package Catalyst::Controller::REST; +our $VERSION = '0.74'; + =head1 NAME -Catalyst::Controller::REST - A RESTful controller +Catalyst::Controller::REST - A RESTful controller =head1 SYNOPSIS @@ -15,11 +17,11 @@ Catalyst::Controller::REST - A RESTful controller # Answer GET requests to "thing" sub thing_GET { my ( $self, $c ) = @_; - + # Return a 200 OK, with the data in entity - # serialized in the body + # serialized in the body $self->status_ok( - $c, + $c, entity => { some => 'data', foo => 'is real bar-y', @@ -28,7 +30,7 @@ Catalyst::Controller::REST - A RESTful controller } # Answer PUT requests to "thing" - sub thing_PUT { + sub thing_PUT { .. some action .. } @@ -36,23 +38,23 @@ Catalyst::Controller::REST - A RESTful controller Catalyst::Controller::REST implements a mechanism for building RESTful services in Catalyst. It does this by extending the -normal Catalyst dispatch mechanism to allow for different -subroutines to be called based on the HTTP Method requested, +normal Catalyst dispatch mechanism to allow for different +subroutines to be called based on the HTTP Method requested, while also transparently handling all the serialization/deserialization for you. This is probably best served by an example. In the above controller, we have declared a Local Catalyst action on -"sub thing", and have used the ActionClass('REST'). +"sub thing", and have used the ActionClass('REST'). Below, we have declared "thing_GET" and "thing_PUT". Any -GET requests to thing will be dispatched to "thing_GET", -while any PUT requests will be dispatched to "thing_PUT". +GET requests to thing will be dispatched to "thing_GET", +while any PUT requests will be dispatched to "thing_PUT". Any unimplemented HTTP methods will be met with a "405 Method Not Allowed" response, automatically containing the proper list of available methods. You can override this behavior through implementing a custom -C method. +C method. If you do not provide an OPTIONS handler, we will respond to any OPTIONS requests with a "200 OK", populating the Allowed header automatically. @@ -67,18 +69,18 @@ contents of $c->request->body based on the requests content-type header. A list of understood serialization formats is below. If we do not have (or cannot run) a serializer for a given content-type, a 415 -"Unsupported Media Type" error is generated. +"Unsupported Media Type" error is generated. To make your Controller RESTful, simply have it - use base 'Catalyst::Controller::REST'; + use base 'Catalyst::Controller::REST'; =head1 SERIALIZATION Catalyst::Controller::REST will automatically serialize your responses, and deserialize any POST, PUT or OPTIONS requests. It evaluates which serializer to use by mapping a content-type to a Serialization module. -We select the content-type based on: +We select the content-type based on: =over 2 @@ -93,7 +95,7 @@ If this is a GET request, you can supply a content-type query parameter. =item B Finally, if the client provided an Accept header, we will evaluate -it and use the best-ranked choice. +it and use the best-ranked choice. =back @@ -101,11 +103,11 @@ it and use the best-ranked choice. A given serialization mechanism is only available if you have the underlying modules installed. For example, you can't use XML::Simple if it's not already -installed. +installed. In addition, each serializer has it's quirks in terms of what sorts of data structures it will properly handle. L makes -no attempt to svae you from yourself in this regard. :) +no attempt to save you from yourself in this regard. :) =over 2 @@ -118,9 +120,11 @@ Returns YAML generated by L. This uses L and L to generate YAML with all URLs turned to hyperlinks. Only useable for Serialization. -=item C => C +=item C => C -Uses L to generate JSON output +Uses L to generate JSON output. It is strongly advised to also have +L installed. The C content type is supported but is +deprecated and you will receive warnings in your log. =item C => C @@ -160,13 +164,13 @@ you serialize be a HASHREF, we transform outgoing data to be in the form of: =item L -Uses a regular Catalyst view. For example, if you wanted to have your +Uses a regular Catalyst view. For example, if you wanted to have your C and C views rendered by TT: - 'text/html' => [ 'View', 'TT' ], - 'text/xml' => [ 'View', 'XML' ], - -Will do the trick nicely. + 'text/html' => [ 'View', 'TT' ], + 'text/xml' => [ 'View', 'XML' ], + +Will do the trick nicely. =back @@ -175,7 +179,7 @@ response if an attempt to use an unsupported content-type is made. You can ensure that something is always returned by setting the C config option: - __PACKAGE__->config->{'default'} = 'text/x-yaml'; + __PACKAGE__->config->{'default'} = 'text/x-yaml'; Would make it always fall back to the serializer plugin defined for text/x-yaml. @@ -197,7 +201,7 @@ Using them will ensure that you are responding with the proper codes, headers, and entities. These helpers try and conform to the HTTP 1.1 Specification. You can -refer to it at: L. +refer to it at: L. These routines are all implemented as regular subroutines, and as such require you pass the current context ($c) as the first argument. @@ -208,32 +212,31 @@ such require you pass the current context ($c) as the first argument. use strict; use warnings; use base 'Catalyst::Controller'; -use Params::Validate qw(:all); +use Params::Validate qw(SCALAR OBJECT); __PACKAGE__->mk_accessors(qw(serialize)); __PACKAGE__->config( - 'stash_key' => 'rest', - 'map' => { - 'text/html' => 'YAML::HTML', - 'text/xml' => 'XML::Simple', - 'text/x-yaml' => 'YAML', - 'text/x-json' => 'JSON', - 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ], - 'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ], - 'text/x-data-taxi' => [ 'Data::Serializer', 'Data::Taxi' ], - 'application/x-storable' => [ 'Data::Serializer', 'Storable' ], - 'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' ], - 'text/x-config-general' => [ 'Data::Serializer', 'Config::General' ], - 'text/x-php-serialization' => [ 'Data::Serializer', 'PHP::Serialization' ], - }, + 'stash_key' => 'rest', + 'map' => { + 'text/html' => 'YAML::HTML', + 'text/xml' => 'XML::Simple', + 'text/x-yaml' => 'YAML', + 'application/json' => 'JSON', + 'text/x-json' => 'JSON', + 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ], + 'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ], + 'text/x-data-taxi' => [ 'Data::Serializer', 'Data::Taxi' ], + 'application/x-storable' => [ 'Data::Serializer', 'Storable' ], + 'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' ], + 'text/x-config-general' => [ 'Data::Serializer', 'Config::General' ], + 'text/x-php-serialization' => [ 'Data::Serializer', 'PHP::Serialization' ], + }, ); -sub begin : ActionClass('Deserialize') { -} +sub begin : ActionClass('Deserialize') { } -sub end : ActionClass('Serialize') { -} +sub end : ActionClass('Serialize') { } =item status_ok @@ -242,7 +245,7 @@ Returns a "200 OK" response. Takes an "entity" to serialize. Example: $self->status_ok( - $c, + $c, entity => { radiohead => "Is a good band!", } @@ -253,7 +256,7 @@ Example: sub status_ok { my $self = shift; my $c = shift; - my %p = validate( @_, { entity => 1, }, ); + my %p = Params::Validate::validate( @_, { entity => 1, }, ); $c->response->status(200); $self->_set_entity( $c, $p{'entity'} ); @@ -268,7 +271,7 @@ and a "location" where the created object can be found. Example: $self->status_created( - $c, + $c, location => $c->req->uri->as_string, entity => { radiohead => "Is a good band!", @@ -283,7 +286,7 @@ This is probably what you want for most PUT requests. sub status_created { my $self = shift; my $c = shift; - my %p = validate( + my %p = Params::Validate::validate( @_, { location => { type => SCALAR | OBJECT }, @@ -310,7 +313,7 @@ Returns a "202 ACCEPTED" response. Takes an "entity" to serialize. Example: $self->status_accepted( - $c, + $c, entity => { status => "queued", } @@ -321,7 +324,7 @@ Example: sub status_accepted { my $self = shift; my $c = shift; - my %p = validate( @_, { entity => 1, }, ); + my %p = Params::Validate::validate( @_, { entity => 1, }, ); $c->response->status(202); $self->_set_entity( $c, $p{'entity'} ); @@ -337,7 +340,7 @@ response. Example: $self->status_bad_request( - $c, + $c, message => "Cannot do what you have asked!", ); @@ -346,7 +349,7 @@ Example: sub status_bad_request { my $self = shift; my $c = shift; - my %p = validate( @_, { message => { type => SCALAR }, }, ); + my %p = Params::Validate::validate( @_, { message => { type => SCALAR }, }, ); $c->response->status(400); $c->log->debug( "Status Bad Request: " . $p{'message'} ) if $c->debug; @@ -363,7 +366,7 @@ response. Example: $self->status_not_found( - $c, + $c, message => "Cannot find what you were looking for!", ); @@ -372,7 +375,7 @@ Example: sub status_not_found { my $self = shift; my $c = shift; - my %p = validate( @_, { message => { type => SCALAR }, }, ); + my %p = Params::Validate::validate( @_, { message => { type => SCALAR }, }, ); $c->response->status(404); $c->log->debug( "Status Not Found: " . $p{'message'} ) if $c->debug; @@ -415,13 +418,14 @@ This class provides a default configuration for Serialization. It is currently: 'text/html' => 'YAML::HTML', 'text/xml' => 'XML::Simple', 'text/x-yaml' => 'YAML', + 'application/json' => 'JSON', 'text/x-json' => 'JSON', 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ], 'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ], 'text/x-data-taxi' => [ 'Data::Serializer', 'Data::Taxi' ], - 'application/x-storable' => [ 'Data::Serializer', 'Storable' + 'application/x-storable' => [ 'Data::Serializer', 'Storable' ], - 'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' + 'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' ], 'text/x-config-general' => [ 'Data::Serializer', 'Config::General' ] , @@ -438,7 +442,7 @@ L. The C method uses L. The C method uses L. If you want to override either behavior, simply implement your own C and C actions -and use NEXT: +and use MRO::Compat: my Foo::Controller::Monkey; use base qw(Catalyst::Controller::REST); @@ -446,17 +450,19 @@ and use NEXT: sub begin :Private { my ($self, $c) = @_; ... do things before Deserializing ... - $self->NEXT::begin($c); + $self->maybe::next::method($c); ... do things after Deserializing ... - } + } sub end :Private { my ($self, $c) = @_; ... do things before Serializing ... - $self->NEXT::end($c); + $self->maybe::next::method($c); ... do things after Serializing ... } +=back + =head1 A MILD WARNING I have code in production using L. That said, @@ -482,6 +488,10 @@ Adam Jacob , with lots of help from mst and jrockway Marchex, Inc. paid me while I developed this module. (http://www.marchex.com) +=head1 MAINTAINER + +J. Shirley + =head1 LICENSE You may distribute this code under the same terms as Perl itself.