Jak mogę sprawdzić, kiedy upłynął limit czasu HttpClient?


142

O ile wiem, nie ma sposobu, aby dowiedzieć się, że nastąpił konkretny limit czasu. Czy nie szukam we właściwym miejscu, czy brakuje mi czegoś większego?

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

To zwraca:

Wystąpił jeden lub więcej błędów.

Zadanie zostało anulowane.



Ogromne głosy za pytaniem. Poza tym ... masz pomysł, jak to zrobić na UWP? Jego Windows.Web.HTTP.HTTPClient nie ma elementu limitu czasu. Również metoda GetAsync nie akceptuje tokena anulowania ...
Do-do-new

1
6 lat później nadal nie można stwierdzić, czy upłynął limit czasu klienta.
Steve Smith

Odpowiedzi:


61

Musisz poczekać na GetAsyncmetodę. Następnie wyrzuci, TaskCanceledExceptionjeśli upłynął limit czasu. Dodatkowo GetStringAsynci GetStreamAsyncwewnętrznie obsługują limit czasu, więc NIGDY nie będą rzucać.

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

2
Przetestowałem to i GetStreamAsyncwrzuciłem TaskCanceledExceptiondla mnie.
Sam

38
Jak mogę sprawdzić, czy TaskCanceledExceptionjest to spowodowane przekroczeniem limitu czasu HTTP, a nie, powiedzmy bezpośrednim anulowaniem lub innym powodem?
UserControl

8
@UserControl check TaskCanceledException.CancellationToken.IsCancellationRequested. Jeśli fałszywe, możesz być dość pewny, że był to limit czasu.
Todd Menier

3
Jak się okazuje, nie można liczyć na IsCancellationRequestedustawienie na tokenie wyjątku przy bezpośrednim anulowaniu, jak wcześniej myślałem: stackoverflow.com/q/29319086/62600
Todd Menier

2
@testing Nie zachowują się inaczej. Po prostu masz jeden token, który będzie reprezentował żądanie anulowania użytkownika i wewnętrzny (nie możesz uzyskać dostępu i nie potrzebujesz), który reprezentuje limit czasu klienta. Jest to inny przypadek użycia
Sir Rufo

59

Odtwarzam ten sam problem i jest to naprawdę denerwujące. Znalazłem te przydatne:

HttpClient - obsługa wyjątków agregujących

Błąd w HttpClient.GetAsync powinien zgłaszać WebException, a nie TaskCanceledException

Jakiś kod na wypadek, gdyby linki nigdzie nie prowadziły:

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!?)
    }
}

Z mojego doświadczenia wynika, że ​​WebException nie można złapać w żadnych okolicznościach. Czy inni doświadczają czegoś innego?
zmiażdżyć

1
@crush WebException można złapać. Może to pomoże.
DavidRR

To nie działa, jeśli nie używam cts. Po prostu używam Task <T> task = SomeTask () try {T result = task.Result} catch (TaskCanceledException) {} catch (Exception e) {} Tylko ogólny wyjątek jest przechwytywany, a nie TaskCanceledException. Co jest nie tak w mojej wersji kodu?
Naomi

1
Utworzyłem nowy raport o błędzie, ponieważ pierwotny wydaje się być w zarchiwizowanym poście na forum: connect.microsoft.com/VisualStudio/feedback/details/3141135
StriplingWarrior

1
Jeśli token jest przekazywany z zewnątrz, to nie jest default(CancellationToken)przed porównaniem z ex.CancellationToken.
SerG

25

Zauważyłem, że najlepszym sposobem ustalenia, czy upłynął limit czasu wywołania usługi, jest użycie tokenu anulowania, a nie właściwości limitu czasu HttpClient:

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

A następnie obsłuż wyjątek CancellationException podczas wezwania serwisowego ...

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

Oczywiście, jeśli przekroczenie limitu czasu występuje po stronie usług, powinno to być obsługiwane przez WebException.


1
Hmm, myślę, że operator negacji (który został dodany w edycji) powinien zostać usunięty, aby ta próbka miała sens? Jeśli cts.Token.IsCancellationRequestedjest trueto musi oznaczać, że limit czasu wystąpił?
Lasse Christiansen

