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?

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

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

¿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: 'user1@example.com', password: 'secret'}}).body

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


... 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"})

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.


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

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

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

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'
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.