web-dev-qa-db-fra.com

Quelle est la meilleure façon de gérer les exceptions de Net :: HTTP?

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?

45

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:

httparty et faraday

44
Mike Lewis

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.

21
Hugo Tavares

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.

3
ctcherry

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.

1
skalee