Search code examples
ruby-on-railsjsonrubyruby-on-rails-5strong-parameters

Rails 5 API Strong Parameters behavior


I generated a new rails 5 --api --database=postgresql app the other day and only created one scaffold (Hero). I'm wondering how the strong parameters work in rails as I am seeing some odd behavior:

Controller looks like:

def create
  hero = Hero.new(hero_params)

  if hero.save
   render json: hero, status: :created, location: hero
  else
   render json: hero.errors, status: :unprocessable_entity
  end
end

My hero_params look like this:

def hero_params
  params.require(:hero).permit(:name)
end

So I would assume that the client is required to submit a hash containing a "hero" key and it's allowed to have a "name" subkey that is allowed to be mass assigned when this controller action is called.

Meaning, the JSON should look like this:

{
  "hero": {
    "name": "test"
  }
}

All is well but here is where I am seeing strange behavior. When the user submits the exact JSON as above, the parameters come in as:

Parameters: {"hero"=>{"name"=>"test"}}

Now if the user submits just:

{ "name": "test" }

It still creates a new resource and the parameters come in as:

Parameters: {"name"=>"test", "hero"=>{"name"=>"test"}}
  1. Why are there two sets of parameters, one with the actual submitted data and one in the format of a hero object as if it was anticipating the mass assignment?

  2. How come the require(:hero) doesn't raise an error when that key is not submitted? I assume the answer to this is because of what is automatically creating that second hash ("hero"=>{"name"=>"test"}} from question 1.

Any information on what I am missing here would be greatly appreciated, as this is barebones rails behavior out-of-the-box.


Solution

  • This behaviour comes from ActionController::ParamsWrapper:

    Wraps the parameters hash into a nested hash. This will allow clients to submit requests without having to specify any root elements.

    Rails applications have parameter wrapping activated by default for JSON requests. You can disable it globally by editing config/initializers/wrap_parameters.rb, or for individual controllers by including wrap_parameters false in the controller.