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