Running a HTTP request with rails - ruby-on-rails

It has been a while since I have used Rails. I currently have a curl request as follows
curl -X GET -H 'Authorization: Element TOKEN, User TOKEN' 'https://api.cloud-elements.com/elements/api-v2/hubs/marketing/ping'
All I am looking to do is to be able to run this request from inside of a rails controller, but my lack of understanding when it comes to HTTP requests is preventing me from figuring it out to how best handle this. Thanks in advance.

Use this method for HTTP requests:
def api_request(type , url, body=nil, header =nil )
require "net/http"
uri = URI.parse(url)
case type
when :post
request = Net::HTTP::Post.new(uri)
request.body = body
when :get
request = Net::HTTP::Get.new(uri)
when :put
request = Net::HTTP::Put.new(uri)
request.body = body
when :delete
request = Net::HTTP::Delete.new(uri)
end
request.initialize_http_header(header)
#request.content_type = 'application/json'
response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') {|http| http.request request}
end
Your example will be:
api_request(:get, "https://api.cloud-elements.com/elements/api-v2/hubs/marketing/ping",nil, {"Authorization" => "Element TOKEN, User TOKEN" })

It would be something like the following. Note that the connection will be blocking, so it can tie up your server depending on how quickly the remote host returns the HTTP response and how many of these requests you are making.
require 'net/http'
# Let Ruby form a canonical URI from our URL
ping_uri = URI('https://api.cloud-elements.com/elements/api-v2/hubs/marketing/ping')
# Pass the basic configuration to Net::HTTP
# Note, this is not asynchronous. Ruby will wait until the HTTP connection
# has closed before moving forward
Net::HTTP.start(ping_uri.host, ping_uri.port, :use_ssl => true) do |http|
# Build the request using the URI as a Net::HTTP::Get object
request = Net::HTTP::Get.new(ping_uri)
# Add the Authorization header
request['Authorization'] = "Element #{ELEMENT_TOKEN}, User #{user.token}"
# Actually send the request
response = http.request(request)
# Ruby will automatically close the connection once we exit the block
end
Once the block exits, you can use the response object as necessary. The response object is always a subclass (or subclass of a subclass) of Net::HTTPResponse and you can use response.is_a? Net::HTTPSuccess to check for a 2xx response. The actual body of the response will be in response.body as a String.

Related

how to save the http.request curl in rails?

