联系第三方API时的错误处理

时间:2013-12-21 02:01:23

标签: ruby-on-rails ruby exception error-handling

鉴于我在Rails控制器中有以下设计代码:

class ProfilesController < ApplicationController
  def show
    @profile = current_user.profile
    @content_from_third_party_api = @profile.get_that_third_party_content
  end
end

此代码位于views/profiles/show.html.haml

%h1
  Profile Page!
%h2
  Data from 3rd party:
= @content_from_third_party_api.first.title

这个用于#get_that_third_party_content的设计代码:

def get_that_third_party_content
  uri = URI.parse("http://api.somesite.com/imfake/")

  http = Net::HTTP.new(uri.host, uri.port)
  request = Net::HTTP::Get.new(uri.request_uri)
  response = http.request(request)

  case response.code.to_i
  when 200 # the happy path
    response
  when (400..499)
    # What should I do/return here?  I have nothing useful to return to the caller.
  when (500..599)
    # Same goes for here...
  end
end

如果给出以下内容,我如何优雅地处理#get_that_third_party_content中的超时/错误/等:

  • 节目视图预计@content_from_third_party_api不能为零。如果它没有,我会在整个地方得到NilClass错误
  • show view需要渲染Something
  • 如果出现错误,
  • #get_that_third_party_content将不会返回任何内容
  • 感觉我需要做很多零检查......而这似乎是错误的
  • rescue_from似乎是一个很好的解决方案,但我不确定这是不是很好

1 个答案:

答案 0 :(得分:2)

您可能希望在此处使用Faraday之类的内容,因为您可以利用中间件。创建法拉第构建器非常简单,您可以编写中间件来处理所有问题。有超时(只需要它)和异常处理的本机中间件。

鉴于您希望“优雅地”处理这些用例,中间件对我来说似乎是最合理的选择。否则,正如您所指出的,您将在控制器中拥有复杂的分支逻辑。

# lib/requestor.rb
require 'faraday'
require 'faraday_middleware' # optional, but nice: https://github.com/lostisland/faraday_middleware

# This is just one example. There are literally hundreds of ways to use
# Faraday, and the documentation is quite good.
class APIConnector
  attr_reader :connection

  def initialize(url)
    @connection = Faraday.new(url) do |connection|
      # Set up your connection here (like response and request objects).
      # There are a lot of examples in the Faraday documentation and things
      # really depend on the API you are accessing.
      #
      # You can set things like timeouts, retrys, mashes, rashes, etc.

      # Set some options, such as timeouts
      connection.options[:timeout]      = 10
      connection.options[:open_timeout] = 10
    end
  end

  def get(path)
    connection.get(path)
  end
end

然后在你的控制器中:

class ProfilesController < ApplicationController
  def show
    @profile = current_user.profile
    response = requestor.get('/path')

    if response.success?
      @third_party_content = response.body
    else
      # This changes depending on the volatility of your application. 
      # You might raise an error, show a flash message, kill the 
      # request, etc. It all depends on the context.
      render text: 'API Request failed'
    end
  end

  private

  def requestor
    @requestor ||= Requestor.new(some_url)
  end
end