Ruby enviar solicitação JSON

86

Como faço para enviar uma solicitação JSON em ruby? Tenho um objeto JSON, mas não acho que posso simplesmente fazer .send. Devo ter o javascript para enviar o formulário?

Ou posso usar a classe net / http em ruby?

Com header - content type = json e body o objeto json?

Andy
fonte

Respostas:

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
fonte
8
Gosto da sua sugestão de usar URI para lidar com o nome do host e a porta, o que, de outra forma, é um tanto entediante. Mas você se esqueceu de definir uri.path em Post.new (...):req = Net::HTTP::Post.new(uri.path, initheader = {'Content-Type' =>'application/json'})
ArnauOrriols
1
Resposta mais simples e limpa. Isso é ótimo.
joelc de
http.request(req).read_bodypara ler o corpo da resposta. Ótimo!
iGian
1
Tenho certeza de que mudou no 2.4.1, mas meu Deus. Essa sintaxe é grosseira. Ele sabe que tem o URI do Post.new () então por que você tem que repassar os valores, dividir, em start (). Bruto. Não é de admirar que haja tantos outros pacotes em ruby ​​que lidam com 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
fonte
Qual exceção deve ser especificada
Mio
7
Para solicitações https, basta adicionar: http.use_ssl = true.
Skill M2
17

HTTParty torna isso um pouco mais fácil, eu acho (e funciona com json aninhado etc, que não parece funcionar em outros exemplos que eu vi.

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

exemplo da vida real, notifique a API Airbrake sobre a nova implantação 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)

Notas:

caso você esteja fazendo autenticação por meio do cabeçalho de conjunto de cabeçalho de autorização req['Authorization'] = "Token xxxxxxxxxxxx" ou http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html

equivalente8
fonte
... mas honestamente, isso é legal e tudo menos ao vivo, eu usaria apenas HTTParty stackoverflow.com/a/14491995/473040 :) ... especialmente se você estiver lidando com manipulação de https
equivalente 8
exigir uri é inútil, pois já é exigido por net / http
noraj
@ Equivalente8: "na vida real eu usaria apenas HTTParty" - isto é, a menos que você esteja construindo uma joia enxuta, ou não queira outra dependência. :)
Sergio Tulentsev
@SergioTulentsev concorda ... a menos que você esteja construindo gem / lib (ou microsserviço baseado em Ruby) onde você não deseja introduzir dependências desnecessárias;)
equivalente
4

Um exemplo de solicitação POST json simples para aqueles que precisam dele ainda mais simples do que o 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
fonte
19
Parece que deve funcionar, mas post_form traduz os parâmetros para a sintaxe? Key = value & key = value. Se você quiser fazer um POST com o corpo da solicitação definido como uma string JSON, acho que você precisa de uma solução diferente.
Ben Gotow
Isso não funciona com json profundamente aninhado. Qualquer coisa além do primeiro nível se torna uma corda.
neoneye
Não apenas pareça. Funciona. É simples com certeza. mas para coisas simples, como o exemplo que dei, ele funciona muito bem
Christoffer
4
Esta não é basicamente uma solicitação JSON. Este é um corpo codificado por url. Não há JSON. O cabeçalho até diz isso. Isso não funciona com nenhum exemplo, nunca.
raylu
4
Esta resposta está incorreta. É um POST em mime / multipart, para um url que diz "json" nele.
John Haugeland
4

É 2020 - ninguém deveria usar Net::HTTPmais e todas as respostas parecem dizer isso, use uma joia de nível mais alto, como Faraday - Github


Dito isso, o que eu gosto de fazer é um invólucro em torno da chamada da API HTTP, algo que se chama como

rv = Transporter::FaradayHttp[url, options]

porque isso me permite falsificar chamadas HTTP sem dependências adicionais, ou seja:

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

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

Onde o falsificador se parece com isso

Eu sei que existem frameworks HTTP mocking / stubbing, mas pelo menos quando eu pesquisei da última vez eles não me permitiram validar as solicitações de forma eficiente e eram apenas para HTTP, não por exemplo para trocas TCP brutas, este sistema me permite ter um estrutura unificada para todas as comunicações API.


Supondo que você deseja apenas uma conversão rápida e suja de um hash em json, envie o json a um host remoto para testar uma API e analisar a resposta para ruby, esta é provavelmente a maneira mais rápida sem envolver joias adicionais:

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

Esperançosamente, nem é preciso dizer, mas não use isso na produção.

bbozo
fonte
2
Lol, por que todos os odiadores? :) O post afirma claramente que não é para produção ou qualquer outro propósito sério, mas é a maneira mais rápida de configurar uma chamada de API JSON para ver como um serviço se comporta
bbozo
Você não pode fazer isso usando a autenticação NTLM. Portanto, Net :: HTTP ainda é o único que possui uma biblioteca que o suporta.
Rhuan
1
Discordar da Net::HTTPdeclaração 'ninguém deveria usar '
JellicleCat
nobody should be using Net::HTTP any moreVotos negados por causa de @bbozo
Patricio Sard
3

Isso funciona no Ruby 2.4 HTTPS Post com objeto JSON e o corpo da resposta 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
fonte
2

Eu gosto deste cliente de solicitação http leve chamado `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
fonte
1

A api net / http pode 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
fonte
Este código não funciona. Você precisa inicializar Net :: HTTP com #start assim:Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |client|
Tyler
Funciona com ruby ​​2.3.7p456 (revisão 63024 de 28/03/2018) [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
fonte