从Net :: HTTP处理异常的最佳方法是什么?

时间:2011-03-20 19:01:36

标签: ruby

从Net :: HTTP中拯救异常的最佳方法是什么?

抛出的异常在Ruby socket.c中描述,如Errno::ETIMEDOUTErrno::ECONNRESETErrno::ECONNREFUSED。所有这些的基类都是SystemCallError,但是编写如下代码感觉很奇怪,因为SystemCallError似乎已经远远没有进行HTTP调用了:

begin
  response = Net::HTTP.get_response(uri)
  response.code == "200"
rescue SystemCallError
  false
end

只是我吗?有没有更好的方法来解决这个问题,除了修复Net::HTTP以处理可能会弹出并将其封装在父Errno中的HttpRequestException例外情况?

4 个答案:

答案 0 :(得分:42)

我同意处理所有潜在的异常是绝对痛苦的。请查看this以查看示例:

  

使用Net::HTTP可能会很痛苦。它有大约40种不同的方式   做任何一项任务,以及它可以抛出的大约50个例外。

     

只是为了谷歌的爱,这就是我为“正确的方式”所拥有的   捕获Net :: HTTP可以向您抛出的任何异常:

begin
  response = Net::HTTP.post_form(...) # or any Net::HTTP call
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
       Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
  ...
end
     

为什么不只是rescue Exception => e?这是一个坏习惯,因为   它隐藏了你实际代码中的任何问题(比如SyntaxErrors,whiny   尼尔斯等)。当然,如果可能的话,这将更加容易   错误有一个共同的祖先。

     

我在处理Net :: HTTP时遇到的问题让我受益匪浅   想知道编写一个新的HTTP客户端库是否值得。   一个更容易在测试中模拟,并没有所有这些   丑陋的小方面。

我所做的,并且看到大多数人所做的,是远离Net :: HTTP并转移到第三方HTTP库,例如:

httpartyfaraday

答案 1 :(得分:21)

我遇到了同样的问题,经过大量研究后,我意识到处理所有异常的最佳方法是Net :: HTTP方法抛出的是从StandardError中解救。

正如Mike Lewis's answer指出的那样,Tammer Saleh blog post建议从许多例外中拯救,但它仍然存在缺陷。他有一些例外,例如Errno::EHOSTUNREACHErrno::ECONNREFUSED,以及可能的socket例外情况。

所以,正如我在tenderlove's translation of an old ruby-dev thread中发现的那样,最好的解决方案是从StandardError抢救,不幸的是:

begin
  response = Net::HTTP.get_response(uri)
rescue StandardError
  false
end

这很糟糕,但如果您希望系统不会因为这些其他异常而中断,请使用此方法。

答案 2 :(得分:3)

你对此的直觉是对的,对于最强大的解决方案,我可能会单独(或小组)拯救每一个并采取适当的行动,例如再次尝试连接,或者一起放弃请求。我喜欢避免使用非常高级/通用的救援,因为它可能会捕获我不准备或不期望的异常。

答案 3 :(得分:1)

另一种方法是将所有这些异常聚合在一个常量中,然后重新使用这个常量,例如:

ALL_NET_HTTP_ERRORS = [
  Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
  Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError
]

begin
  your_http_logic()
rescue *ALL_NET_HTTP_ERRORS
  …
end

它更易于维护和清洁。

然而,警告的话。我已经复制了上述Tammer Saleh博客文章中可能的例外列表,我知道他的列表不完整。例如,Net::HTTP.get(URI("wow"))会引发未列出的Errno::ECONNREFUSED。另外,如果列表应该针对不同的Ruby版本进行修改,我也不会感到惊讶。

出于这个原因,我建议在大多数情况下坚持使用rescue StandardError。为了避免捕获太多,尽可能在begin-rescue-end块之外移动,最好只调用Net::HTTP方法之一。