Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: query parameter vs post parameter

Suppose you have a url

localhost:3000?a=1

and in the request, you also have a post parameter

a=2

What would

params[:a] be in this case? does it depend on the HTTP verb?

If it does depend on the HTTP verb,

what if your form looks like

<form method='post' action='/?a=2'>
  <input type='hidden' name='a' value='3'/>
</form>

what would params[:a] be in this case?

UPDATE

So I just did a small experiment and used Chrome Debugger to append ?authenticity_token=abc the action url. I looked at the server log and I saw that parameters has authenticity_token => 'abc'. I also believe that the method is POST in this case.

Let me know what you guys came up with.

like image 641
denniss Avatar asked Dec 14 '11 19:12

denniss


1 Answers

When I tried this in a sample code, what I was able to see was that query parameters(GET) are given precedence than the POST body. So, I went digging into the code of Rack which handles the HTTP requests in Rails. Here is the code from request.rb

# Returns the data recieved in the query string.
def GET
  ....
end

# Returns the data recieved in the request body.
#
# This method support both application/x-www-form-urlencoded and
# multipart/form-data.
def POST
  ....
end

# The union of GET and POST data.
def params
  @params ||= self.GET.merge(self.POST)
rescue EOFError
  self.GET
end

Here, the method

  • GET - returns the query parameters in a hash format
  • POST - returns the post body in a hash format

So, according to the code for params, the GET parameters should get overridden by POST parameters in case of identical keys. (self.GET.merge(self.POST)). But, this is contrary to what I got when I tried it practically.

So, the only chance is that this code is being overridden by Rails. When I thought about it, it made perfect sense as the params hash from the Rails will always contain "controller" and "action" keys, which will be absent in case of Rack. So, I looked at the code of Rails also, and found that params method was indeed being overridden. Take a look at request.rb and parameters.rb in Rails source code. In parameters.rb, we have:

  # Returns both GET and POST \parameters in a single hash.
  def parameters
    @env["action_dispatch.request.parameters"] ||= begin
      params = request_parameters.merge(query_parameters)
      params.merge!(path_parameters)
      encode_params(params).with_indifferent_access
    end
  end
  alias :params :parameters

and in request.rb:

# Override Rack's GET method to support indifferent access
def GET
  @env["action_dispatch.request.query_parameters"] ||= (normalize_parameters(super) || {})
end
alias :query_parameters :GET

# Override Rack's POST method to support indifferent access
def POST
  @env["action_dispatch.request.request_parameters"] ||= (normalize_parameters(super) || {})
end
alias :request_parameters :POST

So, here

  • query_parameters - Alias for the GET method
  • request_parameters - Alias for the POST method
  • path_parameters - Method which returns the controller and action for the request as a hash
  • parameters - Alias for params (It was overridden here)

Note that GET method and POST method were also overridden, mainly to convert the hash returned to an object of HashWithIndifferentAccess.

So, looking at the code here (params = request_parameters.merge(query_parameters)), it becomes evident that POST parameters are overridden by GET parameters in case of identical keys, in Rails. Or in other words, GET parameters are given precedence over POST parameters.

like image 60
rubyprince Avatar answered Oct 29 '22 15:10

rubyprince