Mark Burns
Mark Burns

Reputation: 158

No params being posted to Rails controller

I have a form in an app with some nested fields. I'm using simple_form_for, bootstrap, slim and Rails 4.2 and a form backing object as described here

Here's the proof params are missing on the server side (N.B. I have checked the authenticity_token field is in the markup)

This is the log

Started POST "/example_sentences/9/breakdowns" for ::1 at 2015-08-31 12:37:57 +0900
  ActiveRecord::SchemaMigration Load (18.8ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by BreakdownsController#create as HTML
  Parameters: {"example_sentence_id"=>"9"}
Can't verify CSRF token authenticity

Switching off CSRF protection to try and debug gives this:

Started POST "/example_sentences/9/breakdowns" for ::1 at 2015-08-31 13:00:05 +0900
Processing by BreakdownsController#create as HTML
  Parameters: {"example_sentence_id"=>"9"}
  User Load (24.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 1]]
Completed 400 Bad Request in 355ms (ActiveRecord: 156.3ms)

ActionController::ParameterMissing - param is missing or the value is empty: breakdown:

Obviously, there's quite a number of factors that mean this is deviating from the Rails way, so any number of things could be going wrong. And showing the view code would introduce a lot more questions.

So, I'll show the generated HTML (minus styling info) and ask if anyone can see anything clearly wrong with this. I'm kind of hoping it will be a facepalm moment and missing something obvious.

<form novalidate="novalidate" id="new_breakdown" action="/example_sentences/9/breakdowns" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="✓">
  <input type="hidden" name="authenticity_token" value="cOU87EZXRkV1a0QA1ohwlqM5Ny43QvgcRaqKan9mdgs12jR8RnJfyVXp9VIJHkMRkBUvZ16Io7QaGcBazjVqGw==">
  <input type="text" value="これ" name="breakdown[word_mapping][1][text]" id="breakdown_word_mapping_1_text" >
  <input type="text" value="this" name="breakdown[word_mapping][1][translation]" id="breakdown_word_mapping_1_translation">
  <input type="text" value="は" name="breakdown[word_mapping][2][text]" id="breakdown_word_mapping_2_text">
  <input type="text" value="" name="breakdown[word_mapping][2][translation]" id="breakdown_word_mapping_2_translation">
  <input type="submit" name="commit" value="Save translations">
</form>

Additional info

In ~/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/request_forgery_protection.rb

during the :valid_authenticity_token? check line 277

For request.body.read I can see the authenticity token "utf8=%E2%9C%93&authenticity_token=RTFWCJZ3QnRgnNWFF66ej2aetx7H5n7BcbLKW4v145YADl6YllJb%2BEAeZNfIOK0IVbKvV64sJWkuAYBrOqb%2Fhg%3D%3D&breakdown%5Bword_mapping%5D%5B1%5D%5Btext%5D=%E3%81%93%E3%82%8C&breakdown%5Bword_mapping%5D%5B1%5D%5Btranslation%5D=this&breakdown%5Bword_mapping%5D%5B1%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B1%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B2%5D%5Btext%5D=%E3%81%AF&breakdown%5Bword_mapping%5D%5B2%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B2%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B2%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B3%5D%5Btext%5D=%E3%83%9A%E3%83%B3&breakdown%5Bword_mapping%5D%5B3%5D%5Btranslation%5D=pen&breakdown%5Bword_mapping%5D%5B3%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B3%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B4%5D%5Btext%5D=%E3%81%A7%E3%81%99&breakdown%5Bword_mapping%5D%5B4%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B4%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B4%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B%5D%5Btext%5D=%E3%81%93%E3%82%8C&breakdown%5Bword_mapping%5D%5B%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B%5D%5Btext%5D=%E3%81%AF&breakdown%5Bword_mapping%5D%5B%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B%5D%5Btext%5D=%E3%83%9A%E3%83%B3&breakdown%5Bword_mapping%5D%5B%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B%5D%5Btext%5D=%E3%81%A7%E3%81%99&breakdown%5Bword_mapping%5D%5B%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B%5D%5B_destroy%5D=0&commit=Save+translations"

But the value of encoded_masked_token is nil

In fact the path_parameters is the only part of the params I'm getting in the request.

in: "~/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/http/request.rb"

(byebug) pp @env.keys.sort.grep(/action_dispatch/).grep(/param/).map{|k| [k, @env[k]]}

[["action_dispatch.parameter_filter", [:password]],
 ["action_dispatch.request.parameters",
  {"controller"=>"breakdowns",
   "action"=>"create",
   "example_sentence_id"=>"9"}],
 ["action_dispatch.request.path_parameters",
  {:controller=>"breakdowns", :action=>"create", :example_sentence_id=>"9"}],
 ["action_dispatch.request.query_parameters", {}],
 ["action_dispatch.request.request_parameters", {}]]

So I'm somehow losing the params already.

Upvotes: 0

Views: 768

Answers (2)

Mark Burns
Mark Burns

Reputation: 158

OK so the problem was with the format of the name param. I didn't catch where exactly in the Rails stack all the params including the authenticity_token/CSRF token get lost but basically:

Instead of this: breakdown[word_mapping][0][translation]

This is correct: breakdown[word_mappings_attributes][0][translation]

It's possibly worth adding extra info in case anyone else had a similar issue.

I was using a nested form with a form-backing object as described in the pivotal blog post linked in the OP.

My slim code looked like this, based on an out-of-date recommendation elsewhere:

= f.simple_fields_for "word_mapping[]", @breakdown.word_mappings do |w|
  = render "word_mapping_fields", f: w

which was resulting in the output HTML shown in the post.

The correct version is:

= f.simple_fields_for "word_mappings", @breakdown.word_mappings do |w|
  = render "word_mapping_fields", f: w

On my form-backing object I had to delegate, word_mappings_attributes= to the AR object ExampleSentence in this case. Although using delegate itself didn't work so I had to manually write the code.

def word_mappings_attributes=(attrs)
  example_sentence.word_mappings_attributes=attrs
end

I also had to add the following to the form:

class << self
  delegate :reflect_on_association, to: ExampleSentence
end

Upvotes: 0

sevenseacat
sevenseacat

Reputation: 25049

From the error message, it sounds like your code to require your parameters is incorrect.

If you have something like the following:

params.require(breakdown: [fields])

Then it should be

params.require(:breakdown).permit(fields)

Give it a try.

Upvotes: 2

Related Questions