SinatraでaタグからPUTリクエストを投げる

やりかた

app.rb を↓のように書き、

require 'sinatra'

class Rack::MethodOverride
  ALLOWED_METHODS=%w[POST GET]
  def method_override(env)
    req = Rack::Request.new(env)
    method = req.params[METHOD_OVERRIDE_PARAM_KEY] || env[HTTP_METHOD_OVERRIDE_HEADER]
    method.to_s.upcase
  end
end

enable :method_override

put '/hoge' do
  # do awesome things
end

htmlの方で↓のように a タグを書きます。

<a href='/hoge?_method=PUT&hoge=fuga'>PUTを投げる</a>

このリンクをクリックすると、hoge=fugaという内容のPUTリクエストが飛びます。

注意 :クローラがやってくるとPOST/PUT/DELETEリクエストが飛びまくります。クローラがやってくるWebアプリケーションでこれをやるのは絶対にやめたほうが良いでしょう。

解説

enable :method_override によってRack::MethodOverrideミドルウェアを有効にし、その挙動を変えています。

以下でこれを説明します。

Rack::MethodOverride とはなにか

Rack::MethodOverrideとは、 リクエストに含まれる _methodというパラメータを読み取って、HTTPのmethodを書き換えてくれるRack middlewareです。 例えば _method=PUT という値をリクエストに入れると、PUTリクエストとして処理されるわけです。

しかし、Rack::MethodOverrideが「method書き換え」をしてくれるのは、元々のmethodが POSTの場合だけ です。 従って、GETリクエストで_methodパラメータを指定してもmethod書き換えは起こりません。

また、 _method という値はPOSTパラメータに入っている必要があります。 このため、Query Stringに _method=PUT と書いてリクエストを投げてもPUTリクエストにならないというわけです。

Rack::MethodOverride の挙動を変更する

では、目的であるところの「 a タグがクリックされたときに POST/PUT/DELETE のリクエストを投げる」を実現するためには、 Rack::MethodOverrideの挙動をどのように変更すれば良いでしょうか。

これは簡単で、

  1. GETリクエストについても「method書き換え」をするようにする
  2. _method パラメータをQuery Stringからも読み取るようにする

の2点について変更すれば良いわけです。

前者に関しては、Rack::MethodOverride::ALLOWED_METHODS の値を %w[POST] から %w[POST GET] に変更してやれば良いです。

また、後者に関しては、Rack::MethodOverride#method_override をオーバーライドして、↓のように1 req.params2 から _method パラメータを取得するようにすれば良いです。

# before
method = req.POST[METHOD_OVERRIDE_PARAM_KEY] || env[HTTP_METHOD_OVERRIDE_HEADER]

# afrer
method = req.params[METHOD_OVERRIDE_PARAM_KEY] || env[HTTP_METHOD_OVERRIDE_HEADER]

参考

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版


  1. 実際のコードはエラー処理が入っており、もう少し複雑である(https://github.com/rack/rack/blob/bfd4c155a9ba2fb1fcee8daab433fbdef582cce2/lib/rack/method_override.rb#L27)

  2. req.params はQuery Stringのパラメータとrequest bodyのパラメータが合成されたもの