2016-07-14 4 views
1

J'utilise Rails 4.2.3 et Nokogiri pour obtenir des données d'un site Web. Je veux effectuer une action quand je ne reçois pas de réponse du serveur, donc je dois:Dans RoR, comment puis-je intercepter une exception si je n'obtiens aucune réponse d'un serveur?

begin 
    content = open(url).read 
    if content.lstrip[0] == '<' 
    doc = Nokogiri::HTML(content) 
    else 
    begin 
     json = JSON.parse(content) 
    rescue JSON::ParserError => e 
     content 
    end 
    end 
rescue Net::OpenTimeout => e 
    attempts = attempts + 1 
    if attempts <= max_attempts 
    sleep(3) 
    retry 
    end 
end 

Notez que ceci est différent que d'obtenir 500 à partir du serveur. Je veux seulement réessayer quand je n'obtiens aucune réponse, soit parce que je n'obtiens aucune connexion de TCP ou parce que le serveur ne répond pas (ou n'importe quelle autre raison qui m'empêche d'obtenir n'importe quelle réponse). Existe-t-il une façon plus générique de prendre en compte cette situation autrement que comme je l'ai? J'ai l'impression qu'il y a beaucoup d'autres types d'exceptions auxquels je ne pense pas.

+0

Vous n'êtes pas en utilisant Nokogiri pour obtenir des données, vous l'utilisez pour _parse_ data. OpenURI "obtient" les données. C'est une distinction importante qui supprime Nokogiri de la question. En outre, le titre est trompeur; Rails est un framework écrit en Ruby. Vous n'écrivez pas de choses dans Rails, vous les écrivez en Ruby, et parfois utilisez les méthodes de Rails. Je suggère de reformuler la question en fonction de cette connaissance. Il y a beaucoup d'erreurs HTTP définies que vous pouvez gérer, et il peut y avoir des erreurs personnalisées définies par les administrateurs d'un site, donc vous devez en être conscient. –

+0

Je ne me soucie pas des messages personnalisés définis par un site - ce qui impliquerait qu'une réponse est renvoyée. J'essaie de rendre compte de la situation (et seulement la situation) où je n'obtiens aucune réponse du tout?Est-ce clair ce que je demande - n'obtenant aucune réponse par rapport à l'obtention de réponses indiquant d'autres conditions? – Dave

+0

Vous ne souhaitez pas obtenir de connexion TCP ou obtenir une connexion TCP, mais le serveur ne répond pas. –

Répondre

4

Ceci est exemple générique comment vous pouvez définir des durées de délai d'attente pour une connexion HTTP et effectuer plusieurs tentatives en cas d'erreur lors de la récupération de contenu (modifié)

require 'open-uri' 
require 'nokogiri' 

url = "http://localhost:3000/r503" 

openuri_params = { 
    # set timeout durations for HTTP connection 
    # default values for open_timeout and read_timeout is 60 seconds 
    :open_timeout => 1, 
    :read_timeout => 1, 
} 

attempt_count = 0 
max_attempts = 3 
begin 
    attempt_count += 1 
    puts "attempt ##{attempt_count}" 
    content = open(url, openuri_params).read 
rescue OpenURI::HTTPError => e 
    # it's 404, etc. (do nothing) 
rescue SocketError, Net::ReadTimeout => e 
    # server can't be reached or doesn't send any respones 
    puts "error: #{e}" 
    sleep 3 
    retry if attempt_count < max_attempts 
else 
    # connection was successful, 
    # content is fetched, 
    # so here we can parse content with Nokogiri, 
    # or call a helper method, etc. 
    doc = Nokogiri::HTML(content) 
    p doc 
end 
+0

Cela ne répond pas exactement à ma question. Vous interceptez une exception pour tout type d'exception lancée, même 404 ou 503, qui sont des réponses du serveur. Je veux prendre en compte les cas d'hte (et seulement les cas) où le serveur ne peut pas être atteint ou n'envoie aucun respones du tout. – Dave

+0

