rails: Nested params are parsed incorrectly (unexpectedly) in controllers

Hi all, I ran into some inconsistency with controller parsing nested params.

Steps to reproduce

post '/', params: {
  "items" => [
    { "brand" => { "name" => "Apple" }, "id" =>1, "color" => "pink" },
    { "brand" => { "name" => "Samsung" }, "id" => 2, "color" => "gold" },
  ]
}

# what appears in controller
{"items"=>[{"brand"=>{"name"=>"Samsung"}, "id"=>"1", "color"=>"pink"}, {"id"=>"2", "color"=>"gold"}]}

https://gist.github.com/simsalabim/6785d3c85bdd20f2e7c5

System configuration

Rails version: 5.0.0.beta3 (current master) Ruby version: 2.3.0

Oddly enough, the gist fails on earlier versions of Rails too (4-2-stable, 4-1-stable), while our production app works fine on 4.2.5.

Could anyone please tell me what I’m missing and where is the best place to start looking for params being processed in a controller action? Thanks.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 26
  • Comments: 50 (27 by maintainers)

Commits related to this issue

Most upvoted comments

@tak1n I checked your test file https://github.com/tak1n/nested_attributes_reproduction/blob/master/test/integration/book_test.rb but I believe there is an issue because this definitely does work for me (same case) in Rails 5 integration tests but you need to specify as: :json there, the format option is for controller tests. Change the code to what you see below and see if it passes:

  test "something" do
    params = {
      book: {
        title: 'Cool',
        pages_params: [
          { id: @page.id, content: 'another content' },
          { id: @delete_page.id, _destroy: 1 },
          { content: 'another new page' }
        ]
      }
    }

    put book_url(@book.id), params: params, as: :json
  end

Note that the as: :json is a separate hash from params entirely. Has to be separate based on docs. Also while the URL does have .json at the end I don’t believe that is sufficient as when I first started doing this I looked at what happens under the hood and the test seems to really do certain things based on the as: option being set. Give it a try and let me know if that works.

Okay, after doing quite a bit of digging, I’ve learned that this issue is related to the fact that the the ActiveSupport Hash#to_query extension sorts the query parameters before joining them. When I try removing the sort! the results show up on the server as expected. It looks like Rack::Utils.parse_nested_query handles things differently depending on the order of the params. I came across this other issue which looks like the same thing but was closed a couple of years ago.

I think we can close this because it was fixed here: https://github.com/rails/rails/pull/33093

If not, lets reopen or make a new ticket. Thanks!

I’m getting something similar in 4.2.5:

This:

{
  thing: [
    { stuff: [1, 2] },
    { stuff: [3, 4] }
  ]
}

Ends up like this:

{
  thing: [
    { stuff: [1, 2, 3, 4] },
    { }
  ]
}

Is there a working version of rack I could use?

@tenderlove Is there a way to address this issue for Rails 4? I tried the as solution and it seems like my endpoint doesn’t detect the params and the headers at all.

post path, params: params, headers: valid_header, as: :json

Using rails 4.2.11.1 with rack 1.6.12

Is there an update on the status of this? It’s still reproducible under Rails 5.1.5 and breaks even if the tests use format: :json

Update: I missed @javierjulio’s comment above, but switching from format: :json to as: :json fixes the problem.

Also referenced in https://github.com/rspec/rspec-rails/issues/1700

Hi all! I’ve stumbled upon a similar problem - not sure if I have to report it as a separate issue.

In short: when submitting multipart form data with hash that has an element with blank value (i.e. blank text field of a form), it doesn’t appear in params (the key is missing). When submitting the same form not as a multipart form data, the field presents in the hash (which is a correct behavior). Attached is a test script demonstrating both scenarios.

Looks like this is Rack issue and it will be fixed soon. Going to keep this open in the meantime.