【问题标题】:Error Handling when Contacting 3rd Party APIs联系第 3 方 API 时的错误处理
【发布时间】:2014-01-09 23:11:17
【问题描述】:

鉴于我在 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 不为零。如果它是 nil,我会在所有地方得到 NilClass 错误
  • 显示视图需要渲染一些东西
  • 如果出现错误,#get_that_third_party_content 将不返回任何内容
  • 感觉我需要做很多 nil 检查......这似乎真的错了
  • rescue_from 似乎是一个很好的解决方案,但我不确定这是否很好

【问题讨论】:

  • 如果您超时,请render 另一个页面解释出现问题并稍后再试。您还可以存储从这些 API 接收到的任何信息,并在超时、错误等情况下返回存储的信息。
  • 这个有规律吗?

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


【解决方案1】:

您可能希望在这里使用Faraday 之类的东西,因为您可以利用中间件。创建 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

【讨论】:

  • 感谢您的回答,但可以使用的代码示例和/或模式将非常有帮助。
  • 非常感谢!那么是否可以肯定地说,无论如何,至少会有 1 个逻辑分支本质上检查请求是否失败,然后呈现一些内容供用户查看?我问是因为在研究这个问题时,我看到很多人使用rescue_from 并提出了一个特定的错误。这似乎使控制器动作没有逻辑。
  • 正确 - 您至少需要一个。除非您将其推入中间件堆栈并引发异常。然后让全局的 rescue_from 处理程序优雅地解决这个问题
  • 赛斯,非常感谢您更新答案!我一直在阅读您的博客文章以寻求帮助,但没有意识到您也在此处回答我的问题! :) 在选择正确答案之前我有两个要求: 1- 我认为应该检查APIConnector#get#initialize 内部,connection 应该是 @connection? 2-如何使用原生中间件进行超时?
  • 已更新以包含超时示例。修复了嵌套,但连接正确(顶部有一个attr_reader)
猜你喜欢
  • 1970-01-01
  • 2011-09-25
  • 2022-06-15
  • 1970-01-01
  • 2014-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多