如何在 Rails 中 运行 cURL 命令

How to run cURL commands in Rails

我在 Rails 5 上使用 Ruby,我需要在我的应用程序中执行以下命令:

curl -F 'client_id=126581840734567' -F 'client_secret=678ebe1b3b8081231aab27dff738313' -F 'grant_type=authorization_code' -F 'redirect_uri=https://uri.com/' -F 'code=AQBi4L2Ohy3Q_N3V48OygFm0zb3gEsL985x5TIyDTNDJaLs93BwXiT1tyGYWoCg1HlBDU7ZRjUfLL5HVlzw4G-7YkVEjp6Id2WuqOz0Ylt-k2ADwDC5upH3CGVtHgf2udQhLlfDnQz5NPsnmxjg4bW3PJpW5FaQs8fn1ztgYp-ssfAf6IRt2-sI45ZC8cqqr5K_12y0Nq_Joh0H-tTfVyNLKatIxHPCqRDb3tfqgmxim1Q' https://api.instagram.com/oauth/access_token

所以它 returns 类似于:

{"access_token": "IGQVJYS0k8V6ZACRC10WjYxQWtyMVRZAN8VXamh0RVBZAYi34RkFlOUxXZnTJsbjlEfnFJNmprQThmQ4hTckpFUmJEaXZAnQlNYa25aWURnX3hpO12NV1VMWDNMWmdIT3FicnJfZAVowM3VldlVWZAEViN1ZAidHlyU2VDMUNuMm2V", "user_id": 17231445640157812}

有没有办法让 Rails 执行这些类型的命令?我正在尝试以下操作:

uri = URI.parse('https://api.instagram.com/oauth/access_token')
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({
  "client_id" => "126581840734567",
  "client_secret" => "678ebe1b3b8081231aab27dff738313",
  "grant_type" => "authorization_code",
  "redirect_uri" => "http://nace.network/",
  "code" => params[:code]
  })    


res = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(request)
end

但出现以下错误:

end of file reached

在这一行中:

res = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(request)
end

您正在使用 HTTPS,因此您需要将此添加到您的代码中:

Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  res = http.request(request)
end

但是如果你不需要持久连接,你也可以使用这个:

res = Net::HTTP.post_form(uri, 
  "client_id" => "126581840734567",
  "client_secret" => "678ebe1b3b8081231aab27dff738313",
  "grant_type" => "authorization_code",
  "redirect_uri" => "http://nace.network/",
  "code" => params[:code]
)

此外,您可以考虑使用像 Faraday 这样的库,这样更容易处理。

编辑

这是来自TinMan下面的评论,声音点。

从 Ruby 或 Rails 内部使用 cURL 非常有价值。 Rails 或 Ruby 中没有实现 cURL 中的大量功能;即使 Ruby 的 HTTP 客户端也很难复制它,因此根据应用程序的需要,cURL 是非常可以接受的。而且,根据应用程序的不同,因为 cURL 在编译的 C 中,它很容易超过纯 Ruby 客户端。

Curl 是一种从命令行发出 HTTP(或 HTTPs)请求的方法。

您不想在 Rails 中使用 CURL。您想要从 Rails 中发出 HTTP 请求。使用 curl 没问题,这是使用 Rails.

发出 HTTP 请求的一种方式

我们可以进一步细化,您希望从 Ruby 发出 HTTP 请求。 Narrowing/distilling 解决最基本的问题总是好的。

我们可能已经知道了这一切 - 仍然值得写下来让我们所有人受益!

在 Ruby

中使用 HTTP

我们想使用 HTTP 客户端。有很多但是,为此我将使用 Faraday (a gem) 因为我喜欢它。

您使用 Ruby 内置的 NET:HTTP 有了一个良好的开端,但我更喜欢 Faraday 的 DSL。它会产生更具可读性和可扩展性的代码。

所以,这里是 class!我几乎没有对此进行过测试,因此将其用作起点。确保为它编写一些单元测试。

    # This is a Plain Old Ruby Object (PORO)
# It will work in Rails but, isn't Rails specific.
require 'faraday' # This require is needed as it's a PORO.

class InstagramOAuth
  attr_reader :code

  # The code parameter will likely change frequently, so we provide it
  # at run time.
  def initialize(code)
    @code = code
  end

  def get_token
    connection.get('/oauth/access_token') do |request|
      request.params[:code] = code
    end
  end

  private

  def connection
    @connection ||= Faraday.new(
      url: instagram_api_url,
      params: params,
      ssl: { :ca_path => https_certificate_location }
    )
  end

  def instagram_api_url
    @url ||= 'https://api.instagram.com'
  end

  # You need to find out where these are for your self.
  def https_certificate_location
    '/usr/lib/ssl/certs'
  end

  def params
    # These params likely won't change to often so we set a write time
    # in the class like this.
    {
      client_id: '126581840734567',
      client_secret: '678ebe1b3b8081231aab27dff738313',
      grant_type: 'authorization_code',
      redirect_uri: 'https://uri.com/'
    }
  end
end

# How do we use it? Like so

# Your big old authorisation code from your question
code = 'AQBi4L2Ohy3Q_N3V48OygFm0zb3gEsL985x5TIyDTNDJaLs93BwXiT1tyGYWoCg1HlBDU'\
       '7ZRjUfLL5HVlzw4G-7YkVEjp6Id2WuqOz0Ylt-k2ADwDC5upH3CGVtHgf2udQhLlfDnQz'\
       '5NPsnmxjg4bW3PJpW5FaQs8fn1ztgYp-ssfAf6IRt2-sI45ZC8cqqr5K_12y0Nq_Joh0H'\
       '-tTfVyNLKatIxHPCqRDb3tfqgmxim1Q'

# This will return a Faraday::Response object but, what is in it?
response = InstagramOAuth.new(code).get_token

# Now we've got a Hash
response_hash = response.to_hash

puts 'Request made'
puts "Request full URL: #{response_hash[:url]}"
puts "HTTP status code: #{response_hash[:status]}"
puts "HTTP response body: #{response_hash[:body]}"

当我 运行 上面的代码片段时,我得到了以下信息。 class 有效,你只需要调整请求参数,直到你得到你想要的。希望 class 演示如何在 Ruby/Rails.

中发送 HTTP 请求
Request made
Request full URL: https://api.instagram.com/oauth/access_token?client_id=126581840734567&client_secret=678ebe1b3b8081231aab27dff738313&code=AQBi4L2Ohy3Q_N3V48OygFm0zb3gEsL985x5TIyDTNDJaLs93BwXiT1tyGYWoCg1HlBDU7ZRjUfLL5HVlzw4G-7YkVEjp6Id2WuqOz0Ylt-k2ADwDC5upH3CGVtHgf2udQhLlfDnQz5NPsnmxjg4bW3PJpW5FaQs8fn1ztgYp-ssfAf6IRt2-sI45ZC8cqqr5K_12y0Nq_Joh0H-tTfVyNLKatIxHPCqRDb3tfqgmxim1Q&grant_type=authorization_code&redirect_uri=https%3A%2F%2Furi.com%2F
HTTP status code: 405
HTTP response body: 

补充阅读

https://lostisland.github.io/faraday/usage/

https://github.com/lostisland/faraday/wiki/Setting-up-SSL-certificates