When sending params to a rails controller like from a test or a view
{ "a[b]" => true }
I receive it as
{"a" => {"b" => true}}
I would like to know how this happens
This happened because rails middleware convert this type of parameter into hash so that it is more easy to access and use. You can have more in depth info in Rails guide.
Related
I'm working on a vanilla Rails 4.2 app for a demo of building a JSON API with Rails.
bundle exec rails g scaffold widget name
Out of the box, Rails provides an easy way to fetch a resource as JSON by simply adding .json to the end of the URL.
/widgets.json
# or...
/widgets/123.json
However, I was wondering what is the most simple way to allow Rails to respond as JSON by using other means than simply appending .json. Do I need to send an Accept header in the request? Or do I have to explicitly respond_to :json in order to add this support? I also need to continue to support HTML, but wanted a clean URL. What do I need to do?
For defining all the routes to json use the following code.
resources :you_resouce, :defaults => { :format => 'json' }
For specific route use the following code.
scope :format => true, :constraints => { :format => 'json' } do
get '/endpoint' => "controller#action_method"
end
If I convert a hash to a query string, how can I convert it back again?
{:filters => {:colour => ['Red', 'Blue'], :size => 'Medium'}}.to_param
=> "filters[colour][]=Red&filters[colour][]=Blue&filters[size]=Medium"
Rails appears to do this automatically when it populates the params hash, but is it possible to call this method directly?
Thanks.
You're looking for Rack::Utils.parse_nested_query(query), which will convert it back into a Hash. You can get it by using this line:
require 'rack/utils'
query_string = "filters[colour][]=Red&filters[colour][]=Blue&filters[size]=Medium"
CGI::parse(query_string)
How would I go about manually filtering a hash using my application's parameter filter?
I imagine it'd go like this:
Rails.application.filter :password => 'pass1234'
# => {:password => '[FILTERED]'}
EDIT (clarification): I'm aware that Rails filters the params hash when writing to the logs. What I want to do is apply that same filter to a different hash at my prerogative before writing it to the logs with something like Rails.logger.info. I'm calling a remote HTTP query as a part of my application (since most of the backend operates through a remote API), and I'm logging the URL and parameters passed. I want to have the logs but also ensure that none of the sensitive params show up there.
After a few minutes of shotgunning it, I figured out this was the way to do it:
filters = Rails.application.config.filter_parameters
f = ActionDispatch::Http::ParameterFilter.new filters
f.filter :password => 'haha' # => {:password=>"[FILTERED]"}
See the config/application.rb file, towards the end there is a line:
config.filter_parameters += [:password]
This way the "password" param will not be shown in logs, but you can still access the value normally.
Edit
It seem that have misunderstood your meaning of "filter" originally. As for the clarified issue, I have no idea on how to handle it the truly Rails way.
Here is a brute force approach:
Parse the query with CGI::parse(URI.parse(my_url_address_with_params).query) to get a hash of param/values (note: values are actually stored as an array; here is the discussion).
Locate the parameters you want to filter out and replace values with literal *filtered*.
Call Rails.logger.info (or debug) directly to log.
Here is what you should dig into when relying on Rails magical classes and methods:
In Rails 3 the code that does the trick seems to live in ActionDispatch::Http (ParameterFilter in particular, method `filtered_parameters'). The documentation is available at API Dock (or, to be honest, very little documentation). You can examine the sources to get an idea of how this works.
My knowledge of Rails internals is not good enough to suggest anything else. I believe that someone with a better understanding of it might be of more help.
Building on Steven Xu's answer above, I made this initializer in my rails app:
class ActionController::Parameters
def filtered
ActionDispatch::Http::ParameterFilter.new(Rails.application.config.filter_parameters).filter(self)
end
end
Which let's me call params.filtered
[1] pry(#<LessonsController>)> params.filtered
{
"controller" => "lessons",
"action" => "search",
"locale" => "en"
}
[2] pry(#<LessonsController>)> params[:password] = "bob"
"bob"
[3] pry(#<LessonsController>)> params.filtered
{
"controller" => "lessons",
"action" => "search",
"locale" => "en",
"password" => "[FILTERED]"
}
It seems that we will need 2 methods, one is CGI.escape and the other might be h (unless we hardcode &.
Is there a method that takes an array or hash of params, and compose it into this form?
src="foo.php?href=http%3A%2F%2Fexample.com%2F&layout=standard"
It is for Rails 2.2.2, so if there is a method in Rails 2.x that can do it?
I believe you can use Object#to_query to do it, but I'm not certain if it is available in Rails 2.x. For example:
{
:href => "http://example.com/",
:layout => "standard"
}.to_query # => "layout=standard&href=http%3A%2F%2Fexample.com%2F"
What is the simplest way to identify and separate GET and POST parameters from a controller in Ruby on Rails, which will be equivalent to $_GET and $_POST variables in PHP?
You can use the request.get? and request.post? methods to distinguish between HTTP Gets and Posts.
See http://api.rubyonrails.org/classes/ActionDispatch/Request.html
I don't know of any convenience methods in Rails for this, but you can access the querystring directly to parse out parameters that are set there. Something like the following:
request.query_string.split(/&/).inject({}) do |hash, setting|
key, val = setting.split(/=/)
hash[key.to_sym] = val
hash
end
You can do it using:
request.POST
and
request.GET
There are three very-lightly-documented hash accessors on the request object for this:
request.query_parameters - sent as part of the query string, i.e. after a ?
request.path_parameters - decoded from the URL via routing, i.e. controller, action, id
request.request_parameters - All params, including above as well as any sent as part of the POST body
You can use Hash#reject to get to the POST-only params as needed.
Source: http://guides.rubyonrails.org/v2.3.8/action_controller_overview.html section 9.1.1
I looked in an old Rails 1.2.6 app and these accessors existed back then as well.
There is a difference between GET and POST params. A POST HTTP request can still have GET params.
GET parameters are URL query parameters.
POST parameters are parameters in the body of the HTTP request.
you can access these separately from the request.GET and request.POST hashes.
request.get? will return boolean true if it is GET method,
request.post? will return boolean true if it is POST method,
If you want to check the type of request in order to prevent doing anything when the wrong method is used, be aware that you can also specify it in your routes.rb file:
map.connect '/posts/:post_id', :controller => 'posts', :action => 'update', :conditions => {:method => :post}
or
map.resources :posts, :conditions => {:method => :post}
Your PostsController's update method will now only be called when you effectively had a post. Check out the doc for resources.
I think what you want to do isn't very "Rails", if you know what I mean. Your GET requests should be idempotent - you should be able to issue the same GET request many times and get the same result each time.
You don't need to know that level of detail in the controller. Your routes and forms will cause appropriate items to be added to the params hash. Then in the controller you just access say params[:foo] to get the foo parameter and do whatever you need to with it.
The mapping between GET and POST (and PUT and DELETE) and controller actions is set up in config/routes.rb in most modern Rails code.
I think what Jesse Reiss is talking about is a situation where in your routes.rb file you have
post 'ctrllr/:a/:b' => 'ctrllr#an_action'
and you POST to "/ctrllr/foo/bar?a=not_foo" POST values {'a' => 'still_not_foo'}, you will have three different values of 'a': 'foo', 'not_foo', and 'still_not_foo'
'params' in the controller will have 'a' set to 'foo'. To find 'a' set to 'not_foo' and 'still_not_foo', you need to examine request.GET and request.POST
I wrote a gem which distinguishes between these different key=>value pairs at https://github.com/pdxrod/routesfordummies.
if request.query_parameters().to_a.empty?