}
}
+=head1 NAME
+
+Catalyst::Action::Deserialize - Deserialize Data in a Request
+
+=head1 SYNOPSIS
+
+ package Foo::Controller::Bar;
+
+ __PACKAGE__->config(
+ serialize => {
+ 'default' => 'YAML',
+ 'stash_key' => 'rest',
+ 'map' => {
+ 'text/x-yaml' => 'YAML',
+ 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ],
+ },
+ }
+ );
+
+ sub begin : ActionClass('Deserialize') {}
+
+=head1 DESCRIPTION
+
+This action will deserialize HTTP POST, PUT, and OPTIONS requests.
+It assumes that the body of the HTTP Request is a serialized object.
+The serializer is selected by introspecting the requests content-type
+header.
+
+It requires that your Catalyst controller have a "serialize" entry
+in it's configuration.
+
+The specifics of deserializing each content-type is implemented as
+a plugin to L<Catalyst::Action::Deserialize>. You can see a list
+of currently implemented plugins in L<Catalyst::Controller::REST>.
+
+The results of your Deserializing will wind up in $c->req->data.
+This is done through the magic of L<Catalyst::Request::REST>.
+
+=head1 CONFIGURATION
+
+=over 4
+
+=item default
+
+The default Serialization format. See the next section for
+available options.
+
+=item map
+
+Takes a hashref, mapping Content-Types to a given plugin.
+
+=back
+
+=head1 SEE ALSO
+
+You likely want to look at L<Catalyst::Controller::REST>, which implements
+a sensible set of defaults for a controller doing REST.
+
+L<Catalyst::Action::Serialize>, L<Catalyst::Action::REST>
+
+=head1 AUTHOR
+
+Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and jrockway
+
+Marchex, Inc. paid me while I developed this module. (http://www.marchex.com)
+
+=head1 LICENSE
+
+You may distribute this code under the same terms as Perl itself.
+
+=cut
+
1;
}
1;
+
+=head1 NAME
+
+Catalyst::Action::Serialize - Serialize Data in a Response
+
+=head1 SYNOPSIS
+
+ package Foo::Controller::Bar;
+
+ __PACKAGE__->config(
+ serialize => {
+ 'default' => 'YAML',
+ 'stash_key' => 'rest',
+ 'map' => {
+ 'text/x-yaml' => 'YAML',
+ 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ],
+ },
+ }
+ );
+
+ sub end : ActionClass('Serialize') {}
+
+=head1 DESCRIPTION
+
+This action will serialize the body of an HTTP Response. The serializer is
+selected by introspecting the requests content-type header.
+
+It requires that your Catalyst controller have a "serialize" entry
+in it's configuration.
+
+The specifics of serializing each content-type is implemented as
+a plugin to L<Catalyst::Action::Serialize>.
+
+=head1 CONFIGURATION
+
+=over 4
+
+=item default
+
+The default Serialization format. See the next section for
+available options. This is used if a requested content-type
+is not recognized.
+
+=item stash_key
+
+Where in the stash the data you want serialized lives.
+
+=item map
+
+Takes a hashref, mapping Content-Types to a given plugin.
+
+=back
+
+=head1 SEE ALSO
+
+You likely want to look at L<Catalyst::Controller::REST>, which implements
+a sensible set of defaults for a controller doing REST.
+
+L<Catalyst::Action::Deserialize>, L<Catalyst::Action::REST>
+
+=head1 AUTHOR
+
+Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and jrockway
+
+Marchex, Inc. paid me while I developed this module. (http://www.marchex.com)
+
+=head1 LICENSE
+
+You may distribute this code under the same terms as Perl itself.
+
+=cut
package Catalyst::Controller::REST;
+=head1 NAME
+
+Catalyst::Controller::REST - A RESTful controller
+
+=head1 SYNOPSIS
+
+ package Foo::Controller::Bar;
+
+ use base 'Catalyst::Controller::REST';
+
+ sub thing : Local : ActionClass('REST') { }
+
+ # Answer GET requests to "thing"
+ sub thing_GET {
+ my ( $self, $c ) = @_;
+
+ # Return a 200 OK, with the data in entity
+ # serialized in the body
+ $self->status_ok(
+ $c,
+ entity => {
+ some => 'data',
+ foo => 'is real bar-y',
+ },
+ );
+ }
+
+ # Answer PUT requests to "thing"
+ sub thing_PUT {
+ .. some action ..
+ }
+
+=head1 DESCRIPTION
+
+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,
+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').
+
+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".
+
+Any unimplemented HTTP METHODS will be met with a "405 Method Not Allowed"
+response, automatically containing the proper list of available methods.
+
+The HTTP POST, PUT, and OPTIONS methods will all automatically deserialize the
+contents of $c->request->body based on the requests content-type header.
+A list of understood serialization formats is below.
+
+Also included in this class are several helper methods, which
+will automatically handle setting up proper response objects
+for you.
+
+To make your Controller RESTful, simply have it
+
+ use base 'Catalyst::Controller::REST';
+
+=head1 SERIALIZATION
+
+Catalyst::Controller::REST will automatically serialize your
+responses. The currently implemented serialization formats are:
+
+ text/x-yaml -> YAML::Syck
+ text/x-data-dumper -> Data::Serializer
+
+By default, L<Catalyst::Controller::REST> will use YAML as
+the serialization format.
+
+Implementing new Serialization formats is easy! Contributions
+are most welcome! See L<Catalyst::Action::Serialize> and
+L<Catalyst::Action::Deserialize> for more information.
+
+=head1 STATUS HELPERS
+
+These helpers try and conform to the HTTP 1.1 Specification. You can
+refer to it at: http://www.w3.org/Protocols/rfc2616/rfc2616.txt.
+These routines are all implemented as regular subroutines, and as
+such require you pass the current context ($c) as the first argument.
+
+=over 4
+
+=cut
+
use strict;
use warnings;
use base 'Catalyst::Controller';
}
);
+
sub begin : ActionClass('Deserialize') {}
sub end : ActionClass('Serialize') { }
-# You probably want to refer to the HTTP 1.1 Spec for these; they should
-# conform as much as possible.
-#
-# ftp://ftp.isi.edu/in-notes/rfc2616.txt
+=item status_ok
+
+Returns a "200 OK" response. Takes an "entity" to serialize.
+
+Example:
+
+ $self->status_ok(
+ $c,
+ entity => {
+ radiohead => "Is a good band!",
+ }
+ );
+
+=cut
+
+sub status_ok {
+ my $self = shift;
+ my $c = shift;
+ my %p = validate(@_,
+ {
+ entity => 1,
+ },
+ );
+
+ $c->response->status(200);
+ $self->_set_entity($c, $p{'entity'});
+ return 1;
+}
+
+=item status_created
+
+Returns a "201 CREATED" response. Takes an "entity" to serialize,
+and a "location" where the created object can be found.
+
+Example:
+
+ $self->status_created(
+ $c,
+ location => $c->req->uri->as_string,
+ entity => {
+ radiohead => "Is a good band!",
+ }
+ );
+
+In the above example, we use the requested URI as our location.
+This is probably what you want for most PUT requests.
+
+=cut
sub status_created {
my $self = shift;
return 1;
}
-sub status_ok {
+=item status_accepted
+
+Returns a "202 ACCEPTED" response. Takes an "entity" to serialize.
+
+Example:
+
+ $self->status_accepted(
+ $c,
+ entity => {
+ status => "queued",
+ }
+ );
+
+=cut
+sub status_accepted {
my $self = shift;
my $c = shift;
my %p = validate(@_,
},
);
- $c->response->status(200);
+ $c->response->status(202);
$self->_set_entity($c, $p{'entity'});
return 1;
}
+=item status_bad_request
+
+Returns a "400 BAD REQUEST" response. Takes a "message" argument
+as a scalar, which will become the value of "error" in the serialized
+response.
+
+Example:
+
+ $self->status_bad_request(
+ $c,
+ entity => {
+ message => "Cannot do what you have asked!",
+ }
+ );
+
+=cut
sub status_bad_request {
my $self = shift;
my $c = shift;
return 1;
}
+=item status_not_found
+
+Returns a "404 NOT FOUND" response. Takes a "message" argument
+as a scalar, which will become the value of "error" in the serialized
+response.
+
+Example:
+
+ $self->status_not_found(
+ $c,
+ entity => {
+ message => "Cannot find what you were looking for!",
+ }
+ );
+
+=cut
sub status_not_found {
my $self = shift;
my $c = shift;
return 1;
}
+=back
+
+=head1 MANUAL RESPONSES
+
+If you want to construct your responses yourself, all you need to
+do is put the object you want serialized in $c->stash->{'rest'}.
+
+=head1 SEE ALSO
+
+L<Catalyst::Action::REST>, L<Catalyst::Action::Serialize>,
+L<Catalyst::Action::Deserialize>
+
+For help with REST in general:
+
+The HTTP 1.1 Spec is required reading. http://www.w3.org/Protocols/rfc2616/rfc2616.txt
+
+Wikipedia! http://en.wikipedia.org/wiki/Representational_State_Transfer
+
+The REST Wiki: http://rest.blueoxen.net/cgi-bin/wiki.pl?FrontPage
+
+=head1 AUTHOR
+
+Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and jrockway
+
+Marchex, Inc. paid me while I developed this module. (http://www.marchex.com)
+
+=head1 LICENSE
+
+You may distribute this code under the same terms as Perl itself.
+
+=cut
+
1;