Unauthorized readbody=true when posting message to hangout chat room with webhook - ruby

Trying to post a message to a hangout chat room. I've generated a webhook for the room and used it as uri in the following code. The rest are basic net/http stuff.
require 'net/http'
require 'uri'
message = 'hello'
# prep and send the http request
uri = URI.parse("https://chat.googleapis.com/v1/spaces/AAAAcroWtl4/messages?key=abc&token=xyz")
request = Net::HTTP::Post.new(uri)
request.content_type = "application/json"
request.body = '"content":[{"type":"text","text":"'+message+'"}]'
req_options = { use_ssl: uri.scheme == "https" }
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
puts response.inspect
The response contains the following text.
#<Net::HTTPUnauthorized 401 Unauthorized readbody=true>
Is there something wrong with the request body?
Edit: key and token changed in the question.
This same code worked with stride rooms, the only difference is that it had
request["Authorization"] = "Bearer #{access_token}"
Since the token is already in the uri I figured this would not be needed.

This worked for me
require 'net/http'
require 'uri'
require 'json'
message = 'hello'
# prep and send the http request
uri = URI.parse("//webhook")
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = "application/json"
request.body = { text: message }.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.inspect

Related

Ruby - HTTPS get calls with headers

Have a query on making a https Get call with headers. I tried the following
uri = URI.parse("https:\\www.someurl.com")
req = Net::HTTP::Get.new(uri)
# Following is where I am adding the header
req['Auth'] = "Bearer" + #token
response = Net::HTTP.start(uri.host,uri.port) do |http|
request = Net::HTTP::Get.new uri.request_uri
http.request request
end
puts(response.body)
I see the following error
400 The plain HTTP request was sent to HTTPS port
I tried searching and what I have found is that Net::HTTP.get supports https calls but without headers.
The call that I am making is via API gateway, so I will have to add a token in the header.
Any help is much appreciated. Thanks in advance.
You need to use use_ssl: true.
uri = URI('https://secure.example.com/some_path?query=string')
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Get.new(uri)
request['Authentication'] = "Bearer %s" % #token
http.request(request)
end

'JSON.parse' error handling invalid/non JSON format

