How to invoke HTTP POST method over SSL in ruby?

2020-01-29 04:24发布

So here's the request using curl:

curl -XPOST -H content-type:application/json -d "{\"credentials\":{\"username\":\"username\",\"key\":\"key\"}}" https://auth.api.rackspacecloud.com/v1.1/auth

I've been trying to make this same request using ruby, but I can't seem to get it to work.

I tried a couple of libraries also, but I can't get it to work. Here's what I have so far:

uri = URI.parse("https://auth.api.rackspacecloud.com")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new("/v1.1/auth")
request.set_form_data({'credentials' => {'username' => 'username', 'key' => 'key'}})
response = http.request(request)

I get a 415 unsupported media type error.

4条回答
地球回转人心会变
2楼-- · 2020-01-29 04:42

You are close, but not quite there. Try something like this instead:

uri = URI.parse("https://auth.api.rackspacecloud.com")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new("/v1.1/auth")
request.add_field('Content-Type', 'application/json')
request.body = {'credentials' => {'username' => 'username', 'key' => 'key'}}.to_json
response = http.request(request)

This will set the Content-Type header as well as post the JSON in the body, rather than in the form data as your code had it. With the sample credentials, it still fails, but I suspect it should work with real data in there.

查看更多
时光不老,我们不散
3楼-- · 2020-01-29 04:49

All others are too long here is a ONE LINER:

Net::HTTP.start('auth.api.rackspacecloud.com', :use_ssl => true).post(
      '/v1.1/auth', {:credentials => {:username => "username",:key => "key"}}.to_json,
      initheader={'Content-Type' => 'application/json'}
    )

* to_json needs require 'json'


OR if you want to

  • NOT verify the hosts
  • be more readable
  • ensure the connection is closed once you're done

then:

ssl_opts={:use_ssl => true, :verify_mode => OpenSSL::SSL::VERIFY_NONE}
Net::HTTP.start('auth.api.rackspacecloud.com', ssl_opts) { |secure_connection|
  secure_connection.post(
      '/v1.1/auth', {:credentials => {:username => "username",:key => "key"}}.to_json,
      initheader={'Content-Type' => 'application/json'}
    )
}

In case it's tough to remember what params go where:

  • SSL options are per connection so you specify them while opening the connection.
  • You can reuse the connection for multiple REST calls to same base url. Think of thread safety of course.
  • Header is a "request header" and hence specified per request. I.e. in calls to get/post/patch/....
  • HTTP.start(): Creates a new Net::HTTP object, then additionally opens the TCP connection and HTTP session.
  • HTTP.new(): Creates a new Net::HTTP object without opening a TCP connection or HTTP session.
查看更多
趁早两清
4楼-- · 2020-01-29 04:51

There's a very good explanation of how to make a JSON POST request with Net::HTTP at this link.

I would recommend using a library like HTTParty. It's well-documented, you can just set up your class like so:

class RackSpaceClient
  include HTTParty

  base_uri "https://auth.api.rackspacecloud.com/"
  format :json
  headers 'Accept' => 'application/json'

  #methods to do whatever

end

It looks like the main difference between the Ruby code you placed there, and the curl request, is that the curl request is POSTing JSON (content-type application/json) to the endpoint, whereas request.set_form_data is going to send a form in the body of the POST request (content-type application/x-www-form-urlencoded). You have to make sure the content going both ways is of type application/json.

查看更多
对你真心纯属浪费
5楼-- · 2020-01-29 05:05

Another example:

#!/usr/bin/ruby

require 'net/http'
require 'json'
require 'uri'

full_url = "http://" + options[:artifactory_url] + "/" + "api/build/promote/" + options[:build]

puts "Artifactory url: #{full_url}"

data = {
    status: "staged",
    comment: "Tested on all target platforms.",
    ciUser: "builder",
    #timestamp: "ISO8601",
    dryRun: false,
    targetRepo: "#{options[:target]}",
    copy: true,
    artifacts: true,
    dependencies: false,
    failFast: true,
}

uri = URI.parse(full_url)
headers = {'Content-Type' => "application/json", 'Accept-Encoding'=> "gzip,deflate",'Accept' => "application/json" }
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri, headers)
request.basic_auth(options[:user], options[:password])
request.body = data.to_json
response = http.request(request)

puts response.code
puts response.body
查看更多
登录 后发表回答