Sinatra(というかRack)で複数の同名パラメータを受け取る

はじめに

Sinatra

  http://localhost:4567/?foo=123&foo=456&foo=789

って感じで複数の同名パラメータを送ったら

require 'sinatra'
get '/' do
  params[:foo] #=> 789
end

のように、最後の1つしか取ってくれませんでしたf**k!
そんなわけで、全部取り出すにはどうすればいいのか調べてみました。

環境

結論から言うと

そもそもSinatra(というよりRack)では、複数の同名パラメータを受け取る場合は

  http://localhost:4567/?foo[]=123&foo[]=456&foo[]=789

のように、キーの後ろに [] をつけるのが正解のようです。
こうすると、

require 'sinatra'
get '/' do
  params[:foo] #=> ["123", "456", "789"]
end

のように、値を配列で取り出すことができます。このあたりはたぶんRailsでも一緒です。

そんなのヤダヤダ、という場合は?

  http://localhost:4567/?foo=123&foo=456&foo=789

という形式でパラメータが渡された場合でも同様の動作にするには、

Rack::Utils.parse_query(qs, d = nil)

でパラメータの文字列(上記でいうと foo=123&foo=567&foo=789)をバラしてやればOKです。


パラメータの文字列は、

  • GETの場合:
@env['rack.request.query_string']
  • POSTの場合:
@env['rack.request.form_vars']

に格納されています。
というわけで、

require 'sinatra'
# http://localhost:4567/?foo=123&foo=456&foo=789 でアクセス

get '/' do
  # GETのときは @env['rack.request.query_string']
  hoge = Rack::Utils.parse_query(@env['rack.request.query_string'])
  hoge['foo'] #=> ["123", "456", "789"]
end

post '/' do
  # POSTのときは @env['rack.request.form_vars']
  hoge = Rack::Utils.parse_query(@env['rack.request.form_vars'])
  hoge['foo'] #=> ["123", "456", "789"]
end

のようにすれば、キーの後ろに [] が無い場合でも複数の同名パラメータを扱えます。
ただし、この場合は

  http://localhost:4567/?foo[]=123&foo[]=456&foo[]=789

のように指定されると、キー名が 'foo[]' と解釈されます。

require 'sinatra'
# http://localhost:4567/?foo[]=123&foo[]=456&foo[]=789 でアクセス

get '/' do
  hoge = Rack::Utils.parse_query(@env['rack.request.query_string'])
  hoge['foo'] #=> nil
  hoge['foo[]'] #=> ["123", "456", "789"]
end

まとめ

  • Sinatra(というかRack)で複数の同名パラメータを使うときは ?foo[]=1&foo[]=2 みたいにキーの後ろに [] をつける
  • Rack::Utils.parse_queryでパラメータの文字列を自前でバラせば ?foo=1&foo=2 みたいなのも扱える