I am trying to determine whether my controller action was called with parameters or not, without hardcoding which parameters can be added on.
So I want to distinguish between
/my_controller
and
/my_controller?q=1
I know that I could look inside the params hash, and check whether it ONLY contains :controller and :action keys. This seems ugly to me, is there a smarter way of doing this check?
There is one direct solution:
request.env["QUERY_STRING"] # => "q=1"
Or with Ruby 1.9.2:
request.env.QUERY_STRING # => "q=1"
For GET request you can use request.query_parameters method. There is also request.request_parameters for POST requests.
Results for request.query_parameters.inspect are:
for '/my_controller' => '{}'
for '/my_controller?q=1' => {"q"=>"1"}
Related
I am having troubles with an RSPec test. The test does a PUT with some objects in the request. The controller which receives the PUT seems to be not getting the correct values
For example, 'put :update, :id => #channel.id, :channel => #channel, :tags => #tag' Then, in the Controller, when I try to use params[:tags] there is an integer in that location. A Gist with the Spec and the controller method is at https://gist.github.com/3715021
This started happening when I upgraded from Rails 3.0.13 to 3.1.8
Any idea what might be happening here and how to resolve it?
I'm assuming that #tag is an object from your Tags model. When you give Rails an object like
`get :action, :foo => foo`
or in a url helper (e.g., foo_path(foo)),
Rails will turn your object into a parameter suitable for use in a url via the #to_param method. You're probably getting an integer because Tag#to_param returns the id of the tag in your db.
It looks like your update action, by contrast, expects params[:tags] to be a hash, presumably generated from a form that includes fields for values like tags[:name].
I can't help much more without knowing more about the relevant code. But I'm guessing what you want to do is change your test to read
put :update, :id => #channel.id, :channel => #channel, :tags => { :name => 'tag' }
or something like that, mimicking the params you'd get by actually submitting the form that PUTs to your update action.
This is difficult to help you because we don't know what you're trying to do. For example, it would be helpful if you showed more of the test (for example, the values you set as your variables) and the specific results of the test.
Anyway, is the #tags variable an arel object? and if so, are you expecting the ID as the value to be passed? If not, then you probably want to specify the attribute referenced in #tags. For example, #tags.name... Or, does #tags reference a hash, itself?
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]"
}
Is there a way in your routes file to check and validate URL parameters. I am NOT talking about restful '/controller/action/:id' params, but 'controller/action?param1=x¶m2=y¶m3=z'. I need to be able to validate each parameter and require them.
Yes, you can. For example to check that param1 exists and is not blank you would do the following:
match 'c/action' => 'c#action', :constraints => lambda{ |req| !req.params[:param1].blank? }
You can also scope these constraints to apply them to multiple routes:
scope :constraints => lambda{ |req| !req.params[:param1].blank? } do
match 'controller/action1' => 'controller#action1'
match 'controller/action2' => 'controller#action2'
end
The problem with constraints approach outlined by Pan Thomakos is that it will prevent the url with invalid set of parameters from ever reaching your codebase and you being able to respond to the user in a meaningful manner(the user will see page not found error I believe).
If that satisfies your requirement, thats fine, but a more user-friendly way would be to move parameter validation into the corresponding controller where in your action method you would go through the set of params this action method has received and if any of the required are missing, you would construct a meaningull message and error it back to the user via a:notice
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?
At the end of one of my controller actions I need to redirect to a page that only accepts put requests. I have been trying to figure out how to get redirect_to to use a put request but to no success.
Is this possible? Or is there another way to accomplish this?
I don't think you are able to do this, and I suspect that the limitation is part of HTTP itself.
When using redirect_to - the redirection happens as a "302 Moved" header unless otherwise specified in the parameters.
Having a look at the HTTP Spec itself doesn't reveal any way to change the type of request the browser makes via redirect.
HTTP Redirects:
This class of status code indicates
that further action needs to be taken
by the user agent in order to fulfill
the request. The action required MAY
be carried out by the user agent
without interaction with the user if
and only if the method used in the
second request is GET or HEAD.
I think you may need to use JavaScript to achieve this functionality, or perhaps rethink the flow of control in your application.
If the action is in the same controller as where you're trying to redirect from, simply call the action and render the template like so:
def show
index
render :action => "index"
end
If it's not, then I don't know how you do that.
Ok, so I found a solution to my problem. I found a very good write up on the situation here. My implementation looks like this:
private
def redirect_post(redirect_post_params)
controller_name = redirect_post_params[:controller]
controller = "#{controller_name.camelize}Controller".constantize
# Throw out existing params and merge the stored ones
request.parameters.reject! { true }
request.parameters.merge!(redirect_post_params)
controller.process(request, response)
if response.redirected_to
#performed_redirect = true
else
#performed_render = true
end
end
Then I called this method like this:
redirect_post :controller => 'registrations', :action => 'order', :_method => 'put', :authenticity_token => params[:authenticity_token]
So I was able to 'fake' a put request by making a post request (using redirect_post) and then assigning 'put' to a _method param. If you look at a normal put request all it is a post from a form with a _method param. So its a bit hackish but it gets the job done.
Also, you have to make sure that when you call redirect_post the values of your hash are strings otherwise errors will be thrown.
You could redirect to a different page that issues the put request from the client, using Javascript.