9

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

Zapytanie systemu nazw domen (DNS) może zająć do 15 sekund, zanim powróci lub przekroczy limit czasu. Jeśli żądanie zawiera nazwę hosta, która wymaga rozwiązania, i ustawisz limit czasu na wartość mniejszą niż 15 sekund, może minąć 15 sekund lub więcej, zanim zostanie zgłoszony wyjątek WebException w celu wskazania limitu czasu w żądaniu .

Następnie uzyskasz dostęp do Statuswłaściwości, zobacz WebExceptionStatus


3
Hm, wracam AggregateExceptionz TaskCancelledExceptionwnętrzem. Muszę robić coś złego ...
Benjol

Czy używasz catch(WebException e)?
user247702

Nie, a jeśli spróbuję, AggregateExceptionjest nieobsługiwany. Jeśli tworzysz projekt konsoli VS, dodaj odwołanie do System.Net.Httpi upuść kod do main, możesz się o tym przekonać (jeśli chcesz).
Benjol

5
Jeśli okres oczekiwania przekroczy limit czasu zadania, otrzymasz plik TaskCanceledException. Wydaje się, że jest to spowodowane wewnętrzną obsługą limitu czasu TPL na wyższym poziomie niż HttpWebClient. Wydaje się, że nie ma dobrego sposobu na rozróżnienie między anulowaniem limitu czasu a anulowaniem użytkownika. W rezultacie możesz nie mieć WebExceptionw swoim AggregateException.
JT.

1
Jak powiedzieli inni, musisz założyć, że wyjątek TaskCanceledException był przekroczeniem limitu czasu. Używam try {// Kod tutaj} catch (wyjątek AggregateException) {if (wyjątek.InnerExceptions.OfType <TaskCanceledException> () .Any ()) {//
Limit

8

Zasadniczo musisz przechwycić OperationCanceledExceptioni sprawdzić stan tokenu anulowania, który został przekazany do SendAsync( GetAsynclub dowolnej HttpClientmetody, której używasz):

  • jeśli zostało anulowane ( IsCancellationRequestedprawda), oznacza to, że żądanie rzeczywiście zostało anulowane
  • jeśli nie, oznacza to, że upłynął limit czasu żądania

Oczywiście nie jest to zbyt wygodne ... lepiej byłoby otrzymać TimeoutExceptionw przypadku przekroczenia limitu czasu. Proponuję tutaj rozwiązanie oparte na niestandardowej obsłudze komunikatów HTTP: Lepsza obsługa limitu czasu dzięki HttpClient


ach! to ty! Napisałem dzisiaj komentarz na Twoim blogu. Ale patrząc na tę odpowiedź, myślę, że twój punkt dotyczący IsCancellationRequested nie jest prawdziwy, ponieważ wydaje mi się, że jest zawsze prawdziwy dla mnie, kiedy sam tego nie anulowałem
knocte

@knocte to dziwne ... Ale w takim przypadku rozwiązanie z mojego posta na blogu ci nie pomoże, ponieważ opiera się na tym
Thomas Levesque

1
w kwestii github na ten temat wielu twierdzi, co powiedziałem: że IsCancellationRequested jest prawdą, gdy jest limit czasu; więc kusi mnie, aby zlekceważyć twoją odpowiedź;)
knocte

@knocte, nie wiem, co ci powiedzieć ... Używam tego przez długi czas i zawsze działało. Czy ustawiłeś na HttpClient.Timeoutnieskończoność?
Thomas Levesque

nie, nie zrobiłem tego, ponieważ nie mogę sam kontrolować HttpClient, jest to biblioteka innej firmy, której używam, ta, która jej używa
knocte

-1
_httpClient = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(5)};

jest tym, co zwykle robię, wydaje mi się, że działa całkiem nieźle, jest szczególnie dobry, gdy używam proxy.


1
W ten sposób ustawiasz limit czasu httpclient. Nie dotyczy to pytania, w jaki sposób można stwierdzić, kiedy upłynął limit czasu httpclient.
Ethan Fischer
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.