Additional pod cleanups
[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
5cb5f6bb 194=head1 AUTHOR
195
196Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and jrockway
197
198Marchex, Inc. paid me while I developed this module. (L<http://www.marchex.com>)
199
2f7533ed 200=head1 CONTRIBUTORS
398c5a1b 201
4ee24376 202Arthur Axel "fREW" Schmidt <frioux@gmail.com>
203
2f7533ed 204Christopher Laco
205
206Luke Saunders
207
208John Goulah
33e5de96 209
210Daisuke Maki <daisuke@endeworks.jp>
211
97b3cf7c 212J. Shirley <jshirley@gmail.com>
213
7580fa2b 214Hans Dieter Pearcey
215
97b3cf7c 216Tomas Doran (t0m) <bobtfish@bobtfish.net>
217
5cb5f6bb 218=head1 COPYRIGHT
2f7533ed 219
5cb5f6bb 220Copyright the above named AUTHOR and CONTRIBUTORS
2f7533ed 221
7328f0ab 222=head1 LICENSE
223
224You may distribute this code under the same terms as Perl itself.
225
226=cut
d34c067a 227