Ruby wysyła żądanie JSON

86

Jak wysłać żądanie JSON w języku Ruby? Mam obiekt JSON, ale nie sądzę, żebym mógł to zrobić .send. Czy muszę mieć javascript, aby wysłać formularz?

Czy mogę użyć klasy net / http w języku ruby?

Z nagłówkiem - typ zawartości = json i treścią obiektu json?

Andy
źródło

Odpowiedzi:

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
źródło
8
Podoba mi się twoja sugestia, aby użyć URI do obsługi nazwy hosta i portu, co w innym przypadku jest dość uciążliwe. Ale zapomniałeś ustawić uri.path w Post.new (...):req = Net::HTTP::Post.new(uri.path, initheader = {'Content-Type' =>'application/json'})
ArnauOrriols
1
Najprostsza, najczystsza odpowiedź. To jest świetne.
joelc
http.request(req).read_bodyaby przeczytać treść odpowiedzi. Świetny!
iGian
1
Jestem prawie pewien, że to się zmieniło w 2.4.1, ale mój Boże. Ta składnia jest obrzydliwa. Wie, że ma identyfikator URI z Post.new (), więc dlaczego musisz powtórzyć wartości, podzielić, w start (). Obrzydliwy. Nic dziwnego, że w Rubim jest tak wiele innych pakietów, które obsługują 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
źródło
Który wyjątek należy określić
Mio
7
W przypadku żądań https po prostu dodaj: http.use_ssl = true.
Skill M2,
17

Wydaje mi się, że HTTParty sprawia, że ​​jest to trochę łatwiejsze (i działa z zagnieżdżonym jsonem itp., Które nie działały w innych przykładach, które widziałem.

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

Przykład z życia, powiadom Airbrake API o nowym wdrożeniu za pośrednictwem 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)

Uwagi:

w przypadku uwierzytelniania przez nagłówek zestawu nagłówków autoryzacji req['Authorization'] = "Token xxxxxxxxxxxx" lub http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html

odpowiednik 8
źródło
... ale szczerze mówiąc, to jest fajne i wszystko, ale w prawdziwym życiu po prostu użyłbym HTTParty stackoverflow.com/a/14491995/473040 :) ... szczególnie jeśli masz do czynienia z obsługą https
odpowiednik 8
wymaganie uri jest bezużyteczne, ponieważ jest już wymagane przez net / http
noraj
@ odpowiednik8: „w prawdziwym życiu po prostu użyłbym HTTParty” - to znaczy, chyba że budujesz szczupły klejnot lub w inny sposób nie chcesz innej zależności. :)
Sergio Tulentsev,
@SergioTulentsev zgadzam się ... chyba że budujesz gem / lib (lub mikrousługę opartą na Ruby), gdzie nie chcesz wprowadzać niepotrzebnych zależności;)
odpowiednik
4

Prosty przykład żądania POST json dla tych, którzy go potrzebują, nawet prostszy niż to, do czego łączy się Tom:

require 'net/http'

uri = URI.parse("http://www.example.com/search.json")
response = Net::HTTP.post_form(uri, {"search" => "Berlin"})
Christoffer
źródło
19
Wygląda na to, że powinno działać, ale post_form tłumaczy parametry na składnię? Key = wartość i klucz = wartość. Jeśli chcesz wykonać POST z treścią żądania ustawioną na ciąg JSON, myślę, że potrzebujesz innego rozwiązania.
Ben Gotow
To nie działa w przypadku głęboko zagnieżdżonego pliku JSON. Wszystko poza pierwszym poziomem staje się ciągiem.
neoneye
Nie tylko wyglądam. To działa. To proste, jasne. ale w przypadku prostych rzeczy, takich jak przykład, który podałem, działa dobrze
Christoffer
4
Zasadniczo nie jest to żądanie JSON. To jest treść z kodem urlencoded. Nie ma JSON. Nagłówek nawet to mówi. To nie działa z żadnym przykładem.
raylu
4
Ta odpowiedź jest nieprawidłowa. Jest to POST w mime / multipart, prowadzący do adresu URL zawierającego słowo „json”.
John Haugeland,
4

Jest rok 2020 - nikt nie powinien już używać Net::HTTPi wszystkie odpowiedzi wydają się tak mówić, użyj klejnotu wyższego poziomu, takiego jak Faraday - Github


To powiedziawszy, to, co lubię robić, to otoka wokół wywołania HTTP api, coś, co nazywa się

rv = Transporter::FaradayHttp[url, options]

ponieważ pozwala mi to na fałszywe wywołania HTTP bez dodatkowych zależności, tj .:

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

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

Gdzie faker wygląda coś jak ten

Wiem, że istnieją frameworki HTTP mocking / stubbing, ale przynajmniej kiedy ostatnio badałem, nie pozwalały mi one na wydajną walidację żądań i były przeznaczone tylko dla HTTP, a nie na przykład dla surowych wymian TCP, ten system pozwala mi mieć ujednolicona struktura dla całej komunikacji API.


Zakładając, że chcesz po prostu szybko i brudnie przekonwertować hash na json, wyślij json do zdalnego hosta, aby przetestować API i przeanalizować odpowiedź na ruby, jest to prawdopodobnie najszybszy sposób bez angażowania dodatkowych klejnotów:

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

Mam nadzieję, że jest to oczywiste, ale nie używaj tego w produkcji.

bbozo
źródło
2
Lol, dlaczego wszyscy hejterzy? :) Post wyraźnie stwierdza, że ​​nie jest przeznaczony do produkcji ani żadnego innego poważnego celu, ale jest to najszybszy sposób na skonfigurowanie wywołania JSON API, aby zobaczyć, jak zachowuje się usługa
bbozo
Nie możesz tego zrobić przy użyciu uwierzytelniania NTLM. Tak więc Net :: HTTP jest nadal jedynym, który ma bibliotekę, która go obsługuje.
Rhuan
1
Nie zgadzam się z Net::HTTPtwierdzeniem „nikt nie powinien używać ”
JellicleCat
Negocjowany z powodu nobody should be using Net::HTTP any more@bbozo
Patricio Sard
3

Działa to na ruby ​​2.4 HTTPS Post z obiektem JSON i wypisaną treścią odpowiedzi.

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
źródło
2

Podoba mi się ten lekki klient żądań http o nazwie „unirest”

gem install unirest

stosowanie:

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
źródło
1

Interfejs api net / http może być trudny w użyciu.

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
źródło
Ten kod nie działa. Musisz zainicjować Net :: HTTP z #start w ten sposób:Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |client|
Tyler
Działa z ruby ​​2.3.7p456 (2018-03-28, wersja 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
źródło