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