use 5.008001;
use strict;
use warnings;
use Plack::Request ();
package Plack::Middleware::MethodOverride;
$Plack::Middleware::MethodOverride::VERSION = '0.20';
# ABSTRACT: Override REST methods to Plack apps via POST
use parent 'Plack::Middleware';
use Plack::Util::Accessor 'param';
my %allowed_method = map { $_ => undef } qw(GET HEAD PUT DELETE OPTIONS TRACE CONNECT PATCH);
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{param} = 'x-tunneled-method' unless exists $self->{param};
$self->{header} = 'X-HTTP-Method-Override' unless exists $self->{header};
$self->header($self->{header}); # munge it
return $self;
}
sub call {
my ($self, $env) = @_;
my $meth = $env->{'plack.original_request_method'} = $env->{REQUEST_METHOD};
if ($meth and uc $meth eq 'POST') {
no warnings 'uninitialized';
my $override = uc (
$env->{$self->header}
or $env->{QUERY_STRING} && Plack::Request->new($env)->query_parameters->{$self->param}
);
$env->{REQUEST_METHOD} = $override if exists $allowed_method{$override};
}
$self->app->($env);
}
sub header {
my $self = shift;
return $self->{header} if not @_;
return $self->{header} = '' if not $_[0];
(my $key = 'HTTP_'.$_[0]) =~ tr/-a-z/_A-Z/;
return $self->{header} = $key;
}
1;
__END__
=pod
=encoding UTF-8
=head1 Name
Plack::Middleware::MethodOverride - Override REST methods to Plack apps via POST
=head1 Version
version 0.20
=head1 Synopsis
In your Plack app:
use Plack::Builder;
builder {
enable MethodOverride;
$app;
};
PUT via a query parameter in your POST forms:
Or override it via the C header in a request:
my $req = HTTP::Request->new(POST => '/foo', [
'X-HTTP-Method-Override' => 'PUT'
]);
=head1 Description
Writing
Lful apps
is a good thing, but if you're also trying to support web browsers, it would
be nice not to be reduced to C and C for everything.
This middleware allows for C requests that pretend to be something else:
by adding either a header named C to the request, or
a query parameter named C to the URI, the client can say
what method it actually meant. That is, as long as it meant one of these:
=over
=item * GET
=item * POST
=item * HEAD
=item * PUT
=item * DELETE
=item * OPTIONS
=item * TRACE
=item * CONNECT
=item * PATCH
=back
If so, then the C in the PSGI environment will be replaced
with the client's desired value. The original request method is always stored
under the C key.
=head1 Configuration
These are the named arguments you can pass to C. Or, more likely, on the
C line in your C block, as in
enable 'MethodOverride', header => 'X-HTTP-Method', param => 'my_method';
=head2 C
Specifies the HTTP header name which specifies the overriding HTTP method.
Defaults to C, as used by Google for its APIs.
=head2 C
Specifies the query parameter name to specify the overriding HTTP method.
Defaults to C.
=head1 Acknowledgements
This module gleefully steals from
L by Dave Rolsky and the
original version by Tatsuhiko Miyagawa (which in turn stole from
L). Thanks to L for the shove in this direction, to L for suggesting that it be implemented as
middleware, and to L for
convincing me not to parse body parameters.
=head1 Authors
=over 4
=item *
Tatsuhiko Miyagawa
=item *
David E. Wheeler
=item *
Aristotle Pagaltzis
=back
=head1 Copyright and License
This software is copyright (c) 2015 by Tatsuhiko Miyagawa, David E. Wheeler, Aristotle Pagaltzis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut