Paul
Paul

Reputation: 26640

Rails: get multipart/form-data POST request parameters with same name

A web site which protocol I can't influence sends me POST requests where are multiple parameters with same name "t".

Here is my request.raw_post:

--===13978193024621189109088990673===
Content-Disposition: form-data; name="t"
Content-Type: text/plain; charset=UTF-8

thevalue1
--===13978193024621189109088990673===
Content-Disposition: form-data; name="t"
Content-Type: text/plain; charset=UTF-8

thevalue2
--===13978193024621189109088990673===
Content-Disposition: form-data; name="t"
Content-Type: text/plain; charset=UTF-8

thevalue3
--===13978193024621189109088990673===
Content-Disposition: form-data; name="t"
Content-Type: text/plain; charset=UTF-8

thevalue4
--===13978193024621189109088990673===
Content-Disposition: form-data; name="t"
Content-Type: text/plain; charset=UTF-8

thevalue5
--===13978193024621189109088990673===
Content-Disposition: form-data; name="t"
Content-Type: text/plain; charset=UTF-8

thevalue6

Standard params collection lists only the last parameter.

How to get all the values?

A way to reproduce this request is:

<form action="/updateinfo" method="post" enctype="multipart/form-data">
  <input type="text" name="t" value="thevalue1"><br>
  <input type="text" name="t" value="thevalue2"><br>
  <input type="text" name="t" value="thevalue3"><br>
  <input type="text" name="t" value="thevalue4"><br>
  <input type="text" name="t" value="thevalue5"><br>
  <input type="text" name="t" value="thevalue6"><br>
  <input type="submit">
</form>

Upvotes: 3

Views: 8330

Answers (2)

Paul
Paul

Reputation: 26640

Finally I found and used multipart-parser gem. It lacks documentation, so here is the code I used:

require 'multipart_parser/reader'

module MultiParams
  def parse_multi_params(request)
    parts={}

    reader = MultipartParser::Reader.new(MultipartParser::Reader::extract_boundary_value(request.headers['CONTENT_TYPE']))

    reader.on_part do |part|
      pn = part.name.to_sym
      part.on_data do |partial_data|
        if parts[pn].nil?
          parts[pn] = partial_data
        else
          parts[pn] = [parts[pn]] unless parts[pn].kind_of?(Array)
          parts[pn] << partial_data
        end
      end
    end

    reader.write request.raw_post
    reader.ended? or raise Exception, 'truncated multipart message'

    parts
  end
end

Usage in a controller:

require 'multi_params'

class MyController < ApplicationController
  include MultiParams

  def my_method
    mp = parse_multi_params(request)
    process_params(mp[:t])
  end
end

Upvotes: 5

Iuri G.
Iuri G.

Reputation: 10630

Please take a look how rack parses incoming parameters and converts into hash. You can take raw_request and put it through your own params parser that would be very similar to racks implementation, with the addition to convert parameter t directly to an array. something like this:

def normalize_params(params, name, v = nil)
  name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
  k = $1 || ''
  after = $' || ''

  return if k.empty?

  if after == "" && k == 't'
    params[k] ||= []
    params[k] << v
  elsif == ""
    params[k] = v
  elsif after == "[]"
    params[k] ||= []
    params[k] << v
  elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
    child_key = $1
    params[k] ||= []
    if params_hash_type?(params[k].last)
      && !params[k].last.key?(child_key)
      normalize_params(params[k].last, child_key, v)
    else
      params[k] << normalize_params(params.class.new, child_key, v)
    end
  else
    params[k] ||= params.class.new
    params[k] = normalize_params(params[k], after, v)
  end

  return params
end 

Upvotes: 0

Related Questions