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