I'm an idiot
[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
7656dd12 97 if ( $code = $controller->action_for($rest_method) ) {
98 $c->execute( $self->class, $self, @{ $c->req->args } );
99 return $c->forward( $code, $c->req->args );
5a173b14 100 } elsif ($code = $controller->can($rest_method)) {
3faede66 101 # Exceute normal action
d34c067a 102 $c->execute( $self->class, $self, @{ $c->req->args } );
3faede66 103 $name = $rest_method;
256c894f 104 }
256c894f 105
3faede66 106 # Generic handling for foo_OPTIONS
107 if (!$code && $c->request->method eq "OPTIONS") {
108 $name = $rest_method;
109 $code = sub { $self->_return_options($self->name, @_) };
110 }
d34c067a 111
3faede66 112 # Otherwise, not implemented.
113 if (!$code) {
114 $name = $self->name . "_not_implemented";
115 $code = $controller->can($name) # User method
116 # Generic not implemented
117 || sub { $self->_return_not_implemented($self->name, @_) };
679978b1 118 }
256c894f 119
3faede66 120 # localise stuff so we can dispatch the action 'as normal, but get
121 # different stats shown, and different code run.
122 local $self->{code} = $code;
123 local $self->{reverse} = $name;
d34c067a 124
679978b1 125 $c->execute( $self->class, $self, @{ $c->req->args } );
d34c067a 126}
127
128sub _get_allowed_methods {
d2d93101 129 my ( $self, $controller, $c, $name ) = @_;
679978b1 130 my $class = ref($controller) ? ref($controller) : $controller;
131 my $methods = Class::Inspector->methods($class);
7ad87df9 132 my @allowed;
eccb2137 133 foreach my $method ( @{$methods} ) {
eccb2137 134 if ( $method =~ /^$name\_(.+)$/ ) {
135 push( @allowed, $1 );
7ad87df9 136 }
137 }
d34c067a 138 return @allowed;
679978b1 139};
140
141sub _return_options {
3faede66 142 my ( $self, $method_name, $controller, $c) = @_;
d2d93101 143 my @allowed = $self->_get_allowed_methods($controller, $c, $method_name);
679978b1 144 $c->response->content_type('text/plain');
145 $c->response->status(200);
146 $c->response->header( 'Allow' => \@allowed );
d34c067a 147}
148
149sub _return_not_implemented {
3faede66 150 my ( $self, $method_name, $controller, $c ) = @_;
d34c067a 151
d2d93101 152 my @allowed = $self->_get_allowed_methods($controller, $c, $method_name);
7ad87df9 153 $c->response->content_type('text/plain');
154 $c->response->status(405);
eccb2137 155 $c->response->header( 'Allow' => \@allowed );
156 $c->response->body( "Method "
157 . $c->request->method
158 . " not implemented for "
679978b1 159 . $c->uri_for( $method_name ) );
7ad87df9 160}
161
256c894f 1621;
7328f0ab 163
164=back
165
166=head1 SEE ALSO
167
168You likely want to look at L<Catalyst::Controller::REST>, which implements
169a sensible set of defaults for a controller doing REST.
170
171L<Catalyst::Action::Serialize>, L<Catalyst::Action::Deserialize>
172
5d7480da 173=head1 TROUBLESHOOTING
174
175=over 4
176
177=item Q: I'm getting a "415 Unsupported Media Type" error. What gives?!
178
69ad525b 179A: Most likely, you haven't set Content-type equal to "application/json", or
180one of the accepted return formats. You can do this by setting it in your query
181accepted return formats. You can do this by setting it in your query string
182thusly: C<< ?content-type=application%2Fjson (where %2F == / uri escaped). >>
5d7480da 183
69ad525b 184B<NOTE> Apache will refuse %2F unless configured otherise.
185Make sure C<< AllowEncodedSlashes On >> is in your httpd.conf file in orde
186for this to run smoothly.
5d7480da 187
69ad525b 188=back
7328f0ab 189
2f7533ed 190=head1 CONTRIBUTORS
398c5a1b 191
2f7533ed 192Christopher Laco
193
194Luke Saunders
195
196John Goulah
33e5de96 197
198Daisuke Maki <daisuke@endeworks.jp>
199
97b3cf7c 200J. Shirley <jshirley@gmail.com>
201
7580fa2b 202Hans Dieter Pearcey
203
97b3cf7c 204Tomas Doran (t0m) <bobtfish@bobtfish.net>
205
2f7533ed 206=head1 AUTHOR
207
208Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and jrockway
209
97b3cf7c 210Marchex, Inc. paid me while I developed this module. (L<http://www.marchex.com>)
2f7533ed 211
7328f0ab 212=head1 LICENSE
213
214You may distribute this code under the same terms as Perl itself.
215
216=cut
d34c067a 217