Ruby envía solicitud JSON

86

¿Cómo envío una solicitud JSON en ruby? Tengo un objeto JSON pero no creo que pueda hacerlo .send. ¿Tengo que tener javascript para enviar el formulario?

¿O puedo usar la clase net / http en ruby?

¿Con header - content type = json y body el objeto json?

Andy
fuente

Respuestas:

76
uri = URI('https://myapp.com/api/v1/resource')
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
req.body = {param1: 'some value', param2: 'some other value'}.to_json
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(req)
end
CarelZA
fuente
8
Me gusta su sugerencia de usar URI para manejar el nombre de host y el puerto, que de lo contrario es bastante tedioso. Pero olvidó establecer uri.path en Post.new (...):req = Net::HTTP::Post.new(uri.path, initheader = {'Content-Type' =>'application/json'})
ArnauOrriols
1
Respuesta más simple y limpia. Esto es genial.
joelc
http.request(req).read_bodypara leer el cuerpo de la respuesta. ¡Excelente!
iGian
1
Estoy bastante seguro de que ha cambiado en 2.4.1, pero Dios mío. Esa sintaxis es asquerosa. Sabe que tiene el URI de Post.new (), entonces, ¿por qué tiene que volver a pasar los valores, dividir, en start ()? Bruto. No es de extrañar que haya tantos otros paquetes en ruby ​​que se ocupan de http.
Rambatino
50
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
neoneye
fuente
¿Qué excepción debe especificarse
Mio
7
Para solicitudes https, simplemente agregue: http.use_ssl = true.
Habilidad M2
17

HTTParty hace que esto sea un poco más fácil, creo (y funciona con json anidado, etc., que no parece funcionar en otros ejemplos que he visto.

require 'httparty'
HTTParty.post("http://localhost:3000/api/v1/users", body: {user: {email: '[email protected]', password: 'secret'}}).body
Brian Armstrong
fuente
6

ejemplo de la vida real, notifique a la API de Airbrake sobre una nueva implementación a través de 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)

Notas:

en caso de que realice la autenticación a través del encabezado del conjunto de encabezados de autorización req['Authorization'] = "Token xxxxxxxxxxxx" o http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html

equivalente8
fuente
... pero, honestamente, esto es genial y casi en la vida real, solo usaría HTTParty stackoverflow.com/a/14491995/473040 :) ... especialmente si se trata de manejo de https
equivalente8
requerir uri es inútil, ya que net / http lo
requiere
@ equivalente8: "en la vida real, solo usaría HTTParty", es decir, a menos que esté construyendo una joya esbelta, o no quiera otra dependencia. :)
Sergio Tulentsev
@SergioTulentsev está de acuerdo ... a menos que esté construyendo gem / lib (o microservicio basado en Ruby) donde no desee introducir dependencias innecesarias;)
equivalente 8
4

Un ejemplo simple de solicitud POST de json para aquellos que lo necesitan incluso más simple que lo que Tom está vinculando:

require 'net/http'

uri = URI.parse("http://www.example.com/search.json")
response = Net::HTTP.post_form(uri, {"search" => "Berlin"})
Christoffer
fuente
19
Esto parece que debería funcionar, pero post_form traduce los parámetros en la sintaxis? Key = value & key = value. Si desea hacer una POST con el cuerpo de la solicitud configurado en una cadena JSON, creo que necesita una solución diferente.
Ben Gotow
Esto no funciona con json profundamente anidado. Cualquier cosa más allá del primer nivel se convierte en una cuerda.
neoneye
No solo parece. Funciona. Seguro que es simple. pero para cosas simples como el ejemplo que he dado, funciona bien
Christoffer
4
Básicamente, esta no es una solicitud JSON. Este es un cuerpo codificado en URL. No hay JSON. El encabezado incluso dice lo mismo. Esto no funciona con ningún ejemplo, nunca.
raylu
4
Esta respuesta es incorrecta. Es un POST en mime / multipart, a una URL que dice "json" en él.
John Haugeland
4

Es 2020: nadie debería usar Net::HTTPmás y todas las respuestas parecen decirlo, use una gema de más alto nivel como Faraday - Github


Dicho esto, lo que me gusta hacer es un envoltorio alrededor de la llamada a la API HTTP, algo que se llama como

rv = Transporter::FaradayHttp[url, options]

porque esto me permite falsificar llamadas HTTP sin dependencias adicionales, es decir:

  if InfoSig.env?(:test) && !(url.to_s =~ /localhost/)
    response_body = FakerForTests[url: url, options: options]

  else
    conn = Faraday::Connection.new url, connection_options

Donde el farsante se parece a esto

Sé que hay marcos de burla / stubbing HTTP, pero al menos cuando investigué la última vez no me permitieron validar las solicitudes de manera eficiente y eran solo para HTTP, no, por ejemplo, para intercambios TCP sin procesar, este sistema me permite tener un marco unificado para todas las comunicaciones API.


Suponiendo que solo desea convertir rápido y sucio un hash a json, envíe el json a un host remoto para probar una API y analizar la respuesta a ruby, esta es probablemente la forma más rápida sin involucrar gemas adicionales:

JSON.load `curl -H 'Content-Type:application/json' -H 'Accept:application/json' -X POST localhost:3000/simple_api -d '#{message.to_json}'`

Con suerte, esto no hace falta decirlo, pero no lo use en producción.

bbozo
fuente
2
Lol, ¿por qué todos los que odian? :) La publicación establece claramente que no es para producción ni para ningún otro propósito serio, pero es la forma más rápida de configurar una llamada a la API JSON para ver cómo se comporta un servicio
bbozo
No puede hacer esto usando la autenticación NTLM. Entonces Net :: HTTP sigue siendo el único que tiene una biblioteca que lo admite.
Rhuan
1
No estoy de acuerdo con la Net::HTTPafirmación 'nadie debería usar '
JellicleCat
Votado en contra por el nobody should be using Net::HTTP any more@bbozo
Patricio Sard
3

Esto funciona en Ruby 2.4 HTTPS Post con el objeto JSON y el cuerpo de la respuesta escrito.

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
szabcsee
fuente
2

Me gusta este cliente de solicitud http de peso ligero llamado 'unirest'

gem install unirest

uso:

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
tokhi
fuente
1

La API net / http puede ser difícil de usar.

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
Moriarty
fuente
Este código no funciona. Necesita inicializar Net :: HTTP con #start así:Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |client|
Tyler
Funciona con ruby ​​2.3.7p456 (2018-03-28 revisión 63024) [universal.x86_64-darwin18]
Moriarty
0
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'
phil pirozhkov
fuente