从Net :: HTTP中拯救异常的最佳方法是什么?
抛出的异常在Ruby socket.c
中描述,如Errno::ETIMEDOUT
,Errno::ECONNRESET
和Errno::ECONNREFUSED
。所有这些的基类都是SystemCallError
,但是编写如下代码感觉很奇怪,因为SystemCallError
似乎已经远远没有进行HTTP
调用了:
begin
response = Net::HTTP.get_response(uri)
response.code == "200"
rescue SystemCallError
false
end
只是我吗?有没有更好的方法来解决这个问题,除了修复Net::HTTP
以处理可能会弹出并将其封装在父Errno
中的HttpRequestException
例外情况?
答案 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库,例如:
答案 1 :(得分:21)
我遇到了同样的问题,经过大量研究后,我意识到处理所有异常的最佳方法是Net :: HTTP方法抛出的是从StandardError中解救。
正如Mike Lewis's answer指出的那样,Tammer Saleh blog post建议从许多例外中拯救,但它仍然存在缺陷。他有一些例外,例如Errno::EHOSTUNREACH
,Errno::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
方法之一。