@Dave Votre question est un peu ambigu sur la façon de gérer tout le reste, mais ce proche de la bonne réponse. Vous voulez sauver 'SocketError's et au lieu de' Net :: OpenTimeout' vous voulez attraper 'Net :: ReadTimeout'. 'Net :: OpenTimeout' ne se verrouille que si l'ouverture de la connexion échoue si nous ne comprenons pas/ne lisons pas une réponse. Ne sauvez pas 'OpenURI :: HTTPError' si vous ne vous souciez pas des autres erreurs. – Azolo

+0

Dave J'ai mis à jour le code après votre premier commentaire (pour afficher une gestion plus fine des exceptions). Comme l'a dit @Azolo, vous pouvez le personnaliser en fonction de vos besoins réels. –

1

Je pense à utiliser un Timeout qui soulève une exception après une courte période:

MAX_RESPONSE_TIME = 2 # seconds 
begin 
    content = nil # needs to be defined before the following block 
    Timeout.timeout(MAX_RESPONSE_TIME) do 
    content = open(url).read 
    end 

    # parsing `content` 
rescue Timeout::Error => e 
    attempts += 1 
    if attempts <= max_attempts 
    sleep(3) 
    retry 
    end 
end 
+0

Merci. Qu'en est-il si le DNS ne résout pas pour l'hôte en question - le compte ci-dessus pour cela? – Dave

+0

'Timeout.timeout' déclenche une exception lorsque le code dans le bloc prend plus de temps que MAX_RESPONSE_TIME' à s'exécuter - peu importe pourquoi cela a pris plus de temps. Si le DNS prend trop de temps que mon exemple couvrirait ce cas. Si le DNS échoue avec une autre exception, vous devrez également le sauver de cette exception (Désolé, je ne suis pas sûr de savoir quelle exception est soulevée dans ce cas). – spickermann

+0

Ce n'est pas une mauvaise solution, les erreurs 'Net :: XTimeout' résultent de l'utilisation interne de' Timeout' dans le module 'Net'. Cependant, ce que cela ne prend pas en compte est le fait que si vous avez une grande page 'open-uri' analyse et charge tout en mémoire, ce que j'ai vu prendre beaucoup de temps. – Azolo

3

en matière de sauvetage des exceptions, vous devriez viser à avoir une compréhension claire de:

  • Quelles lignes dans votre système peuvent soulever des exceptions
  • Qu'est-ce qui se passe sous le capot lorsque ces lignes de code execute
  • Quelles exceptions spécifiques pourrait être soulevée par le code sous-jacent

Dans votre code, la ligne qui est récupérer le contenu est aussi celui qui pourrait voir les erreurs réseau:

content = open(url).read 

Si vous allez à la documentation for the OpenURI module vous verrez qu'il utilise Net::HTTP & amis pour obtenir le contenu d'URI arbitraires. Comprendre ce que Net::HTTP peut soulever est vraiment très compliqué mais, heureusement, d'autres ont déjà fait ce travail pour vous. Le projet de bretelles de Thoughtbot a lists of common network errors que vous pouvez utiliser. Notez que certaines de ces erreurs concernent des conditions de réseau différentes de celles que vous aviez en tête, comme la réinitialisation de la connexion. Je pense que cela vaut la peine de les sauver aussi, mais n'hésitez pas à réduire la liste à vos besoins spécifiques.

Alors, voici ce que votre code devrait ressembler (sauter les parties Nokogiri et JSON pour simplifier les choses un peu): require 'net/http' require 'open-uri'

HTTP_ERRORS = [ 
    EOFError, 
    Errno::ECONNRESET, 
    Errno::EINVAL, 
    Net::HTTPBadResponse, 
    Net::HTTPHeaderSyntaxError, 
    Net::ProtocolError, 
    Timeout::Error, 
] 
MAX_RETRIES = 3 

attempts = 0 

begin 
    content = open(url).read 
rescue *HTTP_ERRORS => e 
    if attempts < MAX_RETRIES 
    attempts += 1 
    sleep(2) 
    retry 
    else 
    raise e 
    end 
end