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