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