Ruby Http Post Parameters - ruby

How can I add post parameters to what I have right now:
#toSend = {
"nonce" => Time.now.to_i,
"command" => "returnCompleteBalances"
}.to_json
uri = URI.parse("https://poloniex.com/tradingApi")
https = Net::HTTP.new(uri.host,uri.port)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
req = Net::HTTP::Post.new(uri.path, initheader = {'Content-Type' =>'application/json'})
req.set_form_data({"nonce" => Time.now.to_i, "command" => "returnCompleteBalances"})
req['Key'] = '******-N4WZI2OG-******-10RX5JYR'
req['Sign'] = 'secret_key'
req.body = "[ #{#toSend} ]"
res = https.request(req)
puts "Response #{res.code} #{res.message}: #{res.body}"
These are the params I want to send:
"nonce" => Time.now.to_i,
"command" => "returnCompleteBalances"
Thank you.

It appears that you're trying to use Poloniex's trading API. If this is your primary goal, you might wish to consider using a library to handle the nitty-gritty details. For example:
https://github.com/Lowest0ne/poloniex
If your primary goal is not simply to use the API, but to use this as a learning experience, here are a few pointers:
The API documentation indicates that the API accepts form-encoded POST data (not JSON) but responds with JSON.
The key parameter ("Key") is like your user id. It allows Poloniex to understand who is attempting to make a request against the API.
The signature parameter ("Sign") is an HMAC generated from the contents of your secret key and the contents of your message (the encoded form data). This produces a sort of fingerprint that only you and Poloniex have the information to reproduce, giving some level of assurance that your request originated from the owner of the secret key. Of course, this assumes that your secret key is indeed only known by you.
I don't use the Poloniex exchange and cannot test this code, but I believe this is close to what you're attempting to accomplish:
require 'net/http'
require 'openssl'
secret = 'your-secret-key'
api_key = 'your-api-key'
uri = URI('https://poloniex.com/tradingApi')
http = Net::HTTP.new(uri.host)
request = Net::HTTP::Post.new(uri.request_uri)
form_data = URI.encode_www_form({:command => 'returnBalances', :nonce => Time.now.to_i * 1000 })
request.body = form_data
request.add_field('Key', api_key)
request.add_field('Sign', OpenSSL::HMAC.hexdigest( 'sha512', secret, form_data))
res = http.request(request)
puts res.body

Related

HTTP GET request with session key

