Request with Content-Type: "application/json" add extra parameter

Howdy,

I am making a few AJAX endpoints using rails, and noticed the following
inconsistent behavior when setting the Content-Type to application/json.
Here are four different requests to show the problem I am having.

  1. First, when I make the following example request:

curl -X GET
http://localhost:3000/api/v1/events?counts=true&order[column]=occurs_at&order[direction]=asc&page[number]=1&page[limit]=100

Rails shows the following in the log:

Started GET
“/api/v1/events?counts=true&order%5Bcolumn%5D=occurs_at&order%5Bdirection%5D=asc&page%5Bnumber%5D=1&page%5Blimit%5D=100”
for 127.0.0.1 at 2013-04-18 03:57:49 +0000
Processing by Api::V1::EventsController#index as JSON
Parameters: {“counts”=>“true”, “order”=>{“column”=>“occurs_at”,
“direction”=>“asc”}, “page”=>{“number”=>“1”, “limit”=>“100”}}

  1. However, if I change the Content-Type to be “application/json”:

curl -X GET -H “Content-Type: application/json”
http://localhost:3000/api/v1/events?counts=true&order[column]=occurs_at&order[direction]=asc&page[number]=1&page[limit]=100

I get the following:

Started GET
“/api/v1/events?counts=true&order%5Bcolumn%5D=occurs_at&order%5Bdirection%5D=asc&page%5Bnumber%5D=1&page%5Blimit%5D=100”
for 127.0.0.1 at 2013-04-18 03:57:33 +0000
Processing by Api::V1::EventsController#index as JSON
Parameters: {“counts”=>“true”, “order”=>{“column”=>“occurs_at”,
“direction”=>“asc”}, “page”=>{“number”=>“1”, “limit”=>“100”},
“event”=>{}}

The bold part is the extra parameter (in this case “event”) that gets
added
by making this a JSON request.

  1. The problem, is that this event parameter will clobber any parameter
    I
    send over also called event. For example:

curl -X GET -H “Content-Type: application/json”
http://localhost:3000/api/v1/events?counts=true&order[column]=occurs_at&order[direction]=asc&page[number]=1&page[limit]=100&
event=Super%20Important%20Info

And it is not parsed from the params:

Started GET
“/api/v1/events?counts=true&order%5Bcolumn%5D=occurs_at&order%5Bdirection%5D=asc&page%5Bnumber%5D=1&page%5Blimit%5D=100&
event=Super%20Important%20Info” for 127.0.0.1 at 2013-04-18 04:02:56
+0000
Processing by Api::V1::EventsController#index as JSON
Parameters: {“counts”=>“true”, “order”=>{“column”=>“occurs_at”,
“direction”=>“asc”}, “page”=>{“number”=>“1”, “limit”=>“100”},
“event”=>{}}

  1. But, if I dont pass the JSON content type:

curl -X GET
http://localhost:3000/api/v1/events?counts=true&order[column]=occurs_at&order[direction]=asc&page[number]=1&page[limit]=100&
event=Super%20Important%20Info

It works just fine:

Started GET
“/api/v1/events?counts=true&order%5Bcolumn%5D=occurs_at&order%5Bdirection%5D=asc&page%5Bnumber%5D=1&page%5Blimit%5D=100&
event=Super%20Important%20Info” for 127.0.0.1 at 2013-04-18 04:02:22
+0000
Processing by Api::V1::EventsController#index as JSON
Parameters: {“counts”=>“true”, “order”=>{“column”=>“occurs_at”,
“direction”=>“asc”}, “page”=>{“number”=>“1”, “limit”=>“100”},
“event”=>“Super
Important Info”
}

Is there any way I can just turn off this functionality? I think it
might
be because this is an EventsController, and Rails is trying to give me
something for free, but I just want to send over whatever I want,
especially as I will have situations like an OrdersController, that I
want
to pass an order to (such as id asc, etc…).

Thanks for any help,

Robert

On Thursday, April 18, 2013 5:10:14 AM UTC+1, [email protected] wrote:

Howdy,

Is there any way I can just turn off this functionality? I think it might be
because this is an EventsController, and Rails is trying to give me something for
free, but I just want to send over whatever I want, especially as I will have
situations like an OrdersController, that I want to pass an order to (such as id
asc, etc…).

There are 2 things happening. First, by default if the content type is
json then rails assumes that the request body (in your case the empty
string) is json, and will parse it and add it to params.

The second is something called wrap_parameters.
This wraps the parameters from the body in a hash, so if you posted the
document

{“name”: “bob”}

To a users controller, instead of polluting the top level parameter
namespace it would set params[:user] to the result of parsing that.

There is an initializer that turns on wrap parameters. You could remove
it and/or only enable it for some controllers.

By default this only happens if the content type is json.

Lastly why are you setting the content type if you’re not submitting the
request body (if you’re trying to control the format of the response I
find that it’s easiest not to mess around with headers and requests
events.json instead)

Fred

Is there meaning of adding a check of method type (GET or POST/PUT) to
ParamsWrapper#_wrapper_enabled?

For example:

def _wrapper_enabled?
return false if request.get?
ref = request.content_mime_type.try(:ref)
_wrapper_formats.include?(ref) && _wrapper_key &&
!request.request_parameters[_wrapper_key]
end

Hi Frederick,

Thanks for the help, disabling wrap_parameters in my initializer fixed
my
problem. Reason why I am setting the content type for the GET is just
because I have jQuery to always ask for JSON. I could probably set it
to
only send the header on POST/PUT requests, but I would still have the
problem were params would get clobbered in my post body anyway.

Really appreciate the help!

On Thursday, 18 April 2013 15:19:09 UTC-4, [email protected] wrote:

Hi Frederick,

Thanks for the help, disabling wrap_parameters in my initializer fixed my
problem. Reason why I am setting the content type for the GET is just
because I have jQuery to always ask for JSON. I could probably set it to
only send the header on POST/PUT requests, but I would still have the
problem were params would get clobbered in my post body anyway.

Content-Type specifies what format the request is in (the data you are
sending). For GET requests, it’s not particularly meaningful - there
isn’t
a “request body”, so there’s nothing to specify the format of.

If you want to specify what format you’d like the response in, use the
Accept header.

–Matt J.