end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

HTTP の REST APIで用いられる PUT や DELETE の METHOD を POST に変換

昔?のWEBアプリは、GET や POST のHTTP METHOD のみで動作できるものが多いと思います。

また、OPTIONS や TRACE の METHODは、セキュリティ的に非推奨であることから WAFやApacheにて、許可するHTTP METHOD を HEAD GET POST のみに 制限しているケースもあると思います。

REST APIWEBサービスでは、GET や POST に加え、PUT や DELETE を使いますが、 過去からの経緯が影響し、PUT や DELETE を利用できないケースもあると思います。

このようなケースは、世の中的に FAQ らしく、 X-HTTP-Method-Override や x-tunneled-method で METHOD を上書きするようです。

Step1 - client側で PUT->POSTに変更

例えば、 vue.js + axios で、X-HTTP-Method-Override による METHOD 変換を行う場合、次のように書けそうです。

ce.a.defaults.headers["X-Requested-With"] = "XMLHttpRequest";

ce.a.put = function(url, data, config) {        ## here
    config["method"] =  'post';                           ## here
    config["url"] =     url;                              ## here
    config["data"] =    data;                             ## here
    config["headers"] = {'X-HTTP-Method-Override':'PUT'}; ## here
    return this.request(config);                          ## here
};

Step2 - server側で POST->PUTに逆変換

ApacheのProxy機能で実現できるかもしれませんが、 Plack/PSGI for perlPlack::Middleware::MethodOverride - Override REST methods to Plack apps via POST - metacpan.org では、 次のように実装されています。

use Plack::Request ();

package Plack::Middleware::MethodOverride;
$Plack::Middleware::MethodOverride::VERSION = '0.20';

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;

他 - Movable Type の Data API の 例

Movable Type の Data API のように Request パラメータに「__method=PUT」に付与することで POST で 代替できるものもあります。

Movable Type Data API v4