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?

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

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

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

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


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

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.


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

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

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

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'
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.