I have a Rails Route like this
resources :agents do
member do
post :location_wifi
end
end
And a method in controllers/agents_controller.rb like this
def location_wifi
puts params
puts params.permit!
end
I send a POST request to that route with a JSON payload like
{ "custom": {"model": "a"} }
When i check the params i can't see the POST payload i sent nowhere. The data i am sending here is not being stored in the database but sent to a google API and that result is later stored. For that reason i don't have a model for it.
What is going on here? Why can't i access the POST payload?
The problem was that i was not sending a Content-Type=application/json header and my API routing namespace defines a :defaults => { :format => 'json' }.
Sending the content header fixed it.
Related
I have been trying to figure out how the params method works and I'm a bit stuck on the process.
For example, if a user clicks on a certain blog post in the index page, I guess that the link_to method calls the Post controller and the show action along with its block #post = Post.find(params[:id]) and then goes to the database to find the post and the view displays it.
So my missing link seems to be when is the post id passed into the params method?
Because the others already explained about params, I'm just going to answer directly a question of yours:
when is the post id passed into the params method
I think it's best explained with an example; see below:
say that you clicked a link:
/posts/1/?param1=somevalue1¶m2=somevalue2
The Rails server receives this request that a client wants to view this GET /posts/1/?param1=somevalue1¶m2=somevalue2 address.
To determine how the Rails server will respond, the server will first go to your routes.rb and find the matching controller-action that will handle this request:
# let's say your routes.rb contain this line
# resources :posts
# resources :posts above actually contains MANY routes. One of them is below
# For sake of example, I commented above code, and I only want you to focus on this route:
get '/posts/:id', to: 'posts#show'
From above notice that there is this :id, Rails will automatically set params[:id] to the value of this :id. This is the answer to your question where params[:id] comes from.
It doesn't have to be :id; you can name it whatever you want. You can even have multiple URL params like so (just an example):
get /users/:user_id/posts/:id which will automatically set the value on params[:user_id] and params[:id] respectively.
In addition to this URL params like :id, Rails also injects values to params[:controller] and params[:action] automatically from the routes. Say from the example above, get '/posts/:id', to: 'posts#show', this will set params[:controller] to 'posts', and params[:action] to 'show'.
params values also comes from other sources like the "Query string" as described by Mayur, and also comes from the body of the request, like when you submit a form (the form values are set within the body part of the request) and like when you have JSON requests, which all of these are automatically parsed by Rails for your convenience, so you could just simply access params and get the values as you need them.
Params are hashes in ruby with Indifferent access which means,
hsh = {"a" => 1, "b" => 2}
Consider this hsh as params returned from a POST request from browser, it's a key value pair with keys as string. Since it's a params so the values can be accessed as
hsh["a"]
=> 1
hsh [:a]
=> 1
params are formed on the client where the interface load, consider a form which has a submit button. When you press submit, the data filled in form or any hidden textboxes are formed into a hash and passed across the request. This when received on server end will be called as params or request params.
For Get requests: data send across the url will be read as params on backend.
GET: http://www.abx.com?user=admin
params on backend: {"user" => "admin"}
This will be displayed in rails server logs
For Put request: data send across the body will be called params.
PUT: http://www.abx.com
data: {"user" => "admin"} Client side
params on backend: {"user" => "admin"}
This will be displayed in rails server logs
How does the params method work?
The params come from the user's browser when they request the page. For an HTTP GET request, which is the most common, the params are encoded in the URL. For example, if a user's browser requested
http://www.example.com/?post=1&comment=demo
then params[:post] would be "1" and params[:comment] would be "demo".
In HTTP/HTML, the params are really just a series of key-value pairs where the key and the value are strings, but Ruby on Rails has a special syntax for making the params be a hash with hashes or array or strings inside.
It might look like this:
{"post"=>"1", "comment"=>"demo"}
Link to Rails Guides on params: guides
When making POST/PATCH requests to my rails webapp, I send the data as json. For example this json data {"name" => "Hallo", "status" => "admin"} to an controller named UsersController. The setting for wrap_parameters in the wrap_parameters.rb file are the default values:
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
end
So every json data gets wrapped like this "user" => {"name"=> "Hallo", "status" => "admin"}. This is how it should be. However when I call a custom method called status which is located in ApplicationController to check the server status, all following requests json data won't get wrapped in their controllers/models name hash, but in a hash like this "application" => {"name"=> "Hallo", "status" => "admin"}.
This is for all following requests until I restart the server. Moreover if I want to keep the "right" behavior I can't call the status method in ApplicationController again which would lead to the "application" => {...}wrapping.
What I found out is when I set Content-Type = text/html (It was Content-Type = application/json before) for my request to the status method the following request are not polluted, meaning I get the right wrapping behavior.
However this is very disturbing, as someone just needs to call the status method, when Content-Type = application/json is set for the request and all the following POST/PATCH requests won't work anylonger.
I don't know if this matters, but I set the following in routes.rb:
namespace :api, defaults: {format: :json} do
namespace :v1 do
...
match "/status", to: "application#status", via: "get"
...
end
end
There's one tricky thing in the wrap_parameters method. You can specify the format as you did but ParamsWrapper is getting the format from request.content_mime_typeinstead of request.format so if you're trying to do POST/PUTrequest, ParamsWrapper format is multipart_form and not json.
So instead of:
wrap_parameters format: [:json]
you have to do:
wrap_parameters format: [:json, :multipart_form]
Source: https://github.com/rails/rails/blob/dda31d59a03adb7e5aa372e72bb66a3886632f2f/actionpack/lib/action_controller/metal/params_wrapper.rb#L281
I am trying to send some raw data in a JSON post request to my RSpec controller test. I have successfully tested the controller with an actual Postman request but I can't get the Rspec test to work
The error I am getting is
param is missing or the value is empty: annotations
Here is my test set-up, which I have copied verbatimly from rails server log when I run the request via Postman
params = '{annotations"=>[{"id"=>1, "location_start"=>1, "location_end"=>3, "source_text"=>"what", "reading"=>"cool"}, {"id"=>2, "location_start"=>1, "location_end"=>-1, "reading"=>"cool"}]}'
patch :update, params, format: :json
I have also tried
params = {:annotations => [{ :id=>1, :location_start=>1, :location_end=>3}]}
patch :update, params.to_json
https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/controller-spec#setting-a-different-content-type-for-example-json-(request-type)
Check out how the json body is defined in this example: It's a ruby hash! So you need to not try passing in a raw string but a Hash that then gets automatically converted to JSON for you.
Hope that helps you out
For GET request its:-
response = Typhoeus::Request.get("http://localhost:3000/users/1.json?oauth_token=12")
This returns Json response perfectly.
for Post request:-
response = Typhoeus::Request.post("http://localhost:3000/users/1.json?oauth_token=12",:params => {'[user][city]' => params[:location]})
is not working...
This is returning routing error.
Update:--
FOr login this api post call is working..
response = Typhoeus::Request.post(API_SERVER_ADDRESS + "user_sessions.json" + API_OAUTH_TOKEN, :params => {'[user_session][email]' => params[:email], '[user_session][password]' =>params[:password]})
In routes its
resources :users
and also web http request is working perfectly fine..
UPDATE
For example http request from rails log is:--
Parameters: {"commit"=>"Update", "authenticity_token"=>"8nvzCd0GF9IxjMcTfHOMJTPnycVPNIENMoMff8w4qAI=", "utf8"=>"✓", "id"=>"1", "user"=>{ "city"=>"abc"}}
Now i want to sent same kind of request..
The :params parameter should be a hash of your parms, meaning key-value pairs, so maybe something like this:
response = Typhoeus::Request.post("http://localhost:3000/users/1.json?oauth_token=12",:params => {:user => 'u', :city => 'c', :location => 'l'})
...or somesuch - whatever the parms are, whatever the values are. Your original doesn't translate into a meaningful hash for what you are wanting to do, I think.
Also, check your routing to make sure that what you are trying to do is properly routed.
Here is the solution
From this
response = Typhoeus::Request.put(API_SERVER_ADDRESS + "users/" +user_id + ".json" ,:params => {:oauth_token=>'12', :user=>{:city => params[:location]}})
Make sure you have declared a separate POST route in your routes.rb file. Even if the URLs are the same, different HTTP methods require different routes.
Using resources :users gives you the following by default:
GET /users/new # new
POST /users # create
GET /users/:id # show
GET /users/:id/edit # edit
PUT /users/:id # update
DELETE /users/:id # destroy
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?