I request information from an HTTP streaming service. It provides data in JSON format. Here is the documentation. Here is a part of the code I am using:
require 'uri'
require 'net/https'
require 'json'
uri = URI("https://api.tradier.com/v1/markets/events/session")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
http.read_timeout = 30
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
# Headers
request["Accept"] = "application/json"
request["Authorization"] = "Bearer xxx"
# Send synchronously
response = http.request(request)
# parses response
parse = JSON.parse(response.body)
#out puts values only from response
sessionid = parse.values[0]["sessionid"]
url = parse.values[0]["url"]
uri = URI("#{url}?sessionid=#{sessionid}&symbols=aapl")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
http.read_timeout = 30
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
# Headers
request["Accept"] = "application/json"
request["Authorization"] = "Bearer xxx"
http.request request do |response|
response.read_body do |data|
puts data.class
# info = JSON.parse(data, :quirks_mode => true)
# puts info.values
end
end
I want to have the system continue the program. It seems that I need to use begin and rescue, but I cannot get them to work. When I request data, I get the following error:
`parse': 757: unexpected token at '{"type":"trade","symbol":"AAPL","exch":"Q","price":"191.23","size":"1081622","cvol":"18308460","date":"1528747200000","last":"191.23"}{"type":"summary","symbol":"AAPL","open":"191.35","high":"191.97","low":"190.21","prevClose":"191.7","close":"191.23"}' (JSON::ParserError)
The endpoint you are using is documented in Tradier API docs and it's a streaming endpoint.
It appears that the response is not chunked to contain just one JSON document per chunk. It however does appear that the documents are separated by linefeeds, making the response look like:
{ "json": "data" }
{ "more": "data" }
And that is not valid JSON. You probably need to parse them one by one by doing something like:
http.request request do |response|
response.read_body do |data|
data.each_line do |chunk|
info = JSON.parse(chunk)
puts info.inspect
end
end
end
If the response chunking happens in the middle of JSON documents, you must use some kind of buffered reader.

An error has occurred that says {"code":"BadArgument","requestId":"XXXX","message":"JSON format error."} on computer vision api of Azure

require 'net/http'
uri = URI('https://westus.api.cognitive.microsoft.com/vision/v1.0/analyze')
uri.query = URI.encode_www_form({
# Request parameters
'visualFeatures' => 'Adult',
'details' => 'Celebrities',
'language' => 'en'
})
request = Net::HTTP::Post.new(uri.request_uri)
# Request headers
request['Content-Type'] = 'application/json'
# Request headers
request['Ocp-Apim-Subscription-Key'] = 'caa91cccdgywtduwdxxf530'
# Request body
request.body = "http://instudy.jp/wp-content/uploads/2015/03/doraemon-coming.jpg"
response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
http.request(request)
end
puts response.body
I wanted to check the photo whether adult things or not so I wrote the code.
I think there is nothing wrong so I don't know why error's occurred.
environment: ruby 2.3.1
As per the documentaion, the vision api is expecting an image url in 'url' key of request body. Also in your case, you are not passing a valid json as request body. So, replace
request.body = "http://instudy.jp/wp-content/uploads/2015/03/doraemon-coming.jpg"
with
request_body = Hash.new
request_body[:url] = "http://instudy.jp/wp-content/uploads/2015/03/doraemon-coming.jpg"
request.body = request_body.to_json

How to use awesome_print for Ruby file that makes a HTTP request

This is my code:
require 'net/https'
uri = URI('https://api.clever.com/v1.1/sections')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
request = Net::HTTP::Get.new(uri.request_uri)
request.add_field 'Authorization', 'Bearer DEMO_TOKEN'
response = http.request(request)
puts response.body
The problem is that my code output is gross and hard to read in terminal. I'm trying to clean it up with awesome print but it isn't working... this is what I'm trying:
require 'net/https'
require 'awesome_print'
uri = URI('https://api.clever.com/v1.1/sections')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
request = Net::HTTP::Get.new(uri.request_uri)
request.add_field 'Authorization', 'Bearer DEMO_TOKEN'
response = http.request(request)
ap response.body
but it's not formatting at all the way I need it to. Any idea what's going on?
The problem is that you need to print the Hash, not the raw String. So use JSON.parse(response.body) would solve your problem.
Alternatively, use pp and json, they are all from stdlib.
require 'net/https'
require 'pp'
require 'json'
uri = URI('https://api.clever.com/v1.1/sections')
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Get.new(uri)
request["authorization"] = "Bearer DEMO_TOKEN"
http.request(request) do |response|
pp JSON.parse(response.body)
end
end
But ultimately, I would recommend to use pry for debugging. It just make life easier 10x for debuging.
gem install pry
Then change above code to:
require 'net/https'
require 'pry'
require 'json'
uri = URI('https://api.clever.com/v1.1/sections')
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Get.new(uri)
request["authorization"] = "Bearer DEMO_TOKEN"
http.request(request) do |response|
res = JSON.parse(response.body)
binding.pry
end
end
After running the file in your terminal, it will paused at where you put binding.pry. Then type res, you will see the nicely formatted hash.
Have fun with pry!
parse your response.body to JSON and use pretty_generate() function, built into later versions of JSON.
require 'net/https'
require 'json'
uri = URI('https://api.clever.com/v1.1/sections')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
request = Net::HTTP::Get.new(uri.request_uri)
request.add_field 'Authorization', 'Bearer DEMO_TOKEN'
response = http.request(request)
myjson = JSON.parse(response.body)
puts JSON.pretty_generate(myjson)
Which will give you output:
{
"data": {
"course_name": "Fine Arts, Class 703",
"course_number": "703",
"created": "2014-02-26T21:15:38.324Z",
"district": "4fd43cc56d11340000000005",
"grade": "7",
"last_modified": "2015-09-30T21:08:09.877Z",
"name": "Fine Arts, Class 703 - 703 - A. Ortiz (Section 3)",
"period": "7",
"school": "530e595026403103360ff9ff",
"sis_id": "674",
"students": [
"530e5960049e75a9262cff59",
"530e5960049e75a9262cff99",
"530e5961049e75a9262cffd5",
"530e5961049e75a9262d001c",
"530e5961049e75a9262d008a",
"530e5962049e75a9262d0144",
"530e5962049e75a9262d0155",
"530e5962049e75a9262d015e",
"530e5963049e75a9262d0200",
"530e5963049e75a9262d022d",
"530e5963049e75a9262d023a",
"530e5964049e75a9262d0275",
"530e5964049e75a9262d029b",
"530e5964049e75a9262d02c0",
"530e5964049e75a9262d02de",
"530e5965049e75a9262d034a",
"530e5965049e75a9262d0354",
"530e5965049e75a9262d03c7",
"530e5966049e75a9262d0419",
"530e5966049e75a9262d046d",
"530e5966049e75a9262d0489",
"530e5967049e75a9262d0560",
"530e5967049e75a9262d05b4",
"530e5967049e75a9262d05bb",
"530e5968049e75a9262d0621",
"530e5968049e75a9262d0637"
],
"subject": "arts and music",
"teacher": "530e5955d50c310f36112bec",
....
....
# I have not post full output but it's pretty good and well structured
awesome print is print your Ruby data structures (Hash, Array etc.) in an easy to read format. Not for HTML!
If you want to format HTML in an easy to read manner, take a look at Nokogiri. Example:
require 'nokogiri'
# your response html
html = response.body
doc = Nokogiri::XML(html,&:noblanks)
puts doc.to_xhtml(indent:4)

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