Am trying to access data from a certain API using POST method but it returns back the actual list of params that I sent. Here is my code I don't know whether am doing this right, I will be glad for your help.
This is my controller
#Request access token from ExactApi
params = {
"code" => "#{code}",
"redirect_uri" => '/auth/exact/callback',
"grant_type" => "authorization_code",
"client_id" => "{CLIENT_ID}",
"client_secret" => "CLIENT_SECRET"
}
uri = URI.parse('https://start.exactonline.nl/api/oauth2/token')
#Encode the url into /x-www-form-urlencoded
uri.query = URI.encode_www_form(params)
#Transform http protocol into a secure protocol[https]
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # You should use VERIFY_PEER in production
#Send the request to the ExactApi and return the received data
res = Net::HTTP::Post.new(uri.request_uri)
res.set_form_data(params)
puts "Received:: "+ res.body.to_yaml
Output
code[CODE]&redirect_uri=%2Fauth%2Fexact%2Fcallback&grant_type=authorization_code&client_id=CLIENT_ID&client_secret=SECRET_ID
How can I access the actual data returned from API?
require "net/http"
require "uri"
uri = URI.parse('https://start.exactonline.nl/api/oauth2/token')
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # You should use VERIFY_PEER in production
request.set_form_data({
"code" => "#{code}",
"redirect_uri" => '/auth/exact/callback',
"grant_type" => "authorization_code",
"client_id" => CLIENT_ID,
"client_secret" => CLIENT_SECRET
})
response = http.request(request)
http://www.rubyinside.com/nethttp-cheat-sheet-2940.html
However I would use Omniauth instead of reinventing the Oauth wheel. Its pretty hard to get right. If you cannot find a ready made provider then creating a custom provider is pretty simple:
require 'omniauth-oauth2'
module OmniAuth
module Strategies
class ExactOnline < OmniAuth::Strategies::OAuth2
# change the class name and the :name option to match your application name
option :name, :exactonile
option :client_options, {
:site => "https://start.exactonline.nl",
:authorize_url => "/api/oauth2/token"
}
uid { raw_info["id"] }
info do
{
:email => raw_info["email"]
# and anything else you want to return to your API consumers
}
end
def raw_info
#raw_info ||= access_token.get('/api/v1/me.json').parsed
end
end
end
end
You are using puts which outputs to the server console. I'm confused where the output is. You should set yourself up a view with the same name as the controller action your block of code is within, for example if this is the index action:
def index
params= { your_hash_keys: "value" }
end
Then you should have an index.html.erb inside the app/views/controller_name/ In your controller instead of puts "Received:: "+ res.body.to_yaml use #debug = "Received:: "+ res.body.to_yaml and inside your view do something to output it like <%= #debug.inspect %>
Alternatively, and not recommended is to render inline in the controller:
render inline: "Received:: " + res.body.to_yaml
Layouts and Rendering with inline
You also should rename your params variable, since that is used by Rails for the incoming parameters. All in all I think a tutorial on MVC would be a good place to start.
Related
I am trying to call external API for my project and I have some troubles while using Net::HTTP in my rails lib . Here is my code
class ApiCall
def self.do_api_request(api_token, body)
require 'net/http'
require 'uri'
uri = URI.parse('https://sample.com')
header = {'Token' => api_token, 'Content-Type' => 'application/json', 'Accept' => 'application/json'}
request = Net::HTTP::Post.new(uri.request_uri, header)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == "https")
request.body = body
http.request(request)
end
end
This is how I use it (assume I knew the api_token and body):
body = {'id' => 2, 'age'=> 23};
ApiCall.do_api_request(api_token, body)
This way, it throws an error back:
NoMethodError: undefined method `bytesize' for Hash
Then after check online, seems like the body is hash instead of string, so I did this
body = URI.encode_www_form(body) and after rerun, it gives me :
400 bad request
I have no ideas how to put both header and body into a rails Net::HTTP method
Solution:
I know where the problem is. request body supposed to be string
so body = "{'id' : 2, 'age' : 23}" , I used body.to_json
I will suggest you to use HTTParty for calling an api. This is real simple to use. Following are the examples-
HTTParty.get("https://api.bigcommerce.com/stores/"+#store.store_hash+"/v3/catalog/categories", :headers => #your_header_data)
This will return the response. Also for post request,
HTTParty.post("https://api.bigcommerce.com/stores/"+#store.store_hash+"/v3/catalog/products", :headers => #auth, :body => product_json)
So you can pass body to in body param here.
I am trying to perform an HTTP authorization using Ruby on Rails. Here is what I'm trying:
res = http.post(uri.request_uri,
:Authorization => cobSessionToken,
"coBrandSessionCredential=loginToken=#{cobSessionToken}&userLogin=#{login}&userPassword=#{password}")
render :json => {"isValid" => true, "Body" => JSON.parse(res.body)}
This doesn't seem to work. How can I perform an authorization?
how about something like this?
url = URI.parse('https://my.url.com/path')
req = Net::HTTP::Post.new(url.path)
req.basic_auth 'user', 'pass'
req.use_ssl = true
req.form_data({'key1' => 'val1', 'key2' => 'val2'})
resp = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
puts resp
I would recommend using something like postman (its a free google program you can get at the google store) to make sure the error is not on the server side. Use Net:http it comes with ruby so you do not need to install it but you have to require it.
Require it by:
require "net/http"
require "uri"
Use this cheatsheet I think you need basic_auth.rb You will see how to form the request.
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.
Is there a way to check for an HTTPS status code in ruby? I know that there are ways to do this in HTTP using require 'net/http', but I'm looking for HTTPS. Maybe there is a different library that I need to use?
You can do this in net/http:
require "net/https"
require "uri"
uri = URI.parse("https://www.secure.com/")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri.request_uri)
res = http.request(request)
res.code #=> "200"
Refs:
Net::HTTP cheat sheet
How to Cure Net::HTTP’s Risky Default HTTPS Behavior
You can use any wrapper around Net::HTTP(S) to get much easier behavior.
I use Faraday here ( https://github.com/lostisland/faraday ) but HTTParty has almost the same functionality ( https://github.com/jnunemaker/httparty )
require 'faraday'
res = Faraday.get("https://www.example.com/")
res.status # => 200
res = Faraday.get("http://www.example.com/")
res.status # => 200
(as a bonus you get options for parsing responses, raising state exceptions, logging requests....
connection = Faraday.new("https://www.example.com/") do |conn|
# url-encode the body if given as a hash
conn.request :url_encoded
# add an authorization header
conn.request :oauth2, 'TOKEN'
# use JSON to convert the response into a hash
conn.response :json, :content_type => /\bjson$/
# ...
conn.adapter Faraday.default_adapter
end
connection.get("/")
# GET https://www.example.com/some/path?query=string
connection.get("/some/path", :query => "string")
# POST, PUT, DELETE, PATCH....
connection.post("/some/other/path", :these => "fields", :will => "be converted to a request string in the body"}
# add any number of headers. in this example "Accept-Language: en-US"
connection.get("/some/path", nil, :accept_language => "en-US")
require 'uri'
require 'net/http'
res = Net::HTTP.get_response(URI('http://www.example.com/index.html'))
puts res.code # -> '200'
Slightly more readable way:
response.kind_of?(Net::HTTPOK)
I need to send different request with headers and body to PayPal. I want to use standatd class NET::HTTP, so here is my code(ISN'T WORKING):
require "net/http"
require "uri"
header = {...}
body = {...}
url = "https://svcs.sandbox.paypal.com/AdaptiveAccounts/GetVerifiedStatus"
uri = URI.parse(url)
args = { 'header' => header,'body' => body }
res = Net::HTTP.post_form(uri, args)
puts res.status
Gives me error:
C:/Ruby193/lib/ruby/1.9.1/net/protocol.rb:141:in `read_nonblock': An existing connection was forcibly closed by the remote host. (Errno::ECONNRESET)
EDIT
Second variant:
require 'httpclient'
require 'xmlsimple'
header = {"X-PAYPAL-SECURITY-USERID" => "tok261_biz_api.abc.com",
"X-PAYPAL-SECURITY-PASSWORD" => "1244612379",
"X-PAYPAL-SECURITY-SIGNATURE" => "lkfg9groingghb4uw5",
"X-PAYPAL-REQUEST-DATA-FORMAT" => "NV",
"X-PAYPAL-RESPONSE-DATA-FORMAT" => "XML",
"X-PAYPAL-APPLICATION-ID" => "APP-80W284485P519543T"
}
#data to be sent in the request
data = {"emailAddress" => "denmed_1342605975_biz#gmail.com",
"firstName"=> "Den",
"lastName" => "Med",
"matchCriteria"=> "NAME",
"requestEnvelope.errorLanguage" => "en_US"}
#initialize the request
clnt = HTTPClient.new
#API end point(sandbox)
uri = "https://svcs.sandbox.paypal.com/AdaptiveAccounts/GetVerifiedStatus"
#make the post
res = clnt.post(uri, data, header)
if res.status == 200
#xml = XmlSimple.xml_in(res.content)
if #xml['accountType']!=nil
account_type = #xml['accountType'][0]
#its pretty obvious from here init?
if account_type.to_s() == "Business"
puts "Business account!"
elseif account_type.to_s() == "Premier"
puts "Premier Account!"
end
elseif account_type.to_s() == "Personal"
puts "Personal account!"
else
puts "Account type not null but not a valid PayPal account type."
end
else
puts "Gee! sorry! something went seriously wrong"
end
This method - constantly gives me - Account type not null but not a valid PayPal account type.
But it is verified in Sandbox ! Tried to leave blank field, but it gave me the same !
Thanks for help in advance !
I am not familiar with your second code example, however I have used the first before (Net::HTTP) and I find it works quite well. I have generally only used it for GET, but I will try to advise on POST :)
First Thing: Headers should not be set as data to be sent with the request (which it appears you are doing) - instead, each header should be set as an attribute of the request:
request['X-HEADER_NAME'] = header_value
Here is how I suggest your code block should look if using Net::HTTP:
require "net/http"
require "uri"
url = "https://svcs.sandbox.paypal.com/AdaptiveAccounts/GetVerifiedStatus"
uri = URI.parse(url)
req = Net::HTTP::Post.new(url)
req["X-PAYPAL-SECURITY-USERID"] = "tok261_biz_api.abc.com"
req["X-PAYPAL-SECURITY-PASSWORD"] = "1244612379"
req["...."] = "...." .... etc for all headers
req.set_form_data( {"emailAddress" => "denmed_1342605975_biz#gmail.com",
"firstName"=> "Den",
"lastName" => "Med",
"matchCriteria"=> "NAME",
"requestEnvelope.errorLanguage" => "en_US"} )
Net::HTTP.start(uri.host, uri.port) do |http|
response = http.request(req)
return response
end
I suggest giving something like that a try, and if it doesn't work, can you give me the exact wording of the given error?
Note
Another suggestion is that, whenever my application receives Errno::ECONNRESET, it means that the back end server is not reachable (in this case I guess that would be the paypal server) - Are you positive the server is running? And are you positive there are no firewalls or anything in place which are preventing you from connecting?