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