I'm trying to connect to an API and retrieve the json results with my rails app, however it doesn't seem to work.
Take for example:
#request = Net::HTTP::Get.new "http://example.com/?search=thing&format=json"
When I try the url in my browser it works! and I get JSON data, however when I try that in Ruby the body is nil.
>> y #request
--- !ruby/object:Net::HTTP::Get
body:
body_stream:
header:
accept:
- "*/*"
user-agent:
- Ruby
method: GET
path: http://example.com/?search=thing&format=json
request_has_body: false
response_has_body: true
Any thoughts?
I usually use open-uri
require 'open-uri'
content = open("http://your_url.com").read
`
You need to actually send the request and retrieve a response object, like this:
response = Net::HTTP.get_response("example.com","/?search=thing&format=json")
puts response.body //this must show the JSON contents
Regards!
PS: While using ruby's HTTP lib, this page has some useful examples.
Related
I'm new to Rails and I'm using rest-client to make outbound requests. It's easy to get successful results for a simple call like this:
#obj = RestClient.get 'https://jsonplaceholder.typicode.com/posts/1'
I need to hit a real endpoint and send a header with a jwt token (using ruby-jwt). According to the JWT readme, the content of the header should look like this:
Authorization: Bearer <token>
So I have some code to use a secret to make that token (and I confirmed the resulting token is valid) and put it into a headers variable, but I'm unsure about the syntax on that headers line, and whether it's right to use strings:
def build_headers (secret)
jwt_token = make_signed_JWT_token(secret)
headers = ("Authorization: Bearer "+ jwt_token)
return headers
end
Running it produces a 'headers' value like this:
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIxMTc3MjF9.dP2k1oPwjna5HdrnFeSqiVfR0Fz6J1ZlupfXMsPtFKw
I include that in my rest-client invocation like so:
#obj = RestClient.get(path, headers)
I'm no longer get a 401 Unauthorized error, but no celebrating yet; we appear to jump out of the code block at that line, with this exception:
undefined method `delete_if' for #<String:0x007f9e4de410b8>
Looking in the rest-client code, there is exactly one block that uses delete_if, whose purpose is to find and extract/remove "params" key if the value is a Hash/ParamsArray:
headers.delete_if do |key, value|
if key.to_s.downcase == 'params' &&
(value.is_a?(Hash) || value.is_a?(RestClient::ParamsArray))
if url_params
raise ArgumentError.new("Multiple 'params' options passed")
end
url_params = value
true
else
false
end
So my error suggests that it found something in this forbidden format and is trying to delete it, but that delete method isn't defined to act on a String. My best hunch is that there's something the matter with that headers item I created, but reading around I'm not finding more clues. Has anyone seen this before, or know if my jwt/header should be different?
You are passing a string where a hash is expected. Try this:
def build_headers (secret)
jwt_token = make_signed_JWT_token(secret)
headers = { authorization: "Bearer "+ jwt_token }
return headers
end
I'm using Net::HTTP with Ruby to crawl an URL.
I don't want to crawl streaming audio such as: http://listen2.openstream.co/334
in fact i only want to crawl Html content, so no pdfs, video, txt..
Right now, I have both open_timeout and read_timeout set to 10, so even if I do crawl these streaming audio pages they will timeout.
url = 'http://listen2.openstream.co/334'
path = uri.path
req= Net::HTTP::Get.new(path, {'Accept' => '*/*', 'Content-Type' => 'text/plain; charset=utf-8', 'Connection' => 'keep-alive','Accept-Encoding' => 'Identity'})
uri = Addressable::URI.parse(url)
resp = Net::HTTP.start(uri.host, uri.inferred_port) do |httpRequest|
httpRequest.open_timeout = 10
httpRequest.read_timeout = 10
#how can I read the headers here before it's streaming the body and then exit b/c the content type is audio?
httpRequest.request(req)
end
However, is there a way to check the header BEFORE I read the body of a http response to see if it's an audio? I want to do so without sending a separate HEAD request.
net/http supports streaming, you can use this to read the header before the body.
Code example,
url = URI('http://stackoverflow.com/questions/41306082/ruby-nethttp-read-the-header-before-the-body-without-head-request')
Net::HTTP.start(url.host, url.port) do |http|
request = Net::HTTP::Get.new(url)
http.request(request) do |response|
# check headers here, body has not yet been read
# then call read_body or just body to read the body
if true
response.read_body do |chunk|
# process body chunks here
end
end
end
end
I will add a ruby example later tonight. However, for a quick response. There is a simple trick to do this.
You can use HTTP Range header to indicate if which range of bytes you want to receive from the server. Here is an example:
curl -XGET http://www.sample-videos.com/audio/mp3/crowd-cheering.mp3 -v -H "Range: bytes=0-1"
The above example means the server will return data from 0 to 1 byte range.
See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
Since I did not find a way to properly do this in Net::HTTP, and I saw that you're using the addressable gem as an external dependency already, here's a solution using the wonderful http gem:
require 'http'
response = HTTP.get('http://listen2.openstream.co/334')
# Here are the headers
puts response.headers
# Everything ok? Start streaming the response
body = response.body
body.stream!
# now just call `readpartial` on the body until it returns `nil`
# or some other break condition is met
Sorry if you're required to use Net::HTTP, hopefully someone else will find an answer. A separate HEAD request might indeed be the way to go in that case.
You can do a whole host of net related things without using a gem. Just use the net/http module.
require 'net/http'
url = URI 'http://listen2.openstream.co/334'
Net::HTTP.start(url.host, url.port){|conn|
conn.request_get(url){|resp|
resp.each{|k_header, v_header|
# process headers
puts "#{k_header}: #{v_header}"
}
#
# resp.read_body{|body_chunk|
# # process body
# }
}
}
Note: while processing headers, just make sure to check the content-type header. For audio related content it would normally contain audio/mpeg value.
Hope, it helped.
In my rails (3.2.13) app I send data to an external server using a form, then the external server process the data I sent and shows that the result is ok or not, I need to save that result or status to my rails app database, but I'm not sure about how to redirect to another page when the process in the external server is done.
I have a function to ask the server if the process of that data went ok using the reference or id that I sent in the first place using the form but as I said I don't know how to redirect after the process is finish...
please help me
You can use some core Ruby libraries to make a subsequent request on the same endpoint to determine the status code of your request. Try the following, cited in whole from Ruby Inside:
# Basic REST.
# Most REST APIs will set semantic values in response.body and response.code.
require "net/http"
http = Net::HTTP.new("api.restsite.com")
request = Net::HTTP::Post.new("/users")
request.set_form_data({"users[login]" => "quentin"})
response = http.request(request)
# Use nokogiri, hpricot, etc to parse response.body.
request = Net::HTTP::Get.new("/users/1")
response = http.request(request)
# As with POST, the data is in response.body.
request = Net::HTTP::Put.new("/users/1")
request.set_form_data({"users[login]" => "changed"})
response = http.request(request)
request = Net::HTTP::Delete.new("/users/1")
response = http.request(request)
Once you've instantiated a response object, you can operate on it in the following manner:
response.code #=> returns HTTP response code
I am trying to send some json data from my controller written in rails to a java webservice.
On form submission i take all the input fields data do some procession on it and convert it into json object using to_json.
But how can i send it to java webservice
http://localhost:8080/exim/jsonToMapService?jsonData={"key":"value"}
You can use net/http. (as #Pierre wrote, you should create a class in lib folder, and put there your function)
url = URI.parse(service_url)
headers = {"host" => URL }
req = Net::HTTP::Post.new(url.path)
req["Content-Type"] = "application/json"
req["Accept"] = "application/json"
req.body = JSON.generate(some_data)
con = Net::HTTP.new(url.host, url.port)
# ssl for https
if full_url.include?("https")
con.use_ssl = true
con.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
res = con.start {|http| http.request(req) }
To do things like this I suggest using either RestClient or Faraday. Howeve I strongly suggest not doing the HTTP call in your controller.
Using RestClient, it would look like this:
RestClient.get('http://localhost:8080/exim/jsonToMapService', { key: :value })
You should create a class to extract this logic in the lib folder for example.
As #eightbitraptor mentioned it, when performing HTTP request like above, you should avoid blocking by performing them in a background process like Delayed Job, Resque or Sideqik.
In Rail my final goal is to write a Net::HTTP client to connect to my REST API that is returning JSON and parse it, pass it to View , etc....
But first things first!
What is the simplest thing I can start with?
I am looking at this page:http://ruby-doc.org/stdlib-1.9.3/libdoc/net/http/rdoc/Net/HTTP.html
and I get the impression that if I have one .rb file with these two lines of code in it, it should show me something?
require 'net/http'
Net::HTTP.get('example.com', '/index.html')
url = URI.parse("http://example.com")
req = Net::HTTP::Get.new(url.path)
#resp = Net::HTTP.new(url.host, url.port).start {|http| http.request(req)}
in a view
<%= "The call to example.com returned this: #{#resp}" %>
You could start testing with something like this:
require 'net/http'
response = Net::HTTP.get_response("www.google.com","/")
puts response.body
I'll recommend you take a look at the docs: Net::HTTPSession