Suppose you have a url
localhost:3000?a=1
and in the request, you also have a post parameter
a=2
What wou
Rails uses Rack::Request
for HTTP requests. However, it redefines the Rack
params method in ActionDispatch::Http::Parameters
(via an alias).
This params method, which returns request parameters, is implemented as:
# 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
Note the aliased parameters
method.
Unless redefined, parameters from the query string will overwrite parameters from the POST body.
No it doesn't depend on de HTTP verb but you sure can have different actions to handle GET
and POST
or it can be the same and you'll get `params[:a] in both cases.
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
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
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.
It would be 2. But for routing, it would match against the '1'.
If you want, you can use request.GET and request.POST to access the get and post vars.