192 votes

Comment puis-je savoir si HttpClient a dépassé le temps imparti ?

Pour autant que je sache, il n'y a aucun moyen de savoir que c'est spécifiquement un dépassement de délai qui s'est produit. Est-ce que je ne cherche pas au bon endroit, ou est-ce que je rate quelque chose de plus important ?

string baseAddress = "http://localhost:8080/";
var client = new HttpClient() 
{ 
    BaseAddress = new Uri(baseAddress), 
    Timeout = TimeSpan.FromMilliseconds(1) 
};
try
{
    var s = client.GetAsync("").Result;
}
catch(Exception e)
{
    Console.WriteLine(e.Message);
    Console.WriteLine(e.InnerException.Message);
}

Cela revient :

Une ou plusieurs erreurs se sont produites.

Une tâche a été annulée.

3 votes

0 votes

Un énorme upvote pour la question. Aussi... une idée de comment faire cela sur UWP ? Son Windows.Web.HTTP.HTTPClient n'a pas de membre timeout. De plus, la méthode GetAsync n'accepte pas le jeton d'annulation...

1 votes

6 ans plus tard, il ne semble toujours pas possible de savoir si un client a dépassé le temps imparti.

82voto

vezenkov Points 46

Je reproduis le même problème et c'est vraiment ennuyeux. J'ai trouvé ceci utile :

HttpClient - gestion des exceptions d'agrégation

Un bogue dans HttpClient.GetAsync devrait provoquer une WebException, et non une TaskCanceledException.

Un peu de code au cas où les liens ne mènent nulle part :

var c = new HttpClient();
c.Timeout = TimeSpan.FromMilliseconds(10);
var cts = new CancellationTokenSource();
try
{
    var x = await c.GetAsync("http://linqpad.net", cts.Token);  
}
catch(WebException ex)
{
    // handle web exception
}
catch(TaskCanceledException ex)
{
    if(ex.CancellationToken == cts.Token)
    {
        // a real cancellation, triggered by the caller
    }
    else
    {
        // a web request timeout (possibly other things!?)
    }
}

0 votes

D'après mon expérience, les WebException ne peuvent être capturées en aucune circonstance. D'autres personnes ont-elles une expérience différente ?

1 votes

@crush WebException puede être attrapé. Peut-être que ce vous aidera.

0 votes

Cela ne fonctionne pas pour moi si je n'utilise pas de cts. J'utilise simplement Task<T> task = SomeTask() try { T result = task.Result} catch (TaskCanceledException) {} catch (Exception e) {} Seule l'exception générale est attrapée, pas la TaskCanceledException. Qu'est-ce qui ne va pas dans ma version du code ?

66voto

murkaeus Points 139

Vous devez attendre le GetAsync de la méthode. Il lancera alors un TaskCanceledException s'il a expiré. De plus, GetStringAsync y GetStreamAsync gèrent en interne les délais d'attente, de sorte qu'ils ne lancent JAMAIS.

string baseAddress = "http://localhost:8080/";
var client = new HttpClient() 
{ 
    BaseAddress = new Uri(baseAddress), 
    Timeout = TimeSpan.FromMilliseconds(1) 
};
try
{
    var s = await client.GetAsync();
}
catch(Exception e)
{
    Console.WriteLine(e.Message);
    Console.WriteLine(e.InnerException.Message);
}

5 votes

Je l'ai testé, et GetStreamAsync a lancé un TaskCanceledException pour moi.

48 votes

Comment puis-je savoir si TaskCanceledException est causée par le dépassement du délai HTTP et non, disons, par une annulation directe ou une autre raison ?

11 votes

Contrôle @UserControl TaskCanceledException.CancellationToken.IsCancellationReques‌​ted . Si elle est fausse, vous pouvez être raisonnablement certain qu'il s'agissait d'un délai d'attente.

50voto

CornéM Points 1541

À partir de .NET 5, la mise en œuvre a changé . HttpClient lance toujours un TaskCanceledException mais il s'agit maintenant d'un TimeoutException comme InnerException . Ainsi, vous pouvez facilement vérifier si une demande a été annulée ou si son exécution a été retardée (exemple de code copié de l'article de blog lié) :

try
{
    using var response = await _client.GetAsync("http://localhost:5001/sleepFor?seconds=100");
}
// Filter by InnerException.
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
    // Handle timeout.
    Console.WriteLine("Timed out: "+ ex.Message);
}
catch (TaskCanceledException ex)
{
    // Handle cancellation.
    Console.WriteLine("Canceled: " + ex.Message);   
}

30voto

Jack Points 324

J'ai découvert que la meilleure façon de déterminer si l'appel de service a expiré est d'utiliser un jeton d'annulation et non la propriété timeout du HttpClient :

var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);

Et ensuite gérer l'exception CancellationException pendant l'appel de service...

catch(TaskCanceledException)
{
    if(cts.Token.IsCancellationRequested)
    {
        // Timed Out
    }
    else
    {
        // Cancelled for some other reason
    }
}

Bien sûr, si le délai d'attente se produit du côté du service, il devrait pouvoir être géré par une WebException.

12voto

Thomas Levesque Points 141081

En gros, vous devez attraper le OperationCanceledException et vérifier l'état du jeton d'annulation qui a été transmis à SendAsync (ou GetAsync ou autre HttpClient que vous utilisez) :

  • s'il a été annulé ( IsCancellationRequested est vrai), cela signifie que la demande a réellement été annulée.
  • Si ce n'est pas le cas, cela signifie que la demande a expiré.

Bien sûr, ce n'est pas très pratique... il serait préférable de recevoir une TimeoutException en cas de dépassement de délai. Je propose ici une solution basée sur un gestionnaire de messages HTTP personnalisé : Meilleure gestion des délais d'attente avec HttpClient

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X