I'm a bit of newbie regarding HTTP GET/POST request. I want to use a get request that requires some kind of authorization.
I'm trying to use the following api API DOCUMENTATION.
Under "get list" it says that it wants the following parameters:
Parameters
- Accept-Language: Language prefered in the response. Note: nb and nn will return the same as no header string
- Authorization: Basic auth. The session_id should be sent as both username and password header string
I use the following code to authorize myself, but the last "GET requests" gives an error:
require 'openssl'
require 'base64'
require 'uri'
require 'net/http'
require 'json'
username = 'MYUSERNAME'
password = 'MYPASSWORD'
service = 'NEXTAPI'
# Create auth
string = Base64.encode64(username) + ':' + Base64.encode64(password) + ':' + Base64.encode64((Time.now.\
to_i * 1000).to_s)
public_key_data = File.read(service + '_TEST_public.pem')
public_key = OpenSSL::PKey::RSA.new(public_key_data)
auth = URI::escape(Base64.encode64(public_key.public_encrypt(string)),
Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
# Setup HTTPS
http = Net::HTTP.new('api.test.nordnet.se', 443)
http.use_ssl = true
# Get status of server
response = http.get('/next/2/', {'Accept' => 'application/json'})
puts response.body
# POST login
response = http.post('/next/2/login', "auth=#{auth}&service=#{service}", {'Accept' => 'application/json'})
puts response.body
data = JSON.parse(response.body)
session_key = data['session_key']
auth_string = "Basic " + session_key + ":" + session_key
response = http.get('/next/2/lists', {'Authorization' => auth_string })
puts response
Not sure what is going wrong here. Or what I need to do. I get the following error.
#<Net::HTTPNotAcceptable:0x007fac74276d20>
Question 1: How do I properly send my session key as both username and password?
Question 2: How would I actually send parameters and headers, and what are the differences?
Question 3: Is there a difference in what will be needed in regards to headers/parameters depending on if I send a GET or POST request?
Thanks
Was able to solve it. Below is the OK code...
require 'openssl'
require 'base64'
require 'uri'
require 'net/https'
require 'json'
require 'cgi'
username = 'USERNAME'
password = 'PASSWORD'
service = 'NEXTAPI'
def http_get(path,params)
http = Net::HTTP.new('api.test.nordnet.se', 443)
http.use_ssl = true
#return http.get("#{path}?", params)
return http.get("#{path}?")
#return Net::HTTP.get(path)
end
# Create auth
string = Base64.encode64(username) + ':' + Base64.encode64(password) + ':' + Base64.encode64((Time.now.\
to_i * 1000).to_s)
public_key_data = File.read(service + '_TEST_public.pem')
public_key = OpenSSL::PKey::RSA.new(public_key_data)
auth = URI::escape(Base64.encode64(public_key.public_encrypt(string)),
Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
# Setup HTTPS
http = Net::HTTP.new('api.test.nordnet.se', 443)
http.use_ssl = true
# Get status of server
response = http.get('/next/2/', {'Accept' => 'application/json'})
puts response.body
# POST login
response = http.post('/next/2/login', "auth=#{auth}&service=#{service}", {'Accept' => 'application/json'})
puts response.body
data = JSON.parse(response.body)
session_key = data['session_key']
uri = URI('https://api.test.nordnet.se:443/next/2/lists')
Net::HTTP.start(uri.host, uri.port,
:use_ssl => uri.scheme == 'https',
:verify_mode => OpenSSL::SSL::VERIFY_NONE) do |http|
request = Net::HTTP::Get.new uri.request_uri
request.add_field('Accept', 'application/json')
request.basic_auth session_key, session_key
response = http.request request # Net::HTTPResponse object
puts response.body# puts response.body
end

net/http PUT with body causes problems

I want to use the Cloudera API from ruby. Therefore I want to
update the configuration of the cloudera manager, which is done
with an HTTP PUT request containing some json data.
The URL is http://localhost:7180/api/v11/cm/config, my first approach was the following code:
require 'net/http'
require 'base64'
port = 7180
host = 'localhost'
req = Net::HTTP::Put.new('/api/v11/cm/config')
req.body = '{"items":[{"name":"TSQUERY_STREAMS_LIMIT","value":1000},{"name":"parcel_proxy_server","value":"proxy"},{"name":"parcel_proxy_port","value":"8080"},{"name":"parcel_update_freq","value":"1"}]}'
req['Content-Type'] = 'application/json'
req['Authorization'] = "Basic #{Base64.encode64('admin:admin')}"
client = Net::HTTP.new(host, port)
resp = client.request(req)
puts resp
puts resp.to_hash
puts resp.body
This variant returns a 400 Bad Request response with the message
"message" : "No content to map due to end-of-input\n at [Source: org.apache.cxf.transport.http.AbstractHTTPDestination$1#4561cbc9; line: 1, column: 1]"
If I want to trace this in wireshark with the setting tcp.port == 7180, this request does somehow not show up.
Then I switched to using Net::HTTP.start with the following source code:
require 'net/http'
require 'base64'
port = 7180
host = 'localhost'
req = Net::HTTP::Put.new('/api/v11/cm/config')
req.body = '{"items":[{"name":"TSQUERY_STREAMS_LIMIT","value":1000},{"name":"parcel_proxy_server","value":"proxy"},{"name":"parcel_proxy_port","value":"8080"},{"name":"parcel_update_freq","value":"1"}]}'
req['Content-Type'] = 'application/json'
req['Authorization'] = "Basic #{Base64.encode64('admin:admin')}"
resp = Net::HTTP.start(host, port) { |client| client.request(req) }
puts resp
puts resp.to_hash
puts resp.body
This thing also returned a bad request, but without any body or else. In contrast to the first approach, this one showed up in wireshark but the body it posted was depicted as with Hypertext-Transfer-Protocol. If I do the same request with cURL, the request body is correctly shown as JavaScript Object Notation.
Does anybody know what the problem of my requests is?
Ok so I tried around for several hours but this behavior is rather strange:
To resolve this I first used Net::HTTP.start instead of Net::HTTP.new. Second and even more important:
I used to do basic authentication by setting the header value manually as you could see in
req['Authorization'] = "Basic #{Base64.encode64('admin:admin')}"
But Net::HTTP::Put already provides a basic_auth method. If this one is not used, strange errors as the ones that I got will occur.
So the working version looks like this:
req = Net::HTTP::Put.new('/api/v11/cm/config')
req.body = '{"items":[{"name":"TSQUERY_STREAMS_LIMIT","value":1000},{"name":"parcel_proxy_server","value":"proxy"},{"name":"parcel_proxy_port","value":"8080"},{"name":"parcel_update_freq","value":"1"}]}'
req['Content-Type'] = 'application/json'
req.basic_auth 'admin', 'admin'
resp = Net::HTTP.start(host, port) { |client| client.request(req) }

How to add "Content-type" header in HTTP POST

I keep getting a
400 "Bad Request" (Net::HTTPServerException)
error whenever I try to add a content-type header from various methods.
I've seen several different examples and I can't get any to work. My goal is to add a content type of JSON to my request. Without the header, my request doesn't error:
def post_data(notice)
uri = URI('my uri is here')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Post.new("#{uri.path}?#{uri.query}")
text = notice
req.add_field('Content-Type', 'application/json')
req.body = "{\"sensu_payload\" = #{payload(text).to_json}}"
response = http.request(req)
verify_response(response)
end
I've also tried this method of adding a header:
request = Net::HTTP::Post.new(#path, initheader = {'Content-Type' =>'application/json'})
Use uri.path instead of #path:
request = Net::HTTP::Post.new(uri.path, initheader = {'Content-Type' =>'application/json'})
Instead of add_field, I think you should use the hash [] form:
req['Content-Type'] = 'application/json'
See the "Setting Headers" example in the documentation and []=.
Also, using:
"#{uri.path}?#{uri.query}"
is never a good idea. String concatenation can't manage the complexities of correctly encoding illegal values in a query, which can break a request. Consider doing something like this instead:
require 'uri'
require 'uri'
foo = URI('http://www.example.com') # => #<URI::HTTP http://www.example.com>
foo.query = URI::encode_www_form({'bar' => 'path/to/file', 'baz' => 'this & that'})
foo.to_s # => "http://www.example.com?bar=path%2Fto%2Ffile&baz=this+%26+that"
Beyond that, I'd recommend using any of the other HTTP-client gems available. They make it much easier to deal with unexpected situations, like redirects and retries than Net::HTTP. It's more like the building block for features that aren't available other ways.

Ruby RestClient post request with cookies

I've been trying for over a week now with no joy to post an api request setting the cookies with values from a previous request. First request is fine:
response = RestClient.post ('http://api-qa1:8180/api/rest/GB/session'),'{"email":""}',:content_type => 'application/json'
obj = JSON.parse(response)
id = obj['id']
profileId = obj['profile_id']
#cookies = response.cookies
dyn = obj['verification_id']
jsessionid = #cookies['JSESSIONID']
puts jsessionid,dyn,profileID
I get a response and the values i need, I now want to use the values returned 'profile_id'(URi), jsessionid(cookie), and dyn(cookie) values to form my second request.
res = RestClient.post ("http://api-qa1:8180/api/rest/GB/profile/#{profileId}/cart/item"),
'{
"sku_id":"1234"
"product_id":"1234"
"quantity":"2"
"recommended":"false"
}',
headers = {
:content_type => 'application/json',
:userPrefLanguage => 'en-GB'
}
cookies = {'JSESSIONID' => jsessionid},{'DYN_USER_CONFIRM' => dyn }
I've tried many combinations all to no avail, this is as far as i've got which gives me a 403, im also aware that post request should have a maximum of 3 arguments I just cant get it to work. The cookies properties i need to set are
DYN_USER_CONFIRM and JSESSION.
Just looking at the documentation for RestClient:
response.cookies
# => {"_applicatioN_session_id" => "1234"}
response2 = RestClient.post(
'http://localhost:3000/',
{:param1 => "foo"},
{:cookies => {:session_id => "1234"}}
)
It looks like you need to set cookies and headers as a hash inside of the request. The code you've posted has cookies = .... and it's not inside of the request. You need to set headers in a hash as well I believe.

Ruby send JSON request

How do I send a JSON request in ruby? I have a JSON object but I dont think I can just do .send. Do I have to have javascript send the form?
Or can I use the net/http class in ruby?
With header - content type = json and body the json object?
uri = URI('https://myapp.com/api/v1/resource')
body = { param1: 'some value', param2: 'some other value' }
headers = { 'Content-Type': 'application/json' }
response = Net::HTTP.post(uri, body.to_json, headers)
require 'net/http'
require 'json'
def create_agent
uri = URI('http://api.nsa.gov:1337/agent')
http = Net::HTTP.new(uri.host, uri.port)
req = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
req.body = {name: 'John Doe', role: 'agent'}.to_json
res = http.request(req)
puts "response #{res.body}"
rescue => e
puts "failed #{e}"
end
HTTParty makes this a bit easier I think (and works with nested json etc, which didn't seem to work in other examples I've seen.
require 'httparty'
HTTParty.post("http://localhost:3000/api/v1/users", body: {user: {email: 'user1#example.com', password: 'secret'}}).body
This works on ruby 2.4 HTTPS Post with JSON object and the response body written out.
require 'net/http' #net/https does not have to be required anymore
require 'json'
require 'uri'
uri = URI('https://your.secure-url.com')
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
request.body = {parameter: 'value'}.to_json
response = http.request request # Net::HTTPResponse object
puts "response #{response.body}"
end
real life example, notify Airbrake API about new deployment via NetHttps
require 'uri'
require 'net/https'
require 'json'
class MakeHttpsRequest
def call(url, hash_json)
uri = URI.parse(url)
req = Net::HTTP::Post.new(uri.to_s)
req.body = hash_json.to_json
req['Content-Type'] = 'application/json'
# ... set more request headers
response = https(uri).request(req)
response.body
end
private
def https(uri)
Net::HTTP.new(uri.host, uri.port).tap do |http|
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
end
end
project_id = 'yyyyyy'
project_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
url = "https://airbrake.io/api/v4/projects/#{project_id}/deploys?key=#{project_key}"
body_hash = {
"environment":"production",
"username":"tomas",
"repository":"https://github.com/equivalent/scrapbook2",
"revision":"live-20160905_0001",
"version":"v2.0"
}
puts MakeHttpsRequest.new.call(url, body_hash)
Notes:
in case you doing authentication via Authorisation header set header req['Authorization'] = "Token xxxxxxxxxxxx" or http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html
A simple json POST request example for those that need it even simpler than what Tom is linking to:
require 'net/http'
uri = URI.parse("http://www.example.com/search.json")
response = Net::HTTP.post_form(uri, {"search" => "Berlin"})
I like this light weight http request client called `unirest'
gem install unirest
usage:
response = Unirest.post "http://httpbin.org/post",
headers:{ "Accept" => "application/json" },
parameters:{ :age => 23, :foo => "bar" }
response.code # Status code
response.headers # Response headers
response.body # Parsed body
response.raw_body # Unparsed body
It's 2020 - nobody should be using Net::HTTP any more and all answers seem to be saying so, use a more high level gem such as Faraday - Github
That said, what I like to do is a wrapper around the HTTP api call,something that's called like
rv = Transporter::FaradayHttp[url, options]
because this allows me to fake HTTP calls without additional dependencies, ie:
if InfoSig.env?(:test) && !(url.to_s =~ /localhost/)
response_body = FakerForTests[url: url, options: options]
else
conn = Faraday::Connection.new url, connection_options
Where the faker looks something like this
I know there are HTTP mocking/stubbing frameworks, but at least when I researched last time they didn't allow me to validate requests efficiently and they were just for HTTP, not for example for raw TCP exchanges, this system allows me to have a unified framework for all API communication.
Assuming you just want to quick&dirty convert a hash to json, send the json to a remote host to test an API and parse response to ruby this is probably fastest way without involving additional gems:
JSON.load `curl -H 'Content-Type:application/json' -H 'Accept:application/json' -X POST localhost:3000/simple_api -d '#{message.to_json}'`
Hopefully this goes without saying, but don't use this in production.
The net/http api can be tough to use.
require "net/http"
uri = URI.parse(uri)
Net::HTTP.new(uri.host, uri.port).start do |client|
request = Net::HTTP::Post.new(uri.path)
request.body = "{}"
request["Content-Type"] = "application/json"
client.request(request)
end
data = {a: {b: [1, 2]}}.to_json
uri = URI 'https://myapp.com/api/v1/resource'
https = Net::HTTP.new uri.host, uri.port
https.use_ssl = true
https.post2 uri.path, data, 'Content-Type' => 'application/json'
Using my favourite http request library in ruby:
resp = HTTP.timeout(connect: 15, read: 30).accept(:json).get('https://units.d8u.us/money/1/USD/GBP/', json: {iAmOne: 'Hash'}).parse
resp.class
=> Hash

Resources