How can prevent Rack request parser for post request to filter out params with empty array value?
my params consist of something like {'imp_key' => [], 'notified' => 'true'}. However rack parser removed imp_key and I get only notified as params but I want to get all params I have send to it.
I am running into the same issue and can confirm it is a problem. I've traced it down to lib/rack/test/utils.rb:19 in build_nested_query. Any parameters with values of empty arrays are getting stripped there
EDIT: This appears to be an open issue with rack-test. See the Github issue
Related
Overview
I want to implement the Google OAuth2 server side (hybrid) with the Omniauth Google OAuth2 Gem (0.2.6). However, the code parameter I send to my app does not get added to the request.params hash. Thus, OmniAuth throws an error, as it can't find the code.
Details
After retrieving the auth code from Google I send it to the server (by AJAX):
// Send the code to the server
$.ajax({
type: 'POST',
url: '/auth/google_oauth2/callback',
contentType: 'application/json',
success: function(result) {
// Handle or verify the server response.
},
processData: false,
data: JSON.stringify(password_result)
});
This throws the error:
"error" : "invalid_request",
"error_description" : "Missing required parameter: code"
After going through the stack, I figured out the following:
As long as I have 'application/json' set as content Type, Rack parses the params correctly and the env object contains the parsed parameters:
"action_dispatch.request.request_parameters"=>{"code"=>"<sent_in_code>"}
However, the request.params hash remains empty. Since OmniAuth checks for request.params['code'], this is the source of the error.
request.POST is empty, which from looking at the source code of Rack is the underlying cause for the empty request.params hash.
When sending the code in standard format as data:"code="+authResult['code'], the parameter is available in the request.params hash. (I get a strange undefined route error then, but this is a different issue.)
Questions
Now, even though I can avoid the issue by not using JSON, I'm still very intereted in the answers to the following questions:
Why is the code parameter not available in request.POST/request.params, even though it gets parsed correctly?
Is there a way to fix this, so I can still send the auth code in JSON to my app?
I've spent two afternoons trying to get the answers myself, but haven't really gotten to a good conclusion so far.
omniauth-google-oauth2 tries to get the auth code from the Rack::Request.params hash. However, Rack apparently does not have JSON parsing built-in. Params calls POST which then calls form_data? which only looks for application/x-www-form-urlencoded or multipart/form-data. It then also tries parseable_data?, which does not parse JSON either. Seems like Rack does not support JSON out of the box, also look at this answer.
The "action_dispatch.request.request_parameters"=>{"code"=>"<sent_in_code>"} hash works because this is done by rails ActionDispatch::Request, which subclasses Rack::Request. However because The omniauth gem is included in your app as a Rack middleware, it does not know of the ActionDispatch request object.
The question remains why this example uses JSON.
Possible solutions:
money-Patch JSON Support into Rack - not recommended
just use application/x-www-form-urlencoded - recommended
This is the way Rails accepts arrays in query parameters:
PUT /resource.json?sport_ids[]=133&sport_ids[]=64&sport_ids[]=71 ...
I tried to google this question but didn't find any explicit docs on it:
How to tell Rails that we want sport_ids to become empty (pass empty array of sport_ids via query parameters) ?
HTTP requests can have only variables on the url itself. That's a limitation feature of HTTP, not Rails.
Take a look at How Does Rack Parse Query Params? With Parse_nested_query to figure out how rails collects the variables into an array, it won't run out of the box in case of an empty array.
You can avoiding sending the params["sport_ids"] and patch your controller with:
params["sport_ids"] ||= []
The best practice to use put/post requests, is passing such data in the request body (json/xml) like:
{
"sport_ids": []
}
Or with data as:
//...
{
"sport_ids": [133, 64, 71]
}
//...
For more info about HTTP request steps, check Running a HTTP request with rails.
While #mohameddiaa27's answer has good advice on how to achieve that by passing such data in the request body as JSON I found that I cannot rely on it within my application: I found that it is not easy to combine such passing of JSON into request body within multipart forms where I want to pass user record (with user[sport_ids] in it) and user's avatar image (user[avatar]) field.
So I continued to investigate how to achieve that using default "query parameters in a request body of POST/PUT request" approach and found the reason why I was not able to reset my sport_ids on server-side: it was the lack of permission for that specific sport_ids field. Now I have the following permits (pseudocode):
current_user.update!(user_params)
where user_params is
user_attributes_to_permit = [
...
:sport_ids, # this is what was needed for just `user[sport_ids]` to work.
{ :sport_ids => [] } # this is needed for sport_ids non-empty arrays to work
...
]
params.require(:user).permit(user_attributes_to_permit)
so now I am able to reset the sport_ids array of my user by passing just user[sport_ids] (without '=' and value! i.e. ...&user[sport_ids]&...) within my query parameters.
I am using Rack middle ware to intercept all requests to my rails app. I am checking for a particular parameter in the query string. If the request has the parameter, I need to extract its value and put it in a database to be processed later. Otherwise I let the normal course of action to continue. Any ideas how to implement it?
P.S : I have done the following already.
1) I can extract the value of my desired parameter through Rack and send it along with the response (For testing purposes only)
2) I have tried created a custom controller and call it from the rack, but I am hitting upon undefined method error everytime.
Any help will be appreciated.
I need to pass params through javascript back to the server. At the moment, I pass them into javascript like so:
sendParams("<%= params[:q].to_json %>");
And then send them back like this:
function sendParams(q){
$.ajax({
url: '/mymodel/myaction',
type: 'post',
data: {'q':q},
contentType: 'json'
});
}
In my controller, I try to use them like I would any other params:
MyModel.where(params[:q])
But the params are coming back empty, even though firebug shows this in the POST tab:
q=%7B%26quot%3Bc%26quot%3B%3A%7B%26quot%3B0%26quot%3B%3A%7B%26quot%3Ba%26quot%3B%3A%7B%26quot%3B0%26quot%3B%3A%7B%26quot%3Bname%26quot%3B%3A%26quot%3Btitle%26quot%3B%7D%7D%2C%26quot%3Bp%26quot%3B%3A%26quot%3Bcont%26quot%3B%2C%26quot%3Bv%26quot%3B%3A%7B%26quot%3B0%26quot%3B%3A%7B%26quot%3Bvalue%26quot%3B%3A%26quot%3B2%26quot%3B%7D%7D%7D%7D%2C%26quot%3Bs%26quot%3B%3A%7B%26quot%3B0%26quot%3B%3A%7B%26quot%3Bname%26quot%3B%3A%26quot%3Bvotes_popularity%26quot%3B%2C%26quot%3Bdir%26quot%3B%3A%26quot%3Bdesc%26quot%3B%7D%7D%7D
Any idea why this information isn't getting processed by the where clause? What can I do to make the params Rails readable again?
UPDATE:
Started POST "/publications/search?scroll=active&page=6" for 127.0.0.1 at 2013-0
2-12 22:55:24 -0600
Processing by PublicationsController#index as */*
Parameters: {"scroll"=>"active", "page"=>"6"}
UPDATE 2:
The problem is apparently stemming from contentType. When I remove it, then q is sent as a Rails parameter. Unfortunately, q is still in JSON, resulting in the error:
undefined method `with_indifferent_access' for #<String:0x686d0a8>
How can I convert JSON to a params hash?
Your data parameter is wrong.
You have
data: {'q':q},
It should be
data: {q: 'q'},
There were a couple of issues that needed to be resolved for this to work. First, q wasn't being sent as a parameter to Rails, even though it was posting. The reason was because it was being treated as JSON data rather than as a parameter. I fixed this by removing the line:
contentType: 'json'
After that, the AJAX properly sent 'q', but Rails had trouble using it as it was in JSON. I had to parse it with ActiveSupport::JSON.decode, but this was throwing a 737: unexpected token error. I ran the code through (JSONlint)[http://jsonlint.com/], and it turns out that all the quotation marks had been escaped.
From there, there were two solutions. The obvious one was to use .html_safe like so:
sendParams("<%= params[:q].to_json.html_safe %>");
But this caused problems when the user inputed quotes. The safer alternative was to decode the escaped HTML entities after they were passed back to Rails like so:
ActiveSupport::JSON.decode(CGI.unescapeHTML(params[:q]))
And this did the trick.
I made a rails 3 rack middleware to log users actions with request = Rack::Request.new(env).
So i send to my database the request.fullpath and request.user_agent, as detailed below:
My issue appears I want to get the POST response too (to get ids, people name extracted from the JSON payload ...).
So i get the response = Rack::Response.new(request.path). But when i print response.body, i have only my request.path, and the request.params does not contain anything ...
By looking at the response with Firebug, I can see all the data I want.
Thanks for your responses.
Problem resolved !
I finally add status, headers, body = #app.call(env) to my middleware and send the body variable to my service. For each POST request, body contains all the post response I want.