I already managed to make a post request of curl in rails
I print on the console the response.body
{"access_token":"XXXXXXX","public_key":"XXXXXX","refresh_token":"XXXXXX","live_mode":false,"user_id":XXXX,"token_type":"bearer","expires_in":15552000,"scope":"offline_access read write"}
now I want to gurantee that data in a table in rails
How can you do that?
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
If I understood correctly what you want is to make a HTTP request then parse the response and store it into a table (model) in your database. Well if that is the case:
require 'net/https'
uri = URI.parse("http://example.com/path")
// You can change the request method to whatever method you want
request = Net::HTTP::Post.new(uri.request_uri)
request.body = { //json hear in case you need it }
request.add_field 'token', 'XXXXX'
response = http.request(request).body
json_response = JSON.parse(response, symbolize_names: true)
You will have the response in json_response, then you only will have to store that information in the model you want:
Model.create(attribute_1: json_response[:parameter_1], attribute_2: json_response[:parameter_2]...)

Bad Request trying to call service with Digest Auth from ruby

I'm trying to call a service with Digest Auth from a rails application and it always returns a 400 bad request error.
I've used net-http-digest_auth gem to create the headers but I think I've missed something.
def get_digest(url)
uri = URI.parse(url)
http = Net::HTTP.new uri.host, uri.port
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
req = Net::HTTP::Get.new(uri.request_uri)
# Fist call with the 401 and auth headers
digest_response = http.request(req)
digest_auth_request = Net::HTTP::DigestAuth.new
uri.user = digest_auth[:user]
uri.password = digest_auth[:password]
auth = digest_auth_request.auth_header uri, digest_response['www-authenticate'], 'GET', true
req.add_field 'Authorization', auth
response = http.request(req)
# Response is always #<Net::HTTPBadRequest 400 Bad Request readbody=true>
if response.code.to_i == 200
response_body = response.body
else
error
end
response_body
end
The request's headers look like this:
Digest username=\"myuser#mydomain.com\", realm=\"Digest\", algorithm=MD5-sess, qop=\"auth\", uri=\"/path/WS/my%20user/path/path/path/path/service.svc\", nonce=\"+Upgraded+v1e3f88bce1c32bd15avn421e440ca6622ebadd4522f7ed201fab1421c39d8fd15b771b972c9eb59894f8879307b9e6a5544476bc05cc7885a\", nc=00000000, cnonce=\"d42e6ea8a37aadsasdbea1231232456709\", response=\"7fbfc75cc3aasdasd342230ebf57ac37df\""
I can't figure out what's happening, is there any other gem to make this easier?
Finally found the problem by comparing browser header vs ruby header.
I wasn't calculating "nc" (calls counter) correctly. After adding +1 it started to return a 401 error (now I have a different problem ;)).

Rails email using Postmark API on Heroku -- connection reset by peer

I have multiple bruises today, trying to learn two things at once... the API for Postmark and Rails HTTP requests.
Goal: Use Postmark add-on for Heroku to send production email.
I am trying to combine this article on HTTP requests...
http://docs.ruby-lang.org/en/2.0.0/Net/HTTP.html
... with this API reference for Postmark...
http://developer.postmarkapp.com/developer-send-api.html
Unfortunately, the examples from Postmark are done in curl and I have not succeeded in translating them into a HTTP request. I suspect the problem centers around the headers -- the parts of the transmission other than the body.
The rescue clause seen in the code below traps the error 'connection reset by peer'. At this point I don't know if I am even close to the right format for the headers that provide Postmark authentication.
I have the proper server token (in the config entry) and the From email has been given the required Postmark signature.
def send_production_email(email_address, subject, email_body)
# Use API to interact with Heroku add-on Postmark
# http://developer.postmarkapp.com/developer-send-api.html
uri = URI('https://api.postmarkapp.com/email')
# Form the request
req = Net::HTTP::Post.new(uri)
# Set request headers -- SUSPECT THIS IS WRONG
req['Accept'] = 'application/json'
req['Content-Type'] = 'application/json'
req['X-Postmark-Server-Token'] = Rails.application.config.postmark_token
rbody ={
'From' => 'Support <michael#mydomain.com>',
'To' => email_address,
'Subject' => subject,
'HtmlBody' => wrap_html(email_body),
'TextBody' => email_body
}.to_json
req.body = rbody
# Send the request, waiting for the response
begin
response = Net::HTTP.new(uri.host, uri.port).start {|http| http.request(req) }
rescue Exception => e
logthis("http request error: #{e.message}")
return
end
# ...parsing section omitted since I do not get that far...
end
A second attempt was formatted this way, but results in the same peer reset error:
rbody ={
'From' => 'Support <michael#disambiguator.com>', # TODO: replace email when domain is live
'To' => email_address,
'Subject' => subject,
'HtmlBody' => wrap_html(email_body),
'TextBody' => email_body
}.to_json
uri = URI('https://api.postmarkapp.com/email')
http = Net::HTTP.new(uri.host, uri.port)
# http.use_ssl = true
request = Net::HTTP::Post.new(uri.path, {'Content-Type' => 'application/json', 'Accept' => 'application/json', 'X-Postmark-Server-Token' => Rails.application.config.postmark_token})
request.body = rbody
# Send the request, waiting for the response
begin
response = http.request(request)
rescue Exception => e
logthis("http request error: #{e.message}")
return
end
I am grateful for any guidance!
I’m a Wildbit’s employee and the maintainer of the official Postmark Ruby gem.
The "connection reset by peer" error is the result of you trying to send an unencrypted HTTP request to an endpoint expecting secure communication via HTTPS. So, if you change this line:
Net::HTTP.new(uri.host, uri.port).start {|http| http.request(req) }
to:
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
response = http.start { |http| http.request(req) }
then you should be able to receive a response from the API. I see that you have this line in the second example, but it is commented. Since you’re doing this as an exercise, I’d like to add that when using net/http you don’t usually have to work with the underlying classes like Net::HTTP::Post. It’s generally simpler to use the higher level API provided by instances of the Net::HTTP class. Here is an example of how your method could be simplified by using it:
def send_production_email(email_address, subject, email_body)
uri = URI('https://api.postmarkapp.com/email')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
headers = {'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Postmark-Server-Token' => Rails.application.config.postmark_token}
payload = {'From' => 'tema#wildbit.com',
'To' => email_address,
'Subject' => subject,
'HtmlBody' => email_body,
'TextBody' => email_body}
http.post(uri.request_uri, payload.to_json, headers)
rescue => e
puts "http request error: #{e.message}"
end
And, if you’re interested in how net/http is used in the official Postmark Ruby gem, check out the HttpClient class’ source.

Why Rails (current 4.0) fails to interpret nested JSON (from a HTTP POST)?

I am writing a simple client server application (using only JSON API) with Ruby (client) and Rails (server).
When trying to create a game from client, I am using:
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({"tttgame" => {"name" => "Marius"}})
resp = http.request(request)
On server side (tttgames_controller.rb) I have:
# POST /tttgames
# POST /tttgames.json
def create
#tttgame = Tttgame.new(tttgame_params)
...
end
...
def tttgame_params
params.require(:tttgame).permit(:name)
end
Logs on server are:
Started POST "/tttgames.json" for 127.0.0.1 at 2013-10-05 12:58:44 +0300
Processing by TttgamesController#create as JSON
Parameters: {"tttgame"=>"{\"name\"=>\"Marius\"}"}
Completed 500 Internal Server Error in 0ms
NoMethodError (undefined method `stringify_keys' for "{\"name\"=>\"Marius\"}":String):
app/controllers/tttgames_controller.rb:33:in `create'
How can I fix this? All examples from the Internet are looking the same. Thanks!
Both methods set_form_data and post_form are encoding data using format x-www-form-urlencoded. Check here.
Examples that are provided do not contain nested hashes.
I have found here an example, under the REST methods section, which works very well.
Thus, in order to get on server a valid structure with nested hashes, the client should use square brackets:
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({"tttgame[name]" => “Marius”)
resp = http.request(request)
or much simpler:
uri = URI.parse(url)
resp = Net::HTTP.post_form(uri, {"tttgame[name]" => “Marius”})
This will generate on server
Parameters: {"tttgame"=>{"name"=>"Marius"}}
You might want to do this instead. It's even more compact.
uri = URI.parse(url)
resp = Net::HTTP.post_form(uri, "tttgame" => {"name" => "Marius"})
From http://ruby-doc.org/stdlib-2.0.0/libdoc/net/http/rdoc/Net/HTTP.html#label-POST+with+Multiple+Values
UPDATE: In addition, your String is not a valid JSON. It needs to be "{\"name\":\"Marius\"}" instead.
You need to parse that response, because right now it is a String ("{\"name\"=>\"Marius\"}") but you actually need a Hash ({"name" => "Marius"}).
Therefore #stringify_keys fails because it is a method that operates on a Hash.
So do a:
#tttgame = Tttgame.new(JSON.parse(tttgame_params))
instead. This will turn your serialized JSON response into a Hash from a String.

Rails 3 Post to external web service

Lets say I have a blog post that a user is creating and I want to send all of the data to an external web service as XML with a specific schema so it can be ingested into that web service.
I have been looking into the ActionDispatch::Request
And I read this Using Ruby on Rails to POST JSON/XML data to a web service post and answer
However I got an error saying content_type was not a valid method for request. So I changed that line to call the header method and create a header for content-type with the appropriate information
Ok... so now where to go?
This is my code so far:
url= URI.parse('http://10.29.3.47:8080/ingest')
response = Net::HTTP::Post.new(url.path)
request.headers["Content-Type"] = 'application/json'
request.body = 'all of my xml data and schema which is far too long to type here'
response = Net::HTTP.start(url.host, url.port) {|http| http.request(request)}
assert_equal '201 Created', response.get_fields('Status')
I get an error saying that request.body is also not a valid method call, but when I look at the API the only thing matching body is "body()" which does not take arguments. So how do I pass the content of my post to the web service?
Thank you for the help!
You had response = Net::HTTP::Post.new(url.path) instead of request = Net::HTTP::Post.new(url.path) and you add headers with add_field.
require 'net/http'
require 'uri'
url= URI.parse('http://10.29.3.47:8080/ingest')
request = Net::HTTP::Post.new(url.path)
request.add_field 'Content-Type', 'application/json'
request.body = 'all of my xml data and schema which is far too long to type here'
response = Net::HTTP.start(url.host, url.port) {|http| http.request(request)}

Resources