Quelle est la meilleure façon de récupérer les exceptions de Net :: HTTP?
Les exceptions levées sont décrites dans Ruby's socket.c
, comme Errno::ETIMEDOUT
, Errno::ECONNRESET
, et Errno::ECONNREFUSED
. La classe de base de tous ces éléments est SystemCallError
, mais il semble étrange d'écrire du code comme celui-ci car SystemCallError
semble si éloigné de faire un appel à HTTP
:
begin
response = Net::HTTP.get_response(uri)
response.code == "200"
rescue SystemCallError
false
end
Est ce juste moi? Y a-t-il une meilleure façon de gérer cela au-delà de la correction Net::HTTP
pour gérer les Errno
exceptions qui pourraient apparaître et les encapsuler dans un parent HttpRequestException
?
Je suis d'accord que c'est une douleur absolue de gérer toutes les exceptions potentielles. Regardez this pour voir un exemple:
Travailler avec
Net::HTTP
peut être pénible. Il y a environ 40 façons différentes de faire n'importe quelle tâche, et environ 50 exceptions qu'il peut lancer.Juste pour l'amour de Google, voici ce que j'ai pour la "bonne façon" d'attraper toute exception que Net :: HTTP peut vous lancer:
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
Pourquoi pas simplement
rescue Exception => e
? C'est une mauvaise habitude à prendre, car elle cache des problèmes dans votre code réel (comme SyntaxErrors, whiny nils, etc.). Bien sûr, cela serait beaucoup plus facile si les erreurs possibles avaient un ancêtre commun.Les problèmes que j'ai rencontrés dans le traitement de Net :: HTTP m'ont amené à me demander si cela ne valait pas la peine d'écrire une nouvelle bibliothèque client HTTP. Celui qui était plus facile à simuler dans les tests, et n'avait pas toutes ces petites facettes laides.
Ce que j'ai fait et vu la plupart des gens, c'est s'éloigner de Net :: HTTP et passer à des bibliothèques HTTP tierces telles que:
J'ai rencontré le même problème et après de nombreuses recherches, j'ai réalisé que la meilleure façon de gérer toutes les exceptions que les méthodes Net :: HTTP lanceraient est de sauvetage de StandardError.
Comme indiqué par réponse de Mike Lewis , article de blog Tammer Saleh propose de sauver de nombreuses exceptions, mais c'est toujours un défaut. Il y a quelques exceptions dont il ne sauve pas, comme Errno::EHOSTUNREACH
, Errno::ECONNREFUSED
, et éventuellement quelques socket
exceptions.
Donc, comme je l'ai découvert dans traduction de tenderlove d'un ancien thread Ruby-dev , la meilleure solution est de sauver de StandardError
, malheureusement:
begin
response = Net::HTTP.get_response(uri)
rescue StandardError
false
end
C'est horrible, mais si vous voulez que votre système ne tombe pas en panne à cause de ces autres exceptions, utilisez cette approche.
Votre intuition est juste, pour la solution la plus robuste, je sauverais chacun individuellement (ou en petits groupes) et prendrais les mesures appropriées, comme essayer à nouveau la connexion, ou abandonner la demande tous ensemble. J'aime éviter d'utiliser un sauvetage générique de très haut niveau car il pourrait intercepter des exceptions auxquelles je ne suis pas préparé ou auquel je ne m'attendais pas.
Une autre approche consiste à agréger toutes ces exceptions dans une constante, puis à réutiliser cette constante, par exemple:
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
Il est beaucoup plus facile à entretenir et plus propre.
Cependant, mot d'avertissement. J'ai copié la liste des exceptions possibles du blog de Tammer Saleh susmentionné, et je sais que sa liste est incomplète. Par exemple, Net::HTTP.get(URI("wow"))
soulève Errno::ECONNREFUSED
Qui n'est pas répertorié. De plus, je ne serais pas surpris si la liste devait être modifiée pour différentes versions Ruby.
Pour cette raison, je recommande de s'en tenir à rescue StandardError
Dans la plupart des cas. Afin d'éviter d'attraper trop, déplacez-vous autant que possible en dehors du bloc begin-rescue-end, de préférence ne laissez qu'un appel à l'une des méthodes Net::HTTP
.