Catalyst-Action-REST
view release on metacpan or search on metacpan
lib/Catalyst/Action/REST.pm view on Meta::CPAN
sub dispatch {
my $self = shift;
my $c = shift;
my $rest_method = $self->name . "_" . uc( $c->request->method );
return $self->_dispatch_rest_method( $c, $rest_method );
}
sub _dispatch_rest_method {
my $self = shift;
my $c = shift;
my $rest_method = shift;
my $req = $c->request;
my $controller = $c->component( $self->class );
my ($code, $name);
# Execute normal 'foo' action.
$c->execute( $self->class, $self, @{ $req->args } );
# Common case, for foo_GET etc
if ( $code = $controller->action_for($rest_method) ) {
return $c->forward( $code, $req->args ); # Forward to foo_GET if it's an action
}
elsif ($code = $controller->can($rest_method)) {
$name = $rest_method; # Stash name and code to run 'foo_GET' like an action below.
}
# Generic handling for foo_*
if (!$code) {
my $code_action = {
OPTIONS => sub {
$name = $rest_method;
$code = sub { $self->_return_options($self->name, @_) };
},
HEAD => sub {
$rest_method =~ s{_HEAD$}{_GET}i;
$self->_dispatch_rest_method($c, $rest_method);
},
default => sub {
# Otherwise, not implemented.
$name = $self->name . "_not_implemented";
$code = $controller->can($name) # User method
# Generic not implemented
|| sub { $self->_return_not_implemented($self->name, @_) };
},
};
my ( $http_method, $action_name ) = ( $rest_method, $self->name );
$http_method =~ s{\Q$action_name\E\_}{};
my $respond = ($code_action->{$http_method}
|| $code_action->{'default'})->();
return $respond unless $name;
}
# localise stuff so we can dispatch the action 'as normal, but get
# different stats shown, and different code run.
# Also get the full path for the action, and make it look like a forward
local $self->{code} = $code;
my @name = split m{/}, $self->reverse;
$name[-1] = $name;
local $self->{reverse} = "-> " . join('/', @name);
$c->execute( $self->class, $self, @{ $req->args } );
}
sub get_allowed_methods {
my ( $self, $controller, $c, $name ) = @_;
my $class = ref($controller) ? ref($controller) : $controller;
my $methods = {
map { /^$name\_(.+)$/ ? ( $1 => 1 ) : () }
@{ Class::Inspector->methods($class) }
};
$methods->{'HEAD'} = 1 if $methods->{'GET'};
delete $methods->{'not_implemented'};
return sort keys %$methods;
};
sub _return_options {
my ( $self, $method_name, $controller, $c) = @_;
my @allowed = $self->get_allowed_methods($controller, $c, $method_name);
$c->response->content_type('text/plain');
$c->response->status(200);
$c->response->header( 'Allow' => \@allowed );
$c->response->body(q{});
}
sub _return_not_implemented {
my ( $self, $method_name, $controller, $c ) = @_;
my @allowed = $self->get_allowed_methods($controller, $c, $method_name);
$c->response->content_type('text/plain');
$c->response->status(405);
$c->response->header( 'Allow' => \@allowed );
$c->response->body( "Method "
. $c->request->method
. " not implemented for "
. $c->uri_for( $method_name ) );
}
__PACKAGE__->meta->make_immutable;
1;
=back
=head1 SEE ALSO
You likely want to look at L<Catalyst::Controller::REST>, which implements a
sensible set of defaults for a controller doing REST.
This class automatically adds the L<Catalyst::TraitFor::Request::REST> role to
your request class. If you're writing a web application which provides RESTful
responses and still needs to accommodate web browsers, you may prefer to use
L<Catalyst::TraitFor::Request::REST::ForBrowsers> instead.
L<Catalyst::Action::Serialize>, L<Catalyst::Action::Deserialize>
=head1 TROUBLESHOOTING
( run in 0.430 second using v1.01-cache-2.11-cpan-71847e10f99 )