I am using the twitter gem for ruby and need to send a POST request to users/lookup endpoint.
As per the gem source code documentation(https://github.com/sferik/twitter/blob/4e8c6dce258073c4ba64f7abdcf604570043af71/lib/twitter/rest/users.rb), the request should be POST by default, unless I pass :get :
#option options [Symbol, String] :method Requests users via a GET request instead of the standard POST request if set to ':get'.
def users(*args)
arguments = Twitter::Arguments.new(args)
request_method = arguments.options.delete(:method) || :post
flat_pmap(arguments.each_slice(MAX_USERS_PER_REQUEST)) do |users|
perform_with_objects(request_method, '/1.1/users/lookup.json', merge_users(arguments.options, users), Twitter::User)
end
end
I am calling it as follows:
users = #client.users(twitter_screen_names_arr, [:method, :post])
However, I am not sure if this is actually resulting in a POST request / a GET request.
How can I make sure if this is a POST/GET? I would like to print the request that is being made to get a clarity on what actually gets sent.
Thanks!
As you can see from the code it uses POST by default. This behavior is also specified with RSpec.
You can invoke the users method like this:
#client.users(twitter_screen_names_arr, :method => :post)
or simply
#client.users(twitter_screen_names_arr)
since POST is the default request method.
If you don’t trust the code or the specs, you could run the request through a proxy to verify this behavior manually.
Related
I started Ruby on Rails few days ago, and I'm struggling with routing.
Indeed, I would like to make a post request through my routes.rb, but I keep having a
No route matches [GET] "/orders/refresh"
error.
Here is my routes.rb :
# frozen_string_literal: true
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get '/orders', to: 'orders#index'
get '/orders/active(/:q)', to: 'orders#active'
post '/orders/refresh', to: 'orders#refresh'
end
and here is my controller (orders_controller.rb) :
# frozen_string_literal: true
class OrdersController < ApplicationController
def index
#orders = Order.order(:departure_date).all
render json: #orders.to_json
end
def active
if !params[:q]
#orders = Order.order(:departure_date).where(active: true)
else
#orders = Order.order(:departure_date).where("reference = ? OR client_name = ? OR departure_city = ? OR arrival_city = ?",
params[:q], params[:q], params[:q], params[:q])
.where(active: true)
end
render json: #orders.to_json
end
def refresh
response = RestClient.get 'https://wakeo-technical-test.s3.eu-west-3.amazonaws.com/api.json'
json = JSON.parse response
if !json.nil?
json.each do |order|
old_order = Order.find_by(reference: order["client_number"])
if !old_order.nil?
old_order.update(departure_date: order["dep_time"])
old_order.update(arrival_date: order["arr_time"])
old_order.update(client_name: order["company"])
old_order.update(departure_city: order["dep_city"])
old_order.update(arrival_city: order["arr_city"])
end
end
else
puts "error seeding external API"
end
end
end
From what I have understood, it seems like RoR will try to find a GET request for that specific URL, and since it won't find any, it will throw that error. How could I make that request be a POST for Rails ?
Also, I would appreciate any suggestion about how I should use ActiveRecord Querying, I'm pretty sure I could do it better here.
Thanks, have a great day !
EDIT : Here is the list of different routes my app seems to be capable of, including my POST.
Routes and error
The most common reason you unexpectly get GET requests instead of PUT, PATCH, POST or DELETE is that you are using link_to 'Something', '/some_path', method: :post and you broke the Rails Unobtrusive Javascript Driver (Rails UJS):
Because submitting forms with HTTP methods other than GET and POST
isn't widely supported across browsers, all other HTTP methods are
actually sent over POST with the intended method indicated in the
_method parameter. Rails automatically detects and compensates for this.
Rails does that with a JavaScript event handler attached to any link with the data-method attribute. But if you broke that functionality the browser will just perform its default action which is sending a GET request when the user clicks a link.
This problem usually boils down to one or more of:
Your javascript is throwing an error which halts script execution (use the browser console to find the error, make it suck less).
Rails UJS is not included in your assets pipeline or webpacker packs and thus not in the page.
The quick and easy solution to sidestep the problem is by using button_to which actually creates a form and does not require any JavaScript trickery. After all forms can send POST requests. And by just passing a _METHOD hidden field Rack will treat the request as any other HTTP verb.
button_to 'Something', '/some_path', method: :post
But in the long run you should probably fix the problem if you want to use any of the features of Rails UJS.
Your routes.rb is expecting a POST request to /orders/refresh routes, but apparently you are testing with a GET request.
Try changing your routes.rb:
Rails.application.routes.draw do
# ...
get '/orders/refresh', to: 'orders#refresh'
end
... or change your request to a POST request. If you are using Rails forms, you must do something like this:
form_with(url: "/orders/refresh", method: "post")
Ok, I think I figured it out.
It might be because when I hit /orders/refresh directly in my web browser, it will try to find a GET corresponding to the request.
I managed to make POST using a client like Postman, and everything works fine.
Thank you for your help !
Is there a way in Rails to extract the HTTP verb(s) associated with a route? For example, given a route like this:
match 'users', to: 'users#show', via: [:get, :post]
Can I achieve something like this?
users_path.respond_to?(:get) (obviously #respond_to is not the right method)
The closest I've managed to come is by doing the following, but it doesn't really seem satisfactory.
Rails.application.routes.routes.named_routes["users"].constraints[:request_method] # => /^GET$/
For context, I have an action that sets a cookie and then does a redirect_to :back, but this action is available globally across the entire site (it's in the footer). So, if a user happens to be in a flow, and one of those routes only accepts POSTs, the redirect fails because the request issued is a GET.
The request object is available to your controller. The following methods are available to determine the type of HTTP request:
if request.get?
# request is a GET request
if request.post?
# request is a POST request
There are similar methods for other HTTP request verbs, including PUT and DELETE.
UPDATE:
Per the update to the question, the following code can be implemented within your controller to yield the constrained verbs on any named route as a pipe-delimited string:
Rails.application.routes.named_routes["users"].verb
#=> "GET|POST"
Accordingly, you can split the string to retrieve an array of each of the HTTP methods specified in the route's constraints:
methods_string = Rails.application.routes.named_routes["users"].verb
#=> "GET|POST"
methods_array = methods_string.split('|')
#=> ["GET", "POST"]
methods_array[0]
#=> "GET"
methods_array[1]
#=> "POST"
for someone looking with it in future you can use
env["REQUEST_METHOD"]
to get HTTP verb of specific action
In views file, my code is similar with:
<%= link_to refresh_post_user_post_path(#user,#post), :method => :put%>
In routes.rb:
resources :users do
resources :posts do
member do
put :refresh_post
end
end
end
The interesting thing is when inspecting the request object in controller:
def refresh_post
... ...
p request.method # => POST
p request.request_method # => PUT
... ...
end
I know method and request method are different, but where's POST request from?
Moreover:
$ rake routes
refresh_post_user_post_path PUT /users/:user_id/posts/:id/refresh_post, {:action => "refresh_post", :controller => "posts"}
I am with Rails 3.0.11 and Ruby ree-1.8.7, everything above works with no exception. But any body knows how come the request is a POST?
Rails emulates "advanced" request types (PUT, DELETE, etc) with a POST type. This is because browsers typically support only GET and POST.
So rails accepts a POST request and looks for a :method parameter. If such parameter is found, it updates request type accordingly (so that your routes can work, for example).
The truth is request.method always returns POST, no matter for a PUT or POST request and no matter the controller method is a default 'update' or a custom one. Sergio, you are right.
It is from Rails' doc for request class:
method:
Returns the original value of the environment’s REQUEST_METHOD, even if it was overridden by middleware
request_method:
Returns the HTTP method that the application should see. In the case where the method was overridden by a middleware (for instance, if a HEAD request was converted to a #GET, or if a _method parameter was used to determine the method the application should use), this method returns the overridden value, not the original.
The interesting thing is even if it is a PUT request, in the log file it says like:
Started POST "/users/251/posts/1234" for 127.0.0.1 at Fri Jan 18 21:48:21 +0800 2012
This happens in Rails 3.0.11, and log file doesn't tell it is a PUT request at all. However in later versions, it has been fixed:
https://github.com/rails/rails/commit/17a91a6ef93008170e50c073d1c3794f038a0a33
And the log becomes as friendly as:
Started PUT "/users/251/posts/1234" for 127.0.0.1 at Fri Jan 18 21:48:21 +0800 2012
OAuth2::AccessToken.post() method is specified like this in the documentation:
(Object) post(path, opts = {}, &block)
I'm trying to pass some arguments, but seems that I*m doing it wrong:
response = token.post('/oauth/create.js', {:title => "title", :description => "desc"})
The parameters are never reaching the method, values are always nil. So, what is the correct way of using the post method with arguments? And what is that &block?
I'm also getting WARNING: Can't verify CSRF token authenticity. This might be contributing to the problem as well. The case is that I'm using OAuth api from the outside of the app. OAuth 2 is implemented via Doorkeeper gem.
Update: The CSRF warning is gone now after I defined scopes. Also I manage to use this post() method with arguments by providing the as part of the url: "?title=test&...". Still would be nice to know how to use this method as documented.
The body in a POST or PUT is accessed via the options body param. No documentation on this. Had to look in the oauth client code itself to discover this:
https://github.com/intridea/oauth2/blob/ebe4be038ec14b3496827d29cb224235e1c9f468/lib/oauth2/client.rb
Your example, with correct body would be:
response = token.post('/oauth/create.js', {body: {:title => "title", :description => "desc"}})
You can use the block to pass parameters to post request:
token.post('/oauth/create.js') do |request|
request.params['title'] = "something"
end
OAuth2 gem uses faraday, the request object is a faraday request, so you might want to check other ways to pass parameters along with the request
faraday gem => https://github.com/lostisland/faraday
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.