Rails 5. Strong params require not used if one permit key is set

16,566

Solution 1

The strong parameter API was designed with the most common use cases in mind. It is not meant as a silver bullet to handle all your whitelisting problems.

http://guides.rubyonrails.org/action_controller_overview.html#strong-parameters

require(key)

Ensures that a parameter is present. If it's present, returns the parameter at the given key, otherwise raises an ActionController::ParameterMissing error.

http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-require

As you can see from the above setting required parameters on a "flat" hash is not really what the strong parameters API is built for. Rather its built around the rails conventions where params are nested under a single key.

You could use ´.require´ to pull a single key at once but that would be rather clumsy.

Rather you can simulate what it does by raising an exception unless the key is present:

def something_params
  req = [:required_attribute1, :required_attribute2]
  req.each do |k|
    raise ActionController::ParameterMissing.new(k) unless params[k].present?
  end
  whitelisted = params.permit(:permit_attribute1, :permit_attribute2)
end

However a better method altogether may be to use model level validations - ActionController::ParameterMissing is supposed to indicate that the general formatting of the request is off - not that a required attribute is missing. For example for a JSONAPI.org formatted request you would do:

params.require(:data).require(:attributes).permit(:email, :username)

Which ensures that the request follows the standard. However enforcing that a User cannot be created without an email is a model level concern.

Solution 2

Yes, by default Rails in API mode wraps JSON request parameters into a hash guessing its name from controller class. You can read details here.

If you don't need this functionality in your app just remove :json from :format array in config\initializers\wrap_parameters.rb. Or you can use fine-grained control at the controller level as described above.

This way you'll get an exception when the required key isn't present.

Share:
16,566
Kramougna
Author by

Kramougna

Updated on June 21, 2022

Comments

  • Kramougna
    Kramougna almost 2 years

    I am using Rails 5 to create a JSON Api.

    My controller uses strong parameters with one require attribute like this:

    params.require(:require_attribute).permit(:permit_attribute1,:permit_attribute2)
    

    Normally I have to send my JSON like this:

    {
        "require_attribute":{
            "permit_attribute1": "data",
            "permit_attribute2": "data"
        }
    }
    

    And if the required attribute is missing,I have to get this message:

    "ActionController::ParameterMissing: param is missing or the value is empty: require_attribute"
    

    My problem is, if I remove the required attribute from JSON and I have one permit attribute in common with strong params it does work.

    The JSON I send :

    {
        "permit_attribute1": "data",
    }
    

    When I get params in log I have:

    {"permit1"=>data, "controller"=>"mycontroller", "action"=>"create", "require_attribute"=>{"permit1"=>1}}

    It seems Rails creates a hash with required key instead of raising an error. But I want to force the required attribute when I receive a JSON.

    Anyone have an idea?