36

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.

Kashyap
  • 15,354
  • 13
  • 64
  • 103
NielMalhotra
  • 1,375
  • 2
  • 13
  • 22

4 Answers4

63

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.

rkyr
  • 3,131
  • 2
  • 23
  • 38
Eugene
  • 4,829
  • 1
  • 24
  • 49
  • CAn you help me with this question : http://stackoverflow.com/questions/20771177/post-request-not-working-rest?noredirect=1#comment31132280_20771177 – John Smith Dec 25 '13 at 09:32
  • 4
    I suggested an edit for `.to_json` at the end of the `request.body` line to avoid encoding errors. Thanks for the great answer though! – Math is Hard Nov 13 '14 at 20:05
  • Awesome! Took me forever to find this solution, but thankfully I found this post. –  Mar 20 '15 at 21:17
  • @Eugene In the second last line, why are we passing the parameters in the body.. shouldn't we do that as a Query parameter? Also, I would like to know how to pass the same as query parameter. – Bhavuk Mathur May 16 '16 at 13:09
  • Because POST requests require the content be sent in the body as opposed to query parameters which are mostly just for GET operations, although they are valid for a few others. POST and PUT require a body though. – Eugene May 16 '16 at 22:44
  • @Eugene, My requirement is to send few query parameters in the POST request, For eg - someurl/token?grant_type=client_credentials. How to do that? – Bhavuk Mathur May 17 '16 at 05:54
  • You should be able to do that just as you would for a GET request `?grant_type=client_credentials` but you will need to make sure your `Content-Length` header is only calculated using the body, and not including the parameters set in the query string. If you are having problems, you should ask a new question about it with more details. – Eugene May 17 '16 at 12:32
  • 2
    Please be careful as using `OpenSSL::SSL::VERIFY_NONE` introduces a security flow. You are vulnerable to an [man-in-the-middle attack](https://en.wikipedia.org/wiki/Man-in-the-middle_attack) by skipping the SSL certificate verification. – Capripot Mar 08 '17 at 20:44
4

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.

Jason Lewis
  • 1,314
  • 8
  • 13
3

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
thesecretmaster
  • 1,950
  • 1
  • 27
  • 39
Noamsh
  • 61
  • 3
3

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.
Kashyap
  • 15,354
  • 13
  • 64
  • 103