How to build a proxy controller and faraday - ruby-on-rails

I have to built an angular js application as a client to consume the api.
The problem is that the api doesn't support jsonp calls.
So I've created a rails application that makes the calls to the api and returns the content.
I'm using the faraday gem
Right now I have a method for each call to the api. But since every method only creates a request, triggers the request and returns the content.
I'm was wandering if I can create a proxy controller that creates the request based on what it receives then creates an request with faraday and returns the result. Something like this :
def proxy_request
if request.method_symbol == :get || request.method_symbol == :delete
line 7: response = faraday_conn.run_request(request.method_symbol, request.fullpath, nil, request.headers)
elsif request.method_symbol == :post || request.method_symbol == :put || request.method_symbol == :patch
response = faraday_conn.run_request(request.method_symbol, request.fullpath, request.body.read, request.headers)
end
render :text => response.body, :status => response.status, :content_type => response.headers["Content-Type"]
end
This is not working. What I'm doing wrong ?
It always fails with
NoMethodError (undefined method `strip' for #<StringIO:0x3436848>):
app/controllers/api_proxy_controller.rb:7:in `proxy_request'

The backtrace is incomplete (some frameworks, e.g. Rails/RSpec, may hide lines coming from outside your project by default). The error actually came from Net::HTTP (net/http/header.rb:17), at least in my case (Ruby 2.1, Rails 4.0.2 and the default Faraday adapter). It expects all values in headers to be strings (or at least respond to strip).
One workaround is to exclude any header with a non-string value. Depending on what you want to do, this may or may not work for you, e.g. something like:
faraday_conn.run_request(
request.method_symbol,
request.fullpath,
request.body.read,
request.headers.select{|k,v| v.respond_to?(:strip)}
)
Note that, since this is in a Rails controller, request.headers returns an instance of ActionDispatch::Http::Headers which quacks like a Hash but not actually a Hash (you can call to_h on it to convert it to a hash).

Related

Setting Up Rails to Receive HTTP POST Request

Is setting up Rails to receive a HTTP POST request that sends information encoded in JSON form as easy as adding the following code to the sessions_controller? Or are there other steps involved?
def create
if user = User.authenticate(params["email"], params["password"])
session[:user_id] = user.id
render:json => "{\"r\": \"t\"}" + req
else
render :json => "{\"r\": \"f\"}"
end
end
Should be that easy, though you will need to add a route to your routes.rb file as well specifying POST as the HTTP verb and pointing it to sessions#create. You also might want to use strong parameters just to validate what parameters are required and which you'll accept. As a heads up, I'm not entirely sure what "{\"r\": \"t\"}" + req is supposed to represent. It looks like req would be undefined in this case, but perhaps you're just omitting some code. Lastly, render :json => ... is sort of the old way of including a hash. I believe as of Ruby 2 the standard is something more like render json: .... Hopefully that helps.

Verify if a request is GET / POST

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.

Ruby on Rails: how to use OAuth2::AccessToken.post?

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

Identify GET and POST parameters in Ruby on Rails

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?

How do can you make redirect_to use a different HTTP request?

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.

Resources