Come posso sapere quando HttpClient è scaduto?


142

Per quanto ne so, non c'è modo di sapere che si è verificato in particolare un timeout. Non sto cercando nel posto giusto o mi sto perdendo qualcosa di più grande?

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

Questo ritorna:

Si sono verificati uno o più errori.

Un'attività è stata annullata.


3
Possiamo risolvere il problema su GitHub: HttpClient genera TaskCanceledException al timeout # 20296
csrowell

Enorme voto per la domanda. Inoltre ... hai idea di come farlo su UWP? Windows.Web.HTTP.HTTPClient non ha un membro di timeout. Anche il metodo GetAsync non accetta il token di annullamento ...
Do-do-new

1
6 anni dopo, e non sembra ancora possibile sapere se un client è scaduto.
Steve Smith,

Risposte:


61

Devi aspettare il GetAsyncmetodo. Quindi lancerà un TaskCanceledExceptionse è scaduto. Inoltre, GetStringAsynce GetStreamAsyncinternamente gestire timeout, quindi non potranno mai buttare.

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
Ho provato questo e ho GetStreamAsynclanciato un regalo TaskCanceledExceptionper me.
Sam,

38
Come posso sapere se TaskCanceledExceptionè causato dal timeout HTTP e non, dire la cancellazione diretta o altri motivi?
UserControl

8
Controllo @UserControl TaskCanceledException.CancellationToken.IsCancellationRequested. Se falso, puoi essere ragionevolmente certo che si è trattato di un timeout.
Todd Menier,

3
A quanto pare, non puoi contare sul fatto di IsCancellationRequestedessere impostato sul token dell'eccezione sulla cancellazione diretta come pensavo in precedenza: stackoverflow.com/q/29319086/62600
Todd Menier

2
@testing Non si comportano diversamente. È solo che hai un token che rappresenterà la richiesta di annullamento dell'utente e un interno (non puoi accedere e non hai bisogno) che rappresenta il timeout del client. È il caso d'uso che differisce
Sir Rufo il

59

Sto riproducendo lo stesso problema ed è davvero fastidioso. Ho trovato questi utili:

HttpClient: gestione delle eccezioni aggregate

Il bug in HttpClient.GetAsync dovrebbe generare WebException, non TaskCanceledException

Alcuni codici nel caso in cui i collegamenti non vadano da nessuna parte:

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

Nella mia esperienza, WebException non può essere catturato in nessuna circostanza. Gli altri stanno vivendo qualcosa di diverso?
schiaccia il

1
@crush WebException può essere catturato. Forse questo aiuterà.
DavidRR,

Questo non funziona per me se non sto usando cts. Sto solo usando Task <T> task = SomeTask () try {T result = task.Result} catch (TaskCanceledException) {} catch (Exception e) {} Viene rilevata solo l'eccezione generale, non TaskCanceledException. Cosa c'è di sbagliato nella mia versione di codice?
Naomi,

1
Ho creato una nuova segnalazione di bug poiché quella originale sembra trovarsi in un post del forum archiviato: connect.microsoft.com/VisualStudio/feedback/details/3141135
StriplingWarrior

1
Se il token viene passato dall'esterno, controlla che non lo sia default(CancellationToken)prima di confrontarlo ex.CancellationToken.
SerG,

25

Ho scoperto che il modo migliore per determinare se la chiamata di servizio è scaduta è utilizzare un token di annullamento e non la proprietà di timeout di HttpClient:

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

E quindi gestire la CancellationException durante la chiamata di servizio ...

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

Ovviamente se il timeout si verifica dal lato del servizio, ciò dovrebbe essere in grado di essere gestito da una WebException.


1
Hmm, immagino che l'operatore di negazione (che è stato aggiunto in una modifica) dovrebbe essere rimosso per questo esempio per avere un senso? Se cts.Token.IsCancellationRequestedè truesi deve significare che un timeout si è verificato?
Lasse Christiansen,

9

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

Una query DNS (Domain Name System) può richiedere fino a 15 secondi per la restituzione o il timeout. Se la richiesta contiene un nome host che richiede una risoluzione e si imposta Timeout su un valore inferiore a 15 secondi, potrebbero essere necessari 15 secondi o più prima che venga lanciata una WebException per indicare un timeout sulla richiesta .

È quindi possibile accedere alla Statusproprietà, consultare WebExceptionStatus


3
Sto tornando AggregateExceptioncon l' TaskCancelledExceptioninterno. Devo fare qualcosa di sbagliato ...
Benjol

Stai usando catch(WebException e)?
user247702,

No, e se ci provo, il AggregateExceptionnon è gestito. Se si crea un progetto console VS, si aggiunge un riferimento System.Net.Httpe si inserisce il codice in main, è possibile vedere di persona (se si desidera).
Benjol,

5
Se il periodo di attesa supera il periodo di timeout dell'attività, otterrai un TaskCanceledException. Questo sembra essere generato dalla gestione del timeout interno del TPL, a un livello superiore rispetto al HttpWebClient. Non sembra esserci un buon modo per distinguere tra una cancellazione del timeout e una cancellazione dell'utente. Il risultato è che potresti non averne uno WebExceptiondentro AggregateException.
JT.

1
Come altri hanno già detto, devi supporre che TaskCanceledException fosse il timeout. Sto usando try {// Code here} catch (eccezione AggregateException) {if (exception.InnerExceptions.OfType <TaskCanceledException> () .Any ()) {//
Gestisci

8

Fondamentalmente, devi catturare OperationCanceledExceptione controllare lo stato del token di annullamento a cui è stato passato SendAsync(o GetAsync, o qualunque HttpClientmetodo tu stia usando):

  • se è stato annullato ( IsCancellationRequestedè vero), significa che la richiesta è stata effettivamente annullata
  • in caso contrario, significa che la richiesta è scaduta

Certo, questo non è molto conveniente ... sarebbe meglio ricevere un TimeoutExceptionin caso di timeout. Propongo qui una soluzione basata su un gestore di messaggi HTTP personalizzato: una migliore gestione del timeout con HttpClient


ah! sei tu! Oggi ho scritto un commento nel tuo post sul blog. Ma a proposito di questa risposta, penso che il tuo punto su IsCancellationRequested non sia vero, perché sembra che sia sempre vero per me, quando non l'ho annullato da solo
knocte

@knocte è strano ... Ma in quel caso, la soluzione dal mio post sul blog non ti aiuterà, dal momento che si basa anche su questo
Thomas Levesque,

1
nella questione github su questo, molti sostengono quello che ho detto: che IsCancellationRequested è vero quando c'è un timeout; quindi sono tentato di sottovalutare la tua risposta;)
knocte

@knocte, non so cosa dirti ... Lo sto usando da molto tempo e ha sempre funzionato per me. Hai impostato l' HttpClient.Timeoutinfinito?
Thomas Levesque,

no non l'ho fatto perché non posso controllare HttpClient da solo, è una libreria di terze parti che sto usando, quella che lo usa
knocte

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

è quello che faccio di solito, sembra funzionare abbastanza bene per me, è particolarmente buono quando uso i proxy.


1
Ecco come impostare il timeout di httpclient. Questo non affronta la domanda, che era come si fa a sapere quando httpclient è scaduto.
Ethan Fischer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.