Wie kann ich feststellen, wann das Zeitlimit für HttpClient abgelaufen ist?

141

Soweit ich das beurteilen kann, gibt es keine Möglichkeit zu wissen, dass es sich speziell um eine Zeitüberschreitung handelt. Suche ich nicht am richtigen Ort oder vermisse ich etwas Größeres?

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);
}

Dies gibt zurück:

Ein oder mehrere Fehler sind aufgetreten.

Eine Aufgabe wurde abgebrochen.

Benjol
quelle
3
Wir können das Problem auf GitHub verbessern
csrowell
Riesige Gegenstimme für die Frage. Auch ... eine Idee, wie man das auf UWP macht? Der Windows.Web.HTTP.HTTPClient hat kein Timeout-Mitglied. Auch GetAsync Methode akzeptiert kein Stornierungstoken ...
Do-do-new
1
6 Jahre später, und es scheint immer noch nicht möglich zu sein, zu wissen, ob ein Kunde eine Zeitüberschreitung hat.
Steve Smith

Antworten:

61

Sie müssen die GetAsyncMethode abwarten . Es wird dann ein geworfen, TaskCanceledExceptionwenn das Zeitlimit abgelaufen ist. Zusätzlich GetStringAsyncund GetStreamAsyncintern mit Timeout umgehen, damit sie NIEMALS werfen.

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);
}
Murkaeus
quelle
2
Ich habe das getestet und GetStreamAsyncein TaskCanceledExceptionfür mich geworfen.
Sam
38
Wie kann ich feststellen, ob dies TaskCanceledExceptiondurch ein HTTP-Timeout verursacht wird und nicht, beispielsweise durch direkte Stornierung oder einen anderen Grund?
UserControl
8
@ UserControl überprüfen TaskCanceledException.CancellationToken.IsCancellationRequested. Wenn dies falsch ist, können Sie ziemlich sicher sein, dass es sich um eine Zeitüberschreitung handelt.
Todd Menier
3
Wie sich herausstellt, können Sie nicht damit rechnen IsCancellationRequested, bei der direkten Stornierung auf das Token der Ausnahme gesetzt zu werden, wie ich zuvor dachte: stackoverflow.com/q/29319086/62600
Todd Menier
2
@testing Sie verhalten sich nicht anders. Es ist nur so, dass Sie ein Token haben, das die Benutzer-Stornierungsanforderung darstellt, und ein internes Token (auf das Sie nicht zugreifen können und das Sie nicht benötigen), das das Client-Timeout darstellt. Es ist der Anwendungsfall, der sich unterscheidet
Sir Rufo
59

Ich reproduziere das gleiche Problem und es ist wirklich ärgerlich. Ich habe diese nützlich gefunden:

HttpClient - Umgang mit aggregierten Ausnahmen

Ein Fehler in HttpClient.GetAsync sollte eine WebException auslösen, keine TaskCanceledException

Etwas Code für den Fall, dass die Links nirgendwo hingehen:

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!?)
    }
}
vezenkov
quelle
Nach meiner Erfahrung kann WebException unter keinen Umständen abgefangen werden. Erleben andere etwas anderes?
Crush
1
@crush WebException kann gefangen werden. Vielleicht dies helfen.
DavidRR
Dies funktioniert bei mir nicht, wenn ich kein cts verwende. Ich verwende nur Task <T> task = SomeTask () try {T result = task.Result} catch (TaskCanceledException) {} catch (Ausnahme e) {} Nur die allgemeine Ausnahme wird abgefangen, nicht die TaskCanceledException. Was ist in meiner Codeversion falsch?
Naomi
1
Ich habe einen neuen Fehlerbericht erstellt, da der ursprüngliche in einem archivierten Forumsbeitrag zu sein scheint: connect.microsoft.com/VisualStudio/feedback/details/3141135
StriplingWarrior
1
Wenn das Token von außen übergeben wird, erfolgt dies nicht default(CancellationToken)vor dem Vergleich mit ex.CancellationToken.
SerG
25

Ich habe festgestellt, dass der beste Weg, um festzustellen, ob das Zeitlimit für den Serviceabruf abgelaufen ist, die Verwendung eines Stornierungs-Tokens und nicht der Timeout-Eigenschaft des HttpClient ist:

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

Und dann die CancellationException während des Serviceabrufs behandeln ...

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

Wenn das Timeout auf der Serviceseite auftritt, sollte dies natürlich von einer WebException verarbeitet werden können.

Jack
quelle
1
Hmm, ich denke, dass der Negationsoperator (der in einer Bearbeitung hinzugefügt wurde) entfernt werden sollte, damit dieses Beispiel Sinn macht? Wenn cts.Token.IsCancellationRequestedist truees muss bedeuten , dass ein Timeout aufgetreten ist?
Lasse Christiansen
9

Von http://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout.aspx

Es kann bis zu 15 Sekunden dauern, bis eine DNS-Abfrage (Domain Name System) zurückgegeben wird oder eine Zeitüberschreitung auftritt. Wenn Ihre Anfrage einen Hostnamen enthält, für den eine Auflösung erforderlich ist, und Sie Timeout auf einen Wert von weniger als 15 Sekunden festlegen , kann es 15 Sekunden oder länger dauern, bis eine WebException ausgelöst wird, um ein Timeout für Ihre Anfrage anzuzeigen .

Sie erhalten dann Zugriff auf die StatusEigenschaft, siehe WebExceptionStatus

user247702
quelle
3
Hm, ich komme AggregateExceptionmit einem TaskCancelledExceptionInneren zurück. Ich muss etwas falsch machen ...
Benjol
Sind Sie mit catch(WebException e)?
user247702
Nein, und wenn ich es versuche, AggregateExceptionwird das nicht behandelt. Wenn Sie ein VS-Konsolenprojekt erstellen, einen Verweis auf System.Net.Httpden Code hinzufügen und ihn dort ablegen main, können Sie sich selbst davon überzeugen (wenn Sie möchten).
Benjol
5
Wenn die Wartezeit die Zeitüberschreitung der Aufgabe überschreitet, erhalten Sie eine TaskCanceledException. Dies scheint durch die interne Timeout-Behandlung der TPL auf einer höheren Ebene als der ausgelöst zu werden HttpWebClient. Es scheint keine gute Möglichkeit zu geben, zwischen einer Timeout-Stornierung und einer Benutzer-Stornierung zu unterscheiden. Das Ergebnis davon ist, dass Sie möglicherweise keine WebExceptionin Ihrem bekommen AggregateException.
JT.
1
Wie andere gesagt haben, müssen Sie davon ausgehen, dass die TaskCanceledException das Timeout war. Ich verwende try {// Code hier} catch (AggregateException-Ausnahme) {if (Ausnahme.InnerExceptions.OfType <TaskCanceledException> () .Any ()) {// Zeitüberschreitung hier behandeln}}
Vdex
7

Grundsätzlich müssen Sie die fangen OperationCanceledExceptionund prüfen Sie den Zustand des Widerrufs Token dass weitergegeben wurde SendAsync(oder GetAsync, oder was auch immer HttpClientMethode Sie verwenden):

  • wenn es abgesagt wurde (IsCancellationRequested ist wahr), bedeutet dies, dass die Anfrage wirklich storniert wurde
  • Wenn nicht, bedeutet dies, dass die Anforderung abgelaufen ist

Natürlich ist dies nicht sehr praktisch ... es wäre besser, TimeoutExceptionim Falle einer Zeitüberschreitung eine zu erhalten . Ich schlage hier eine Lösung vor, die auf einem benutzerdefinierten HTTP-Nachrichtenhandler basiert: Bessere Timeout-Behandlung mit HttpClient

Thomas Levesque
quelle
Ah! du bist es! Ich habe heute früher einen Kommentar in Ihrem Blogpost geschrieben. Aber in Bezug auf diese Antwort denke ich, dass Ihr Punkt über IsCancellationRequested nicht wahr ist, weil es für mich immer wahr zu sein scheint, wenn ich es nicht selbst
abgesagt
@knocte das ist komisch ... Aber in diesem Fall hilft dir die Lösung aus meinem Blog-Beitrag nicht weiter, da sie auch darauf beruht
Thomas Levesque
1
In der Github-Ausgabe darüber behaupten viele, was ich gesagt habe: dass IsCancellationRequested wahr ist, wenn es eine Zeitüberschreitung gibt; deshalb bin ich versucht, deine Antwort herabzustimmen;)
knocte
@knocte, ich weiß nicht, was ich dir sagen soll ... Ich benutze das schon lange und es hat immer bei mir funktioniert. Hast du das HttpClient.Timeoutauf unendlich gesetzt?
Thomas Levesque
Nein, ich habe es nicht getan, weil ich HttpClient nicht selbst steuern kann. Es ist eine Drittanbieter-Bibliothek, die ich verwende, die es verwendet
Knocte
-1
_httpClient = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(5)};

ist das, was ich normalerweise mache, scheint ziemlich gut für mich zu funktionieren, es ist besonders gut, wenn ich Proxys benutze.

Syv-Entwicklung
quelle
1
So richten Sie das Timeout von httpclient ein. Hiermit wird die Frage nicht beantwortet. Wie können Sie feststellen, wann das Zeitlimit für httpclient abgelaufen ist?
Ethan Fischer