12568f7eab8f56b0690d0ac65b66b343c711174a
[catagits/Catalyst-Action-REST.git] / lib / Catalyst / Controller / REST.pm
1 package Catalyst::Controller::REST;
2
3 use Moose;
4 use namespace::autoclean;
5
6 =head1 NAME
7
8 Catalyst::Controller::REST - A RESTful controller
9
10 =head1 SYNOPSIS
11
12     package Foo::Controller::Bar;
13     use Moose;
14     use namespace::autoclean;
15
16     BEGIN { extends '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         my ( $self, $c ) = @_;
38
39         $radiohead = $c->req->data->{radiohead};
40
41         $self->status_created(
42             $c,
43             location => $c->req->uri,
44             entity => {
45                 radiohead => $radiohead,
46             }
47         );
48     }
49
50 =head1 DESCRIPTION
51
52 Catalyst::Controller::REST implements a mechanism for building
53 RESTful services in Catalyst.  It does this by extending the
54 normal Catalyst dispatch mechanism to allow for different
55 subroutines to be called based on the HTTP Method requested,
56 while also transparently handling all the serialization/deserialization for
57 you.
58
59 This is probably best served by an example.  In the above
60 controller, we have declared a Local Catalyst action on
61 "sub thing", and have used the ActionClass('REST').
62
63 Below, we have declared "thing_GET" and "thing_PUT".  Any
64 GET requests to thing will be dispatched to "thing_GET",
65 while any PUT requests will be dispatched to "thing_PUT".
66
67 Any unimplemented HTTP methods will be met with a "405 Method Not Allowed"
68 response, automatically containing the proper list of available methods.  You
69 can override this behavior through implementing a custom
70 C<thing_not_implemented> method.
71
72 If you do not provide an OPTIONS handler, we will respond to any OPTIONS
73 requests with a "200 OK", populating the Allowed header automatically.
74
75 Any data included in C<< $c->stash->{'rest'} >> will be serialized for you.
76 The serialization format will be selected based on the content-type
77 of the incoming request.  It is probably easier to use the L<STATUS HELPERS>,
78 which are described below.
79
80 "The HTTP POST, PUT, and OPTIONS methods will all automatically
81 L<deserialize|Catalyst::Action::Deserialize> the contents of
82 C<< $c->request->body >> into the C<< $c->request->data >> hashref", based on
83 the request's C<Content-type> header. A list of understood serialization
84 formats is L<below|/AVAILABLE SERIALIZERS>.
85
86 If we do not have (or cannot run) a serializer for a given content-type, a 415
87 "Unsupported Media Type" error is generated.
88
89 To make your Controller RESTful, simply have it
90
91   BEGIN { extends 'Catalyst::Controller::REST' }
92
93 =head1 CONFIGURATION
94
95 See L<Catalyst::Action::Serialize/CONFIGURATION>. Note that the C<serialize>
96 key has been deprecated.
97
98 =head1 SERIALIZATION
99
100 Catalyst::Controller::REST will automatically serialize your
101 responses, and deserialize any POST, PUT or OPTIONS requests. It evaluates
102 which serializer to use by mapping a content-type to a Serialization module.
103 We select the content-type based on:
104
105 =over
106
107 =item B<The Content-Type Header>
108
109 If the incoming HTTP Request had a Content-Type header set, we will use it.
110
111 =item B<The content-type Query Parameter>
112
113 If this is a GET request, you can supply a content-type query parameter.
114
115 =item B<Evaluating the Accept Header>
116
117 Finally, if the client provided an Accept header, we will evaluate
118 it and use the best-ranked choice.
119
120 =back
121
122 =head1 AVAILABLE SERIALIZERS
123
124 A given serialization mechanism is only available if you have the underlying
125 modules installed.  For example, you can't use XML::Simple if it's not already
126 installed.
127
128 In addition, each serializer has its quirks in terms of what sorts of data
129 structures it will properly handle.  L<Catalyst::Controller::REST> makes
130 no attempt to save you from yourself in this regard. :)
131
132 =over 2
133
134 =item * C<text/x-yaml> => C<YAML::Syck>
135
136 Returns YAML generated by L<YAML::Syck>.
137
138 =item * C<text/html> => C<YAML::HTML>
139
140 This uses L<YAML::Syck> and L<URI::Find> to generate YAML with all URLs turned
141 to hyperlinks.  Only usable for Serialization.
142
143 =item * C<application/json> => C<JSON>
144
145 Uses L<JSON> to generate JSON output.  It is strongly advised to also have
146 L<JSON::XS> installed.  The C<text/x-json> content type is supported but is
147 deprecated and you will receive warnings in your log.
148
149 You can also add a hash in your controller config to pass options to the json object.
150 There are two options. C<json_options> are used when decoding incoming JSON, and C<json_options_encode> 
151 is used when encoding JSON for output.
152
153 For instance, to relax permissions when deserializing input, add:
154
155   __PACKAGE__->config(
156     json_options => { relaxed => 1 }
157   )
158
159 To indent the JSON output so it becomes more human readable, add:
160
161   __PACKAGE__->config(
162     json_options_encode => { indent => 1 }
163   )
164
165
166 =item * C<text/javascript> => C<JSONP>
167
168 If a callback=? parameter is passed, this returns javascript in the form of: $callback($serializedJSON);
169
170 Note - this is disabled by default as it can be a security risk if you are unaware.
171
172 The usual MIME types for this serialization format are: 'text/javascript', 'application/x-javascript',
173 'application/javascript'.
174
175 =item * C<text/x-data-dumper> => C<Data::Serializer>
176
177 Uses the L<Data::Serializer> module to generate L<Data::Dumper> output.
178
179 =item * C<text/x-data-denter> => C<Data::Serializer>
180
181 Uses the L<Data::Serializer> module to generate L<Data::Denter> output.
182
183 =item * C<text/x-data-taxi> => C<Data::Serializer>
184
185 Uses the L<Data::Serializer> module to generate L<Data::Taxi> output.
186
187 =item * C<text/x-config-general> => C<Data::Serializer>
188
189 Uses the L<Data::Serializer> module to generate L<Config::General> output.
190
191 =item * C<text/x-php-serialization> => C<Data::Serializer>
192
193 Uses the L<Data::Serializer> module to generate L<PHP::Serialization> output.
194
195 =item * C<text/xml> => C<XML::Simple>
196
197 Uses L<XML::Simple> to generate XML output.  This is probably not suitable
198 for any real heavy XML work. Due to L<XML::Simple>s requirement that the data
199 you serialize be a HASHREF, we transform outgoing data to be in the form of:
200
201   { data => $yourdata }
202
203 =item * L<View>
204
205 Uses a regular Catalyst view.  For example, if you wanted to have your
206 C<text/html> and C<text/xml> views rendered by TT, set:
207
208   __PACKAGE__->config(
209       map => {
210           'text/html' => [ 'View', 'TT' ],
211           'text/xml'  => [ 'View', 'XML' ],
212       }
213   );
214
215 Your views should have a C<process> method like this:
216
217   sub process {
218       my ( $self, $c, $stash_key ) = @_;
219
220       my $output;
221       eval {
222           $output = $self->serialize( $c->stash->{$stash_key} );
223       };
224       return $@ if $@;
225
226       $c->response->body( $output );
227       return 1;  # important
228   }
229
230   sub serialize {
231       my ( $self, $data ) = @_;
232
233       my $serialized = ... process $data here ...
234
235       return $serialized;
236   }
237
238 =item * Callback
239
240 For infinite flexibility, you can provide a callback for the
241 deserialization/serialization steps.
242
243   __PACKAGE__->config(
244       map => {
245           'text/xml'  => [ 'Callback', { deserialize => \&parse_xml, serialize => \&render_xml } ],
246       }
247   );
248
249 The C<deserialize> callback is passed a string that is the body of the
250 request and is expected to return a scalar value that results from
251 the deserialization.  The C<serialize> callback is passed the data
252 structure that needs to be serialized and must return a string suitable
253 for returning in the HTTP response.  In addition to receiving the scalar
254 to act on, both callbacks are passed the controller object and the context
255 (i.e. C<$c>) as the second and third arguments.
256
257 =back
258
259 By default, L<Catalyst::Controller::REST> will return a
260 C<415 Unsupported Media Type> response if an attempt to use an unsupported
261 content-type is made.  You can ensure that something is always returned by
262 setting the C<default> config option:
263
264   __PACKAGE__->config(default => 'text/x-yaml');
265
266 would make it always fall back to the serializer plugin defined for
267 C<text/x-yaml>.
268
269 =head1 CUSTOM SERIALIZERS
270
271 Implementing new Serialization formats is easy!  Contributions
272 are most welcome!  If you would like to implement a custom serializer,
273 you should create two new modules in the L<Catalyst::Action::Serialize>
274 and L<Catalyst::Action::Deserialize> namespace.  Then assign your new
275 class to the content-type's you want, and you're done.
276
277 See L<Catalyst::Action::Serialize> and L<Catalyst::Action::Deserialize>
278 for more information.
279
280 =head1 STATUS HELPERS
281
282 Since so much of REST is in using HTTP, we provide these Status Helpers.
283 Using them will ensure that you are responding with the proper codes,
284 headers, and entities.
285
286 These helpers try and conform to the HTTP 1.1 Specification.  You can
287 refer to it at: L<http://www.w3.org/Protocols/rfc2616/rfc2616.txt>.
288 These routines are all implemented as regular subroutines, and as
289 such require you pass the current context ($c) as the first argument.
290
291 =over
292
293 =cut
294
295 BEGIN { extends 'Catalyst::Controller' }
296 use Params::Validate qw(SCALAR OBJECT);
297
298 __PACKAGE__->mk_accessors(qw(serialize));
299
300 __PACKAGE__->config(
301     'stash_key' => 'rest',
302     'map'       => {
303         'text/xml'           => 'XML::Simple',
304         'application/json'   => 'JSON',
305         'text/x-json'        => 'JSON',
306     },
307 );
308
309 sub begin : ActionClass('Deserialize') { }
310
311 sub end : ActionClass('Serialize') { }
312
313 =item status_ok
314
315 Returns a "200 OK" response.  Takes an "entity" to serialize.
316
317 Example:
318
319   $self->status_ok(
320     $c,
321     entity => {
322         radiohead => "Is a good band!",
323     }
324   );
325
326 =cut
327
328 sub status_ok {
329     my $self = shift;
330     my $c    = shift;
331     my %p    = Params::Validate::validate( @_, { entity => 1, }, );
332
333     $c->response->status(200);
334     $self->_set_entity( $c, $p{'entity'} );
335     return 1;
336 }
337
338 =item status_created
339
340 Returns a "201 CREATED" response.  Takes an "entity" to serialize,
341 and a "location" where the created object can be found.
342
343 Example:
344
345   $self->status_created(
346     $c,
347     location => $c->req->uri,
348     entity => {
349         radiohead => "Is a good band!",
350     }
351   );
352
353 In the above example, we use the requested URI as our location.
354 This is probably what you want for most PUT requests.
355
356 =cut
357
358 sub status_created {
359     my $self = shift;
360     my $c    = shift;
361     my %p    = Params::Validate::validate(
362         @_,
363         {
364             location => { type     => SCALAR | OBJECT },
365             entity   => { optional => 1 },
366         },
367     );
368
369     $c->response->status(201);
370     $c->response->header( 'Location' => $p{location} );
371     $self->_set_entity( $c, $p{'entity'} );
372     return 1;
373 }
374
375 =item status_accepted
376
377 Returns a "202 ACCEPTED" response.  Takes an "entity" to serialize.
378 Also takes optional "location" for queue type scenarios.
379
380 Example:
381
382   $self->status_accepted(
383     $c,
384     location => $c->req->uri,
385     entity => {
386         status => "queued",
387     }
388   );
389
390 =cut
391
392 sub status_accepted {
393     my $self = shift;
394     my $c    = shift;
395     my %p    = Params::Validate::validate(
396         @_,
397         {
398             location => { type => SCALAR | OBJECT, optional => 1 },
399             entity   => 1,
400         },
401     );
402
403     $c->response->status(202);
404     $c->response->header( 'Location' => $p{location} ) if exists $p{location};
405     $self->_set_entity( $c, $p{'entity'} );
406     return 1;
407 }
408
409 =item status_no_content
410
411 Returns a "204 NO CONTENT" response.
412
413 =cut
414
415 sub status_no_content {
416     my $self = shift;
417     my $c    = shift;
418     $c->response->status(204);
419     $self->_set_entity( $c, undef );
420     return 1;
421 }
422
423 =item status_multiple_choices
424
425 Returns a "300 MULTIPLE CHOICES" response. Takes an "entity" to serialize, which should
426 provide list of possible locations. Also takes optional "location" for preferred choice.
427
428 =cut
429
430 sub status_multiple_choices {
431     my $self = shift;
432     my $c    = shift;
433     my %p    = Params::Validate::validate(
434         @_,
435         {
436             entity => 1,
437             location => { type     => SCALAR | OBJECT, optional => 1 },
438         },
439     );
440
441     $c->response->status(300);
442     $c->response->header( 'Location' => $p{location} ) if exists $p{'location'};
443     $self->_set_entity( $c, $p{'entity'} );
444     return 1;
445 }
446
447 =item status_found
448
449 Returns a "302 FOUND" response. Takes an "entity" to serialize.
450 Also takes optional "location".
451
452 =cut
453
454 sub status_found {
455     my $self = shift;
456     my $c    = shift;
457     my %p    = Params::Validate::validate(
458         @_,
459         {
460             entity => 1,
461             location => { type     => SCALAR | OBJECT, optional => 1 },
462         },
463     );
464
465     $c->response->status(302);
466     $c->response->header( 'Location' => $p{location} ) if exists $p{'location'};
467     $self->_set_entity( $c, $p{'entity'} );
468     return 1;
469 }
470
471 =item status_bad_request
472
473 Returns a "400 BAD REQUEST" response.  Takes a "message" argument
474 as a scalar, which will become the value of "error" in the serialized
475 response.
476
477 Example:
478
479   $self->status_bad_request(
480     $c,
481     message => "Cannot do what you have asked!",
482   );
483
484 =cut
485
486 sub status_bad_request {
487     my $self = shift;
488     my $c    = shift;
489     my %p    = Params::Validate::validate( @_, { message => { type => SCALAR }, }, );
490
491     $c->response->status(400);
492     $c->log->debug( "Status Bad Request: " . $p{'message'} ) if $c->debug;
493     $self->_set_entity( $c, { error => $p{'message'} } );
494     return 1;
495 }
496
497 =item status_forbidden
498
499 Returns a "403 FORBIDDEN" response.  Takes a "message" argument
500 as a scalar, which will become the value of "error" in the serialized
501 response.
502
503 Example:
504
505   $self->status_forbidden(
506     $c,
507     message => "access denied",
508   );
509
510 =cut
511
512 sub status_forbidden {
513     my $self = shift;
514     my $c    = shift;
515     my %p    = Params::Validate::validate( @_, { message => { type => SCALAR }, }, );
516
517     $c->response->status(403);
518     $c->log->debug( "Status Forbidden: " . $p{'message'} ) if $c->debug;
519     $self->_set_entity( $c, { error => $p{'message'} } );
520     return 1;
521 }
522
523 =item status_not_found
524
525 Returns a "404 NOT FOUND" response.  Takes a "message" argument
526 as a scalar, which will become the value of "error" in the serialized
527 response.
528
529 Example:
530
531   $self->status_not_found(
532     $c,
533     message => "Cannot find what you were looking for!",
534   );
535
536 =cut
537
538 sub status_not_found {
539     my $self = shift;
540     my $c    = shift;
541     my %p    = Params::Validate::validate( @_, { message => { type => SCALAR }, }, );
542
543     $c->response->status(404);
544     $c->log->debug( "Status Not Found: " . $p{'message'} ) if $c->debug;
545     $self->_set_entity( $c, { error => $p{'message'} } );
546     return 1;
547 }
548
549 =item gone
550
551 Returns a "41O GONE" response.  Takes a "message" argument as a scalar,
552 which will become the value of "error" in the serialized response.
553
554 Example:
555
556   $self->status_gone(
557     $c,
558     message => "The document have been deleted by foo",
559   );
560
561 =cut
562
563 sub status_gone {
564     my $self = shift;
565     my $c    = shift;
566     my %p    = Params::Validate::validate( @_, { message => { type => SCALAR }, }, );
567
568     $c->response->status(410);
569     $c->log->debug( "Status Gone " . $p{'message'} ) if $c->debug;
570     $self->_set_entity( $c, { error => $p{'message'} } );
571     return 1;
572 }
573
574 =item status_see_other
575
576 Returns a "303 See Other" response.  Takes an optional "entity" to serialize,
577 and a "location" where the client should redirect to.
578
579 Example:
580
581   $self->status_see_other(
582     $c,
583     location => $some_other_url,
584     entity => {
585         radiohead => "Is a good band!",
586     }
587   );
588
589 =cut
590
591 sub status_see_other {
592     my $self = shift;
593     my $c    = shift;
594     my %p    = Params::Validate::validate(
595         @_,
596         {
597             location => { type     => SCALAR | OBJECT },
598             entity   => { optional => 1 },
599         },
600     );
601
602     $c->response->status(303);
603     $c->response->header( 'Location' => $p{location} );
604     $self->_set_entity( $c, $p{'entity'} );
605     return 1;
606 }
607
608 =item status_moved
609
610 Returns a "301 MOVED" response.  Takes an "entity" to serialize, and a
611 "location" where the created object can be found.
612
613 Example:
614
615  $self->status_moved(
616    $c,
617    location => '/somewhere/else',
618    entity => {
619      radiohead => "Is a good band!",
620    },
621  );
622
623 =cut
624
625 sub status_moved {
626    my $self = shift;
627    my $c    = shift;
628    my %p    = Params::Validate::validate(
629       @_,
630       {
631          location => { type     => SCALAR | OBJECT },
632          entity   => { optional => 1 },
633       },
634    );
635
636    my $location = ref $p{location}
637       ? $p{location}->as_string
638       : $p{location}
639    ;
640
641    $c->response->status(301);
642    $c->response->header( Location => $location );
643    $self->_set_entity($c, $p{entity});
644    return 1;
645 }
646
647 sub _set_entity {
648     my $self   = shift;
649     my $c      = shift;
650     my $entity = shift;
651     if ( defined($entity) ) {
652         $c->stash->{ $self->{'stash_key'} } = $entity;
653     }
654     return 1;
655 }
656
657 =back
658
659 =head1 MANUAL RESPONSES
660
661 If you want to construct your responses yourself, all you need to
662 do is put the object you want serialized in $c->stash->{'rest'}.
663
664 =head1 IMPLEMENTATION DETAILS
665
666 This Controller ties together L<Catalyst::Action::REST>,
667 L<Catalyst::Action::Serialize> and L<Catalyst::Action::Deserialize>.  It should be suitable for most applications.  You should be aware that it:
668
669 =over 4
670
671 =item Configures the Serialization Actions
672
673 This class provides a default configuration for Serialization.  It is currently:
674
675   __PACKAGE__->config(
676       'stash_key' => 'rest',
677       'map'       => {
678          'text/html'          => 'YAML::HTML',
679          'text/xml'           => 'XML::Simple',
680          'text/x-yaml'        => 'YAML',
681          'application/json'   => 'JSON',
682          'text/x-json'        => 'JSON',
683          'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ],
684          'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ],
685          'text/x-data-taxi'   => [ 'Data::Serializer', 'Data::Taxi'   ],
686          'application/x-storable'   => [ 'Data::Serializer', 'Storable' ],
687          'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' ],
688          'text/x-config-general'    => [ 'Data::Serializer', 'Config::General' ],
689          'text/x-php-serialization' => [ 'Data::Serializer', 'PHP::Serialization' ],
690       },
691   );
692
693 You can read the full set of options for this configuration block in
694 L<Catalyst::Action::Serialize>.
695
696 =item Sets a C<begin> and C<end> method for you
697
698 The C<begin> method uses L<Catalyst::Action::Deserialize>.  The C<end>
699 method uses L<Catalyst::Action::Serialize>.  If you want to override
700 either behavior, simply implement your own C<begin> and C<end> actions
701 and forward to another action with the Serialize and/or Deserialize
702 action classes:
703
704   package Foo::Controller::Monkey;
705   use Moose;
706   use namespace::autoclean;
707
708   BEGIN { extends 'Catalyst::Controller::REST' }
709
710   sub begin : Private {
711     my ($self, $c) = @_;
712     ... do things before Deserializing ...
713     $c->forward('deserialize');
714     ... do things after Deserializing ...
715   }
716
717   sub deserialize : ActionClass('Deserialize') {}
718
719   sub end :Private {
720     my ($self, $c) = @_;
721     ... do things before Serializing ...
722     $c->forward('serialize');
723     ... do things after Serializing ...
724   }
725
726   sub serialize : ActionClass('Serialize') {}
727
728 If you need to deserialize multipart requests (i.e. REST data in
729 one part and file uploads in others) you can do so by using the
730 L<Catalyst::Action::DeserializeMultiPart> action class.
731
732 =back
733
734 =head1 A MILD WARNING
735
736 I have code in production using L<Catalyst::Controller::REST>.  That said,
737 it is still under development, and it's possible that things may change
738 between releases.  I promise to not break things unnecessarily. :)
739
740 =head1 SEE ALSO
741
742 L<Catalyst::Action::REST>, L<Catalyst::Action::Serialize>,
743 L<Catalyst::Action::Deserialize>
744
745 For help with REST in general:
746
747 The HTTP 1.1 Spec is required reading. http://www.w3.org/Protocols/rfc2616/rfc2616.txt
748
749 Wikipedia! http://en.wikipedia.org/wiki/Representational_State_Transfer
750
751 The REST Wiki: http://rest.blueoxen.net/cgi-bin/wiki.pl?FrontPage
752
753 =head1 AUTHORS
754
755 See L<Catalyst::Action::REST> for authors.
756
757 =head1 LICENSE
758
759 You may distribute this code under the same terms as Perl itself.
760
761 =cut
762
763 __PACKAGE__->meta->make_immutable;
764
765 1;