8

The docs on parameter wrapping state:

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

It helpfully elides which parameters hash is being wrapped. The Action Controller overview guide gives this run-down:

Rails collects all of the parameters sent along with the request in the params hash, whether they are sent as part of the query string or the post body. […] The query_parameters hash contains parameters that were sent as part of the query string while the request_parameters hash contains parameters sent as part of the post body. The path_parameters hash contains parameters that were recognized by the routing as being part of the path leading to this particular controller and action.

The fun happens when you use RESTful resources and routes. Say you have a model A which has_many Bs; B thus has a foreign key a_id.

You POST /as/1/bs with an empty payload (because B has no other fields). Assuming a_id is attr_accessible, one might assume that a_id would be wrapped in a b object. Instead you'll see:

Processing by BsController#create as HTML
  Parameters: {"b"=>{}, "a_id" => "1"}

No such luck. It turns out that ParamsWrapper uses request_parameters and not params, so not including a_id in the POST payload means it doesn't get wrapped. This is pretty confusing, because you still see it included in params, due to URI globbing, and wonder why it got excluded of all things.

Is there any good reason to use request_parameters and not params here?

I can understand that, from a "REST philosophy" point of view, it's more pure if we assume that the payload contains the entire object, but that essentially means that the a_id in the URI is completely ignored, which seems a pity.

tl;dr: ParamsWrapper uses request_parameters as the parameter source, so URI-globbed variables are skipped. Is this a Rails bug? Pure REST advocates may say no, but pragmatism suggests yes.

Asherah
  • 18,948
  • 5
  • 53
  • 72

2 Answers2

0

As far as I understand, the reason a_id is not included in the hash for 'b' is we need that id value to first check if the record exists in our database. This way, we can simply reject other params in the request. As per the reason for not including it in 'b' hash: It can prevent accidents. Take this scenario: Suppose someone is updating a form and passes complete 'b' hash as an argument to a model object. Now when we call model_object.save, it may save the record in our database instead of updating older record which will be a security threat (Can happen if the object has been initialized earlier). Not a full proof scenario, but accidents do happen while coding and it can help us prevent such accidents.

udit mittal
  • 529
  • 3
  • 14
0

Depends on your specific use case, but if you're using strong params in your controller, you can do either

params[:b][:a_id] = params[:a_id]
params.require(:b).permit(:a_id)

or just skip the "require" method entirely:

params.permit(:a_id)
NateQ
  • 781
  • 9
  • 13