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?
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.