Cosmetic changes to C::C::REST POD
[catagits/Catalyst-Action-REST.git] / lib / Catalyst / Controller / REST.pm
CommitLineData
256c894f 1package Catalyst::Controller::REST;
a66af307 2use strict;
3use warnings;
256c894f 4
eaa7cec1 5our $VERSION = '0.78';
a66af307 6$VERSION = eval $VERSION;
832e768d 7
398c5a1b 8=head1 NAME
9
db8bb647 10Catalyst::Controller::REST - A RESTful controller
398c5a1b 11
12=head1 SYNOPSIS
13
14 package Foo::Controller::Bar;
15
16 use base 'Catalyst::Controller::REST';
17
18 sub thing : Local : ActionClass('REST') { }
19
20 # Answer GET requests to "thing"
21 sub thing_GET {
22 my ( $self, $c ) = @_;
db8bb647 23
398c5a1b 24 # Return a 200 OK, with the data in entity
db8bb647 25 # serialized in the body
398c5a1b 26 $self->status_ok(
db8bb647 27 $c,
398c5a1b 28 entity => {
29 some => 'data',
30 foo => 'is real bar-y',
31 },
32 );
33 }
34
35 # Answer PUT requests to "thing"
db8bb647 36 sub thing_PUT {
95318468 37 ... some action ...
398c5a1b 38 }
39
40=head1 DESCRIPTION
41
42Catalyst::Controller::REST implements a mechanism for building
43RESTful services in Catalyst. It does this by extending the
db8bb647 44normal Catalyst dispatch mechanism to allow for different
45subroutines to be called based on the HTTP Method requested,
398c5a1b 46while also transparently handling all the serialization/deserialization for
47you.
48
49This is probably best served by an example. In the above
50controller, we have declared a Local Catalyst action on
db8bb647 51"sub thing", and have used the ActionClass('REST').
398c5a1b 52
53Below, we have declared "thing_GET" and "thing_PUT". Any
db8bb647 54GET requests to thing will be dispatched to "thing_GET",
55while any PUT requests will be dispatched to "thing_PUT".
398c5a1b 56
e601adda 57Any unimplemented HTTP methods will be met with a "405 Method Not Allowed"
58response, automatically containing the proper list of available methods. You
59can override this behavior through implementing a custom
db8bb647 60C<thing_not_implemented> method.
e601adda 61
62If you do not provide an OPTIONS handler, we will respond to any OPTIONS
63requests with a "200 OK", populating the Allowed header automatically.
64
65Any data included in C<< $c->stash->{'rest'} >> will be serialized for you.
66The serialization format will be selected based on the content-type
67of the incoming request. It is probably easier to use the L<STATUS HELPERS>,
68which are described below.
398c5a1b 69
70The HTTP POST, PUT, and OPTIONS methods will all automatically deserialize the
71contents of $c->request->body based on the requests content-type header.
72A list of understood serialization formats is below.
73
e601adda 74If we do not have (or cannot run) a serializer for a given content-type, a 415
db8bb647 75"Unsupported Media Type" error is generated.
398c5a1b 76
77To make your Controller RESTful, simply have it
78
95318468 79 BEGIN {extends 'Catalyst::Controller::REST'; }
80
81Or if you use pre-Moose Catalyst versions,
82
83 use parent 'Catalyst::Controller::REST';
84
398c5a1b 85
86=head1 SERIALIZATION
87
88Catalyst::Controller::REST will automatically serialize your
e601adda 89responses, and deserialize any POST, PUT or OPTIONS requests. It evaluates
90which serializer to use by mapping a content-type to a Serialization module.
db8bb647 91We select the content-type based on:
e601adda 92
93=over 2
94
95=item B<The Content-Type Header>
96
97If the incoming HTTP Request had a Content-Type header set, we will use it.
98
99=item B<The content-type Query Parameter>
100
101If this is a GET request, you can supply a content-type query parameter.
102
103=item B<Evaluating the Accept Header>
104
105Finally, if the client provided an Accept header, we will evaluate
db8bb647 106it and use the best-ranked choice.
e601adda 107
108=back
109
95318468 110
e601adda 111=head1 AVAILABLE SERIALIZERS
112
113A given serialization mechanism is only available if you have the underlying
114modules installed. For example, you can't use XML::Simple if it's not already
db8bb647 115installed.
e601adda 116
95318468 117In addition, each serializer has its quirks in terms of what sorts of data
e601adda 118structures it will properly handle. L<Catalyst::Controller::REST> makes
db8bb647 119no attempt to save you from yourself in this regard. :)
e601adda 120
121=over 2
122
95318468 123=item * C<text/x-yaml> => C<YAML::Syck>
e601adda 124
125Returns YAML generated by L<YAML::Syck>.
126
95318468 127=item * C<text/html> => C<YAML::HTML>
e601adda 128
129This uses L<YAML::Syck> and L<URI::Find> to generate YAML with all URLs turned
130to hyperlinks. Only useable for Serialization.
131
95318468 132=item * C<application/json> => C<JSON>
e601adda 133
db8bb647 134Uses L<JSON> to generate JSON output. It is strongly advised to also have
e540a1fa 135L<JSON::XS> installed. The C<text/x-json> content type is supported but is
136deprecated and you will receive warnings in your log.
e601adda 137
95318468 138=item * C<text/x-data-dumper> => C<Data::Serializer>
e601adda 139
140Uses the L<Data::Serializer> module to generate L<Data::Dumper> output.
141
95318468 142=item * C<text/x-data-denter> => C<Data::Serializer>
e601adda 143
144Uses the L<Data::Serializer> module to generate L<Data::Denter> output.
145
95318468 146=item * C<text/x-data-taxi> => C<Data::Serializer>
e601adda 147
148Uses the L<Data::Serializer> module to generate L<Data::Taxi> output.
149
95318468 150=item * C<application/x-storable> => C<Data::Serializer>
e601adda 151
152Uses the L<Data::Serializer> module to generate L<Storable> output.
153
95318468 154=item * C<application/x-freezethaw> => C<Data::Serializer>
e601adda 155
156Uses the L<Data::Serializer> module to generate L<FreezeThaw> output.
157
95318468 158=item * C<text/x-config-general> => C<Data::Serializer>
e601adda 159
160Uses the L<Data::Serializer> module to generate L<Config::General> output.
161
95318468 162=item * C<text/x-php-serialization> => C<Data::Serializer>
e601adda 163
164Uses the L<Data::Serializer> module to generate L<PHP::Serialization> output.
165
95318468 166=item * C<text/xml> => C<XML::Simple>
e601adda 167
168Uses L<XML::Simple> to generate XML output. This is probably not suitable
169for any real heavy XML work. Due to L<XML::Simple>s requirement that the data
170you serialize be a HASHREF, we transform outgoing data to be in the form of:
171
172 { data => $yourdata }
173
95318468 174=item * L<View>
9a76221e 175
db8bb647 176Uses a regular Catalyst view. For example, if you wanted to have your
9a76221e 177C<text/html> and C<text/xml> views rendered by TT:
178
21d3f6ae 179 'text/html' => [ 'View', 'TT' ],
180 'text/xml' => [ 'View', 'XML' ],
db8bb647 181
182Will do the trick nicely.
9a76221e 183
e601adda 184=back
185
95318468 186By default, L<Catalyst::Controller::REST> will return a
187C<415 Unsupported Media Type> response if an attempt to use an unsupported
188content-type is made. You can ensure that something is always returned by
189setting the C<default> config option:
398c5a1b 190
21d3f6ae 191 __PACKAGE__->config->{'default'} = 'text/x-yaml';
398c5a1b 192
95318468 193would make it always fall back to the serializer plugin defined for
194C<text/x-yaml>.
398c5a1b 195
e601adda 196=head1 CUSTOM SERIALIZERS
197
95318468 198Implementing new Serialization formats is easy! Contributions
199are most welcome! If you would like to implement a custom serializer,
200you should create two new modules in the L<Catalyst::Action::Serialize>
201and L<Catalyst::Action::Deserialize> namespace. Then assign your new
202class to the content-type's you want, and you're done.
203
204See L<Catalyst::Action::Serialize> and L<Catalyst::Action::Deserialize>
205for more information.
e601adda 206
398c5a1b 207=head1 STATUS HELPERS
208
e601adda 209Since so much of REST is in using HTTP, we provide these Status Helpers.
210Using them will ensure that you are responding with the proper codes,
211headers, and entities.
212
398c5a1b 213These helpers try and conform to the HTTP 1.1 Specification. You can
db8bb647 214refer to it at: L<http://www.w3.org/Protocols/rfc2616/rfc2616.txt>.
398c5a1b 215These routines are all implemented as regular subroutines, and as
216such require you pass the current context ($c) as the first argument.
217
218=over 4
219
220=cut
221
256c894f 222use base 'Catalyst::Controller';
d4611771 223use Params::Validate qw(SCALAR OBJECT);
256c894f 224
225__PACKAGE__->mk_accessors(qw(serialize));
226
227__PACKAGE__->config(
e540a1fa 228 'stash_key' => 'rest',
229 'map' => {
230 'text/html' => 'YAML::HTML',
231 'text/xml' => 'XML::Simple',
232 'text/x-yaml' => 'YAML',
233 'application/json' => 'JSON',
234 'text/x-json' => 'JSON',
235 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ],
236 'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ],
237 'text/x-data-taxi' => [ 'Data::Serializer', 'Data::Taxi' ],
95318468 238 'application/x-storable' => [ 'Data::Serializer', 'Storable' ],
239 'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' ],
240 'text/x-config-general' => [ 'Data::Serializer', 'Config::General' ],
e540a1fa 241 'text/x-php-serialization' => [ 'Data::Serializer', 'PHP::Serialization' ],
242 },
256c894f 243);
244
e540a1fa 245sub begin : ActionClass('Deserialize') { }
5511d1ff 246
0ba73721 247sub end : ActionClass('Serialize') { }
248
398c5a1b 249=item status_ok
250
251Returns a "200 OK" response. Takes an "entity" to serialize.
252
253Example:
254
255 $self->status_ok(
db8bb647 256 $c,
398c5a1b 257 entity => {
258 radiohead => "Is a good band!",
259 }
260 );
261
262=cut
263
264sub status_ok {
265 my $self = shift;
e601adda 266 my $c = shift;
d4611771 267 my %p = Params::Validate::validate( @_, { entity => 1, }, );
398c5a1b 268
269 $c->response->status(200);
e601adda 270 $self->_set_entity( $c, $p{'entity'} );
398c5a1b 271 return 1;
272}
273
274=item status_created
275
276Returns a "201 CREATED" response. Takes an "entity" to serialize,
277and a "location" where the created object can be found.
278
279Example:
280
281 $self->status_created(
db8bb647 282 $c,
398c5a1b 283 location => $c->req->uri->as_string,
284 entity => {
285 radiohead => "Is a good band!",
286 }
287 );
288
289In the above example, we use the requested URI as our location.
290This is probably what you want for most PUT requests.
291
292=cut
bb4130f6 293
5511d1ff 294sub status_created {
295 my $self = shift;
e601adda 296 my $c = shift;
d4611771 297 my %p = Params::Validate::validate(
e601adda 298 @_,
5511d1ff 299 {
e601adda 300 location => { type => SCALAR | OBJECT },
301 entity => { optional => 1 },
5511d1ff 302 },
303 );
256c894f 304
5511d1ff 305 my $location;
e601adda 306 if ( ref( $p{'location'} ) ) {
5511d1ff 307 $location = $p{'location'}->as_string;
33e5de96 308 } else {
309 $location = $p{'location'};
5511d1ff 310 }
311 $c->response->status(201);
e601adda 312 $c->response->header( 'Location' => $location );
313 $self->_set_entity( $c, $p{'entity'} );
bb4130f6 314 return 1;
315}
316
398c5a1b 317=item status_accepted
318
319Returns a "202 ACCEPTED" response. Takes an "entity" to serialize.
320
321Example:
322
323 $self->status_accepted(
db8bb647 324 $c,
398c5a1b 325 entity => {
326 status => "queued",
327 }
328 );
329
330=cut
e601adda 331
398c5a1b 332sub status_accepted {
bb4130f6 333 my $self = shift;
e601adda 334 my $c = shift;
d4611771 335 my %p = Params::Validate::validate( @_, { entity => 1, }, );
bb4130f6 336
398c5a1b 337 $c->response->status(202);
e601adda 338 $self->_set_entity( $c, $p{'entity'} );
bb4130f6 339 return 1;
340}
341
bbf0feae 342=item status_no_content
343
344Returns a "204 NO CONTENT" response.
345
346=cut
347
348sub status_no_content {
349 my $self = shift;
350 my $c = shift;
351 $c->response->status(204);
352 $self->_set_entity( $c, undef );
353 return 1.;
354}
355
398c5a1b 356=item status_bad_request
357
358Returns a "400 BAD REQUEST" response. Takes a "message" argument
359as a scalar, which will become the value of "error" in the serialized
360response.
361
362Example:
363
364 $self->status_bad_request(
db8bb647 365 $c,
33e5de96 366 message => "Cannot do what you have asked!",
398c5a1b 367 );
368
369=cut
e601adda 370
cc186a5b 371sub status_bad_request {
372 my $self = shift;
e601adda 373 my $c = shift;
d4611771 374 my %p = Params::Validate::validate( @_, { message => { type => SCALAR }, }, );
cc186a5b 375
376 $c->response->status(400);
faf5c20b 377 $c->log->debug( "Status Bad Request: " . $p{'message'} ) if $c->debug;
e601adda 378 $self->_set_entity( $c, { error => $p{'message'} } );
cc186a5b 379 return 1;
380}
381
398c5a1b 382=item status_not_found
383
384Returns a "404 NOT FOUND" response. Takes a "message" argument
385as a scalar, which will become the value of "error" in the serialized
386response.
387
388Example:
389
390 $self->status_not_found(
db8bb647 391 $c,
33e5de96 392 message => "Cannot find what you were looking for!",
398c5a1b 393 );
394
395=cut
e601adda 396
bb4130f6 397sub status_not_found {
398 my $self = shift;
e601adda 399 my $c = shift;
d4611771 400 my %p = Params::Validate::validate( @_, { message => { type => SCALAR }, }, );
bb4130f6 401
402 $c->response->status(404);
faf5c20b 403 $c->log->debug( "Status Not Found: " . $p{'message'} ) if $c->debug;
e601adda 404 $self->_set_entity( $c, { error => $p{'message'} } );
bb4130f6 405 return 1;
406}
407
bbf0feae 408=item gone
409
410Returns a "41O GONE" response. Takes a "message" argument as a scalar,
411which will become the value of "error" in the serialized response.
412
413Example:
414
415 $self->status_gone(
416 $c,
417 message => "The document have been deleted by foo",
418 );
419
420=cut
421
422sub status_gone {
423 my $self = shift;
424 my $c = shift;
425 my %p = Params::Validate::validate( @_, { message => { type => SCALAR }, }, );
426
427 $c->response->status(410);
428 $c->log->debug( "Status Gone " . $p{'message'} ) if $c->debug;
429 $self->_set_entity( $c, { error => $p{'message'} } );
430 return 1;
431}
432
bb4130f6 433sub _set_entity {
e601adda 434 my $self = shift;
435 my $c = shift;
bb4130f6 436 my $entity = shift;
e601adda 437 if ( defined($entity) ) {
faf5c20b 438 $c->stash->{ $self->{'stash_key'} } = $entity;
5511d1ff 439 }
440 return 1;
eccb2137 441}
256c894f 442
398c5a1b 443=back
444
445=head1 MANUAL RESPONSES
446
447If you want to construct your responses yourself, all you need to
448do is put the object you want serialized in $c->stash->{'rest'}.
449
e601adda 450=head1 IMPLEMENTATION DETAILS
451
452This Controller ties together L<Catalyst::Action::REST>,
453L<Catalyst::Action::Serialize> and L<Catalyst::Action::Deserialize>. It should be suitable for most applications. You should be aware that it:
454
455=over 4
456
457=item Configures the Serialization Actions
458
459This class provides a default configuration for Serialization. It is currently:
460
461 __PACKAGE__->config(
95318468 462 'stash_key' => 'rest',
463 'map' => {
464 'text/html' => 'YAML::HTML',
465 'text/xml' => 'XML::Simple',
466 'text/x-yaml' => 'YAML',
467 'application/json' => 'JSON',
468 'text/x-json' => 'JSON',
469 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ],
470 'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ],
471 'text/x-data-taxi' => [ 'Data::Serializer', 'Data::Taxi' ],
472 'application/x-storable' => [ 'Data::Serializer', 'Storable' ],
473 'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' ],
474 'text/x-config-general' => [ 'Data::Serializer', 'Config::General' ],
475 'text/x-php-serialization' => [ 'Data::Serializer', 'PHP::Serialization' ],
476 },
e601adda 477 );
478
479You can read the full set of options for this configuration block in
480L<Catalyst::Action::Serialize>.
481
482=item Sets a C<begin> and C<end> method for you
483
484The C<begin> method uses L<Catalyst::Action::Deserialize>. The C<end>
485method uses L<Catalyst::Action::Serialize>. If you want to override
486either behavior, simply implement your own C<begin> and C<end> actions
def65dcc 487and use MRO::Compat:
e601adda 488
489 my Foo::Controller::Monkey;
490 use base qw(Catalyst::Controller::REST);
491
492 sub begin :Private {
493 my ($self, $c) = @_;
db8bb647 494 ... do things before Deserializing ...
495 $self->maybe::next::method($c);
e601adda 496 ... do things after Deserializing ...
db8bb647 497 }
e601adda 498
499 sub end :Private {
500 my ($self, $c) = @_;
db8bb647 501 ... do things before Serializing ...
def65dcc 502 $self->maybe::next::method($c);
e601adda 503 ... do things after Serializing ...
504 }
505
e540a1fa 506=back
507
e601adda 508=head1 A MILD WARNING
509
510I have code in production using L<Catalyst::Controller::REST>. That said,
511it is still under development, and it's possible that things may change
512between releases. I promise to not break things unneccesarily. :)
513
398c5a1b 514=head1 SEE ALSO
515
516L<Catalyst::Action::REST>, L<Catalyst::Action::Serialize>,
517L<Catalyst::Action::Deserialize>
518
519For help with REST in general:
520
521The HTTP 1.1 Spec is required reading. http://www.w3.org/Protocols/rfc2616/rfc2616.txt
522
523Wikipedia! http://en.wikipedia.org/wiki/Representational_State_Transfer
524
525The REST Wiki: http://rest.blueoxen.net/cgi-bin/wiki.pl?FrontPage
526
527=head1 AUTHOR
528
529Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and jrockway
530
531Marchex, Inc. paid me while I developed this module. (http://www.marchex.com)
532
e540a1fa 533=head1 MAINTAINER
534
535J. Shirley <jshirley@cpan.org>
536
398c5a1b 537=head1 LICENSE
538
539You may distribute this code under the same terms as Perl itself.
540
541=cut
542
256c894f 5431;