Rails Net::HTTP form data with custom headers - ruby-on-rails

I am trying to send a file through form_data and set the Content-Type header as the content type of the file (image/png, image/jpeg, etc.) instead of multipart/form-data.
I have tried multiple ways, but here is my method:
def fetch(url, body, headers, limit, use_ssl, use_file_content_type)
raise HTTPRedirectLevelTooDeep, 'HTTP redirect too deep' if limit.zero?
headers[:'Content-Type'] = body['file'].content_type if use_file_content_type
uri = URI(url)
request = Net::HTTP::Post.new(url)
headers.each { |key, value| request[key.to_s] = value }
form_data = body.to_a
request.set_form form_data, 'multipart/form-data'
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl) do |http|
http.request(request)
end
case response
when Net::HTTPSuccess then response
when Net::HTTPRedirection then fetch(response['location'], body, headers, limit - 1)
else
response.error!
end
end
This sends the body just as I want it, but does not set the Content Type header correctly (it uses multipart/form-data). I also tried setting the header after setting the form data:
request['Content-Type'] = body['file'].content_type right before http.request(request) but it sends Content-Type: application/x-www-form-urlencoded.
I also tried setting the header in the Net::HTTP::Post.new method, or moving everything after request = inside the Net::HTTP.start method, but it still does not work for me. I also tried using request.set_form form_data, body['file'].content_type, but that just says invalid enctype: image/jpeg
One thing that sent the headers correctly was replacing this:
form_data = body.to_a
request.set_form form_data, 'multipart/form-data'
with
request.body = body.to_json
But this does not send the file as the external API I'm using expects it.
Do you have any suggestions of how I could tackle this? Thanks!

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]...)

Net/HTTP in rails with request header and body

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.

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 ;)).

Running a HTTP request with 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.

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