I ran into this very interesting problem which i am trying to resolve since last 3-4 hours, where i am not able to permit unknown nested array in json , its setting to empty array
params=ActionController::Parameters.new({"id"=>21, "tags"=>{"key"=>[["browser", "Chrome 28.0.1500"], ["browser.name", "Chrome"]]}, "nested_key"=>{"key_2"=>"value_2"}})
doing
params.permit(:id, tags: {}, nested_key: {})
is giving me nested array as empty("key"=>[]),
#<ActionController::Parameters {"id"=>21, "tags"=>#<ActionController::Parameters {"key"=>[]} permitted: true>, "nested_key"=>#<ActionController::Parameters {"key_2"=>"value_2"} permitted: true>} permitted: true>
I want all nested elements of array to be permitted since its part of the json.
Can anyone help me with this ?
Please note that nested keys are dynamic which i will not be aware of while permitting.
Related
Using javascript, I make a fetch post.
const game = {name: this.player, snake: this.snake, score: this.score, apple: this.apple, skull: this.skull, completed: this.completed}
return fetch("http://localhost:3000/games", {
method: "POST",
headers: {
"Content-Type": "application/json"},
body: JSON.stringify(game)
})
.then(resp => resp.json())
.then(json => Game.appendHighScores(json));
I access the data via params on the Ruby on Rails end. The data for params[:snake][:body] are supposed to look like "body"=>[{"x"=>16, "y"=>15}, {"x"=>16, "y"=>14}, {"x"=>16, "y"=>15}]}, yet when I type them into the command line, they look like this:
[<ActionController::Parameters {"x"=>16, "y"=>15} permitted: false>, <ActionController::Parameters {"x"=>16, "y"=>14} permitted: false>, <ActionController::Parameters {"x"=>16, "y"=>15} permitted: false>]
It is accessible via indexing, but I get everything along with the data I'm looking for.
I was hoping it would look like the original params when I typed it in
<ActionController::Parameters {"name"=>"Don", "snake"=>{"x"=>16, "y"=>15, "direction"=>"down", "speed"=>0, "directions"=>{"left"=>{"x"=>-1, "y"=>0}, "up"=>{"x"=>0, "y"=>-1}, "right"=>{"x"=>1, "y"=>0}, "down"=>{"x"=>0, "y"=>1}}, "image"=>{}, "body"=>[{"x"=>16, "y"=>15}, {"x"=>16, "y"=>14}, {"x"=>16, "y"=>15}]}, "score"=>0, "apple"=>{"image"=>{}, "x"=>2, "y"=>10}, "skull"=>{"image"=>{}, "x"=>12, "y"=>12}, "completed"=>true, "controller"=>"games", "action"=>"create", "game"=>{"score"=>0, "skull"=>{"image"=>{}, "x"=>12, "y"=>12}, "apple"=>{"image"=>{}, "x"=>2, "y"=>10}, "completed"=>true}} permitted: false>
Anyway to get the params as an array without it looking so messy with ActionController::Parameters inside of the element?
The reason everything is wrapped inside ActionController::Parameters is for your security (mass assignment in particular). You should never trust data send from the internet. This class allows you to permit/whitelist what properties you trust and filter out everything that you don't trust.
snake_params = params.require(:snake).permit(body: [:x, :y])
You can then convert this into a hash with a simple to_h call, which will drill down into all other nested parameters that are also permitted.
snake_data = snake_params.to_h
#=> { "body" => [{"x"=>16, "y"=>15}, {"x"=>16, "y"=>14}, {"x"=>16, "y"=>15}] }
If you'd like to include other attributes as well you can add them to the permit list.
.permit(:direction, :speed, directions: {left: [:x, :y], up: [:x, :y], ...}, body: [:x, :y])
For more info about permit I suggest checking out the guide Action Controller Overview - 4.5 Strong Parameters.
If you don't care about permitting certain parameters you can permit everything with permit!.
Note that you don't have to permit parameters if you extract the values directly. The code below would work perfectly fine without permitting anything.
body = params[:snake][:body]
body.each |body_part|
x = body_part[:x]
y = body_part[:y]
// do stuff with x and y
end
Because Rails utilize "strong parameters" you have to list and allow the parameters in the controller you want to use.
Since you have a list of parameters with dynamic keys the easiest way to do, while you are in development mode is to permit them like this:
# in your controller
def game_params
params.require(:game).tap do |permitted|
permitted[:name] = params[:name].permit!
permitted[:snake] = params[:snake].permit!
permitted[:apple] = params[:apple].permit!
permitted[:skull] = params[:skull].permit!
permitted[:completed] = params[:completed].permit!
end
end
and then you will be able to access your params:
game_params[:snake][:body]
In production tho, I will encourage you to whitelist all of the keys, for example like this.
I am using nested parameters in my controller. the params are submitted through react forms. a form for the original object and a bunch of nested forms. the problem is that my params object has each object wrapped in an ActionController::Parameters object.
I expect only the main object to be wrapped in ActionController::Parameters and not every single nested object.
Debugging: the params object in the control looks something like this:
ActionController::Parameters {
"_method"=>"post",
"authenticity_token"=>"kano3zIa5vs6JM6s/4fqqOQO61kq75IHlJglHGhlSOoI/yypu2nfV2cnS4VTKx0ENypFqE58wjB9Czq+T4Rbsw==",
"campaign"=>
<ActionController::Parameters {
"name"=>"12",
"advertiser_id"=>"1", "revenue"=>"sd",
"revenue_currency"=>"EUR",
"start_date"=>"2020-05-27",
"end_date"=>"2020-05-27",
"status"=>"done",
"media_budgets_attributes"=>
<ActionController::Parameters {"0"=>
<ActionController::Parameters {"id"=>"",
"_destroy"=>"false",
"title"=>"asdf",
"cost"=>"12",
"cost_currency"=>"EUR",
"affiliate_group_id"=>"1"} permitted: true>} permitted: true>} permitted: true>,
"controller"=>"campaigns",
"action"=>"create"} permitted: true>
Notice that every object is wrapped with ActionController::Parameters. This makes it painful to permit params. I expect the media_budgets_attributes to be a plain array in campaign object. but media_budgets_attributes is wrapped with ActionController::Parameters, which in its turn has a ActionController::Parameters objects in it.
Can anybody help me with this?
How do you assign a value to a key from a parameter into a ActionController::Parameters object?
What I tried:
(byebug) foo_inquiry_params
<ActionController::Parameters {"email"=>"fds#fsdgf.com", "subject"=>"fds", "description"=>"fds"} permitted: true>
(byebug) name_inquiry_params["user_id"] = 5
5
(byebug) foo_inquiry_params
<ActionController::Parameters {"email"=>"fds#fsdgf.com", "subject"=>"fds", "description"=>"fds"} permitted: true>
(byebug)
ActionController::Parameters can't be treated like a normal hash, so if you want to call .merge on it, you'll have to convert your params to a hash first:
ActionController::Parameters.new({foo: 'bar'}).to_h.merge({bar: 'baz'})
A workflow that I find myself using when I have to do this kind of thing is:
def update
attrs_to_update = thing_params.to_h.merge(one_more_attribute: 'some value')
#thing.update(attrs_to_update)
end
def thing_params
params.require(:thing).permit(:some_attribute, :another_attribute)
end
I would like to get any additional GET and POST params in Rails without Rails' own additions such as controller, format, etc...
params.inspect gave me what I want but it has some keys that Rails adds for me such as controller or format. I only want to get user input GET and POST params as hash. How can I do that? I cannot find anything.
URL:
http:/test.com/some/path?query1=1&query2=1
Run:
puts params.inspect
Expected:
{"query1"=>"1", "query2"=>"1"}
Actual:
{"query1"=>"1", "query2"=>"1", "format"=>":json", "controller"=>"get", "action"=>"index", "folder"=>"some/path"}
Also this can be combined with POST request. I only want to filter them and only get them as hash.
I execute this from inside of a controller. Rails 5 used.
You should have permitted params (Strong parameters).
In your controller have permitted params method.
def your_params
params.permit(:query1, :query2)
end
If you wish to have a hash of those you can do
your_params.to_h #This will return hash of permitted params
Update:
Incase you have multiple query* type of parameters you can select them and permit!. Here is a command line explanation
# passing the list of parameters
> params = ActionController::Parameters.new({query1: 'aa', query2: 'bb', query3: 'cc', controller: 'users', action: 'index'})
=> <ActionController::Parameters {"query1"=>"aa", "query2"=>"bb", "query3"=>"cc", "controller"=>"users", "action"=>"index"} permitted: false>
# Select all the parameters of type query<integer>
> all_queries = params.select {|i| i.match?(/query\d/) }
=> <ActionController::Parameters {"query1"=>"aa", "query2"=>"bb", "query3"=>"cc"} permitted: false>
# mark the selected as permitted
> all_queries.permit!
=> <ActionController::Parameters {"query1"=>"aa", "query2"=>"bb", "query3"=>"cc"} permitted: true>
# get them as hash
> all_queries.to_h
=> {"query1"=>"aa", "query2"=>"bb", "query3"=>"cc"}
Controller method will look like
# getting all query<integer> like params
def your_params
params.select {|param| param.match?(/query\d/}.permit!
end
In the controller, you have access to a request object. request has a method query_parameters, which will return a hash of just the explicitly supplied parameters.
`request.query_parameters`
http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-query_parameters
I put in a binding.pry at the top of my controller's update action. Once at that break point, I put in params[:foo_bar] to examine the params hash. Here is what I get:
<ActionController::Parameters {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"123==", "foobar"=><ActionController::Parameters {"barbazz_attributes"=>{"start_date"=>"08/27/2016", "end_date"=>"08/29/2016", "id"=>"89"}, "bazz_id"=>"3", "abc_id"=>"330", "bazzbazz_attributes"=>{"0"=>{"_destroy"=>"1", "city_id"=>"1669", "id"=>"26"}, "1"=>{"city_id"=>"1681", "id"=>"27"}, "2"=>{"city_id"=>"1672"}}} permitted: false>, "cat_id"=>["1", "1", "1"], "commit"=>"Update FooBar", "controller"=>"foo_bars", "action"=>"update", "id"=>"52"} permitted: false>
I assumed permitted: false is there because I did not whitelist some attributes. I looked over the attributes and it appears to me that I did whitelist everything.
I am using Rails 5 if that happens to make any difference.
Question: What is an easy way to find out why the strong parameters are returning params: false.
Don't access params directly with params instead use the name you gave your permitted params, for example: foobar_params.
If foobar_params is defined:
def foobar_params
params.require(:foobar).permit ...
end
The easiest way is to read the source code for ActionController::Parameter, permitted = false is the default unless you call permit! to allow all, but that defeats the purpose of strong parameters.