Merge branch 'master' of git://github.com/bobtfish/catalyst-action-rest
[catagits/Catalyst-Action-REST.git] / lib / Catalyst / Action / REST.pm
CommitLineData
256c894f 1#
2# REST.pm
be3c588a 3# Created by: Adam Jacob, Marchex, <adam@hjksolutions.com>
256c894f 4# Created on: 10/12/2006 03:00:32 PM PDT
5#
6# $Id$
7
8package Catalyst::Action::REST;
9
10use strict;
11use warnings;
12
13use base 'Catalyst::Action';
7ad87df9 14use Class::Inspector;
9a76221e 15use Catalyst::Request::REST;
ebba5325 16use Catalyst::Controller::REST;
17
9c5c9bd1 18BEGIN { require 5.008001; }
256c894f 19
db8bb647 20our $VERSION = '0.74';
9a76221e 21
5132f5e4 22sub new {
23 my $class = shift;
24 my $config = shift;
37694e6c 25 Catalyst::Request::REST->_insert_self_into( $config->{class} );
e34b463b 26 return $class->next::method($config, @_);
5132f5e4 27}
7328f0ab 28
29=head1 NAME
30
31Catalyst::Action::REST - Automated REST Method Dispatching
32
33=head1 SYNOPSIS
34
5e87ec47 35 sub foo :Local :ActionClass('REST') {
36 ... do setup for HTTP method specific handlers ...
37 }
7328f0ab 38
44d48631 39 sub foo_GET {
7328f0ab 40 ... do something for GET requests ...
41 }
42
44d48631 43 sub foo_PUT {
7328f0ab 44 ... do somethign for PUT requests ...
45 }
46
47=head1 DESCRIPTION
48
49This Action handles doing automatic method dispatching for REST requests. It
50takes a normal Catalyst action, and changes the dispatch to append an
44d48631 51underscore and method name.
7328f0ab 52
53For example, in the synopsis above, calling GET on "/foo" would result in
54the foo_GET method being dispatched.
55
44d48631 56If a method is requested that is not implemented, this action will
57return a status 405 (Method Not Found). It will populate the "Allow" header
5e87ec47 58with the list of implemented request methods. You can override this behavior
59by implementing a custom 405 handler like so:
60
61 sub foo_not_implemented {
62 ... handle not implemented methods ...
63 }
64
65If you do not provide an _OPTIONS subroutine, we will automatically respond
66with a 200 OK. The "Allow" header will be populated with the list of
67implemented request methods.
7328f0ab 68
5e87ec47 69It is likely that you really want to look at L<Catalyst::Controller::REST>,
70which brings this class together with automatic Serialization of requests
71and responses.
398c5a1b 72
9a76221e 73When you use this module, the request class will be changed to
74L<Catalyst::Request::REST>.
75
7328f0ab 76=head1 METHODS
77
78=over 4
79
80=item dispatch
81
82This method overrides the default dispatch mechanism to the re-dispatching
83mechanism described above.
84
85=cut
d34c067a 86
256c894f 87sub dispatch {
bb4130f6 88 my $self = shift;
d34c067a 89 my $c = shift;
256c894f 90
2f91bf68 91 my $controller = $c->component( $self->class );
3faede66 92 my $rest_method = $self->name . "_" . uc( $c->request->method );
679978b1 93
3faede66 94 my ($code, $name);
679978b1 95
3faede66 96 # Common case, for foo_GET etc
5a173b14 97 if ( my $action = $controller->action_for($rest_method) ) {
44d48631 98 return $c->forward( $action, $c->req->args );
5a173b14 99 } elsif ($code = $controller->can($rest_method)) {
3faede66 100 # Exceute normal action
d34c067a 101 $c->execute( $self->class, $self, @{ $c->req->args } );
3faede66 102 $name = $rest_method;
256c894f 103 }
256c894f 104
3faede66 105 # Generic handling for foo_OPTIONS
106 if (!$code && $c->request->method eq "OPTIONS") {
107 $name = $rest_method;
108 $code = sub { $self->_return_options($self->name, @_) };
109 }
d34c067a 110
3faede66 111 # Otherwise, not implemented.
112 if (!$code) {
113 $name = $self->name . "_not_implemented";
114 $code = $controller->can($name) # User method
115 # Generic not implemented
116 || sub { $self->_return_not_implemented($self->name, @_) };
679978b1 117 }
256c894f 118
3faede66 119 # localise stuff so we can dispatch the action 'as normal, but get
120 # different stats shown, and different code run.
121 local $self->{code} = $code;
122 local $self->{reverse} = $name;
d34c067a 123
679978b1 124 $c->execute( $self->class, $self, @{ $c->req->args } );
d34c067a 125}
126
127sub _get_allowed_methods {
d2d93101 128 my ( $self, $controller, $c, $name ) = @_;
679978b1 129 my $class = ref($controller) ? ref($controller) : $controller;
130 my $methods = Class::Inspector->methods($class);
7ad87df9 131 my @allowed;
eccb2137 132 foreach my $method ( @{$methods} ) {
eccb2137 133 if ( $method =~ /^$name\_(.+)$/ ) {
134 push( @allowed, $1 );
7ad87df9 135 }
136 }
d34c067a 137 return @allowed;
679978b1 138};
139
140sub _return_options {
3faede66 141 my ( $self, $method_name, $controller, $c) = @_;
d2d93101 142 my @allowed = $self->_get_allowed_methods($controller, $c, $method_name);
679978b1 143 $c->response->content_type('text/plain');
144 $c->response->status(200);
145 $c->response->header( 'Allow' => \@allowed );
d34c067a 146}
147
148sub _return_not_implemented {
3faede66 149 my ( $self, $method_name, $controller, $c ) = @_;
d34c067a 150
d2d93101 151 my @allowed = $self->_get_allowed_methods($controller, $c, $method_name);
7ad87df9 152 $c->response->content_type('text/plain');
153 $c->response->status(405);
eccb2137 154 $c->response->header( 'Allow' => \@allowed );
155 $c->response->body( "Method "
156 . $c->request->method
157 . " not implemented for "
679978b1 158 . $c->uri_for( $method_name ) );
7ad87df9 159}
160
256c894f 1611;
7328f0ab 162
163=back
164
165=head1 SEE ALSO
166
167You likely want to look at L<Catalyst::Controller::REST>, which implements
168a sensible set of defaults for a controller doing REST.
169
170L<Catalyst::Action::Serialize>, L<Catalyst::Action::Deserialize>
171
5d7480da 172=head1 TROUBLESHOOTING
173
174=over 4
175
176=item Q: I'm getting a "415 Unsupported Media Type" error. What gives?!
177
5a173b14 178<<<<<<< HEAD:lib/Catalyst/Action/REST.pm
44d48631 179A: Most likely, you haven't set Content-type equal to "application/json", or one of the
5d7480da 180accepted return formats. You can do this by setting it in your query string thusly:
44d48631 181?content-type=application%2Fjson (where %2F == / uri escaped).
6df7d0ae 182
183**NOTE** Apache will refuse %2F unless configured otherise.
184Make sure AllowEncodedSlashes On is in your httpd.conf file in order for this to run smoothly.
5d7480da 185
186=cut
187
188=cut
189
5a173b14 190=======
69ad525b 191A: Most likely, you haven't set Content-type equal to "application/json", or
192one of the accepted return formats. You can do this by setting it in your query
193accepted return formats. You can do this by setting it in your query string
194thusly: C<< ?content-type=application%2Fjson (where %2F == / uri escaped). >>
5a173b14 195>>>>>>> f04ed654a172628f642bdefe8483c1e6becf9ad1:lib/Catalyst/Action/REST.pm
5d7480da 196
69ad525b 197B<NOTE> Apache will refuse %2F unless configured otherise.
198Make sure C<< AllowEncodedSlashes On >> is in your httpd.conf file in orde
199for this to run smoothly.
5d7480da 200
69ad525b 201=back
7328f0ab 202
2f7533ed 203=head1 CONTRIBUTORS
398c5a1b 204
2f7533ed 205Christopher Laco
206
207Luke Saunders
208
209John Goulah
33e5de96 210
211Daisuke Maki <daisuke@endeworks.jp>
212
97b3cf7c 213J. Shirley <jshirley@gmail.com>
214
7580fa2b 215Hans Dieter Pearcey
216
97b3cf7c 217Tomas Doran (t0m) <bobtfish@bobtfish.net>
218
2f7533ed 219=head1 AUTHOR
220
221Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and jrockway
222
97b3cf7c 223Marchex, Inc. paid me while I developed this module. (L<http://www.marchex.com>)
2f7533ed 224
7328f0ab 225=head1 LICENSE
226
227You may distribute this code under the same terms as Perl itself.
228
229=cut
d34c067a 230