¿Cómo puedo saber cuándo HttpClient ha excedido el tiempo de espera?

142

Por lo que puedo decir, no hay forma de saber que se haya producido un tiempo de espera específico. ¿No estoy buscando en el lugar correcto o me estoy perdiendo algo más 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);
}

Esto devuelve:

Uno o más errores ocurrieron.

Se canceló una tarea.

Benjol
fuente
3
Podemos votar el problema en GitHub: HttpClient lanza TaskCanceledException en el tiempo de espera # 20296
csrowell el
Gran voto a favor de la pregunta. Además ... ¿alguna idea de cómo hacer esto en UWP? Su Windows.Web.HTTP.HTTPClient no tiene miembro de tiempo de espera. También el método GetAsync no acepta token de cancelación ...
Do-do-new
1
6 años después, y todavía no parece posible saber si un cliente agotó el tiempo de espera.
Steve Smith

Respuestas:

61

Necesitas esperar el GetAsyncmétodo. Luego lanzará un TaskCanceledExceptionsi ha expirado. Además, GetStringAsyncy GetStreamAsyncmanejan internamente el tiempo de espera, por lo que NUNCA tirarán.

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
fuente
2
Probé esto y GetStreamAsyncarrojé uno TaskCanceledExceptionpara mí.
Sam
38
¿Cómo puedo saber si TaskCanceledExceptiones causado por el tiempo de espera HTTP y no, por ejemplo, la cancelación directa u otra razón?
UserControl
8
@UserControl check TaskCanceledException.CancellationToken.IsCancellationRequested. Si es falso, puede estar razonablemente seguro de que fue un tiempo de espera.
Todd Menier
3
Como resultado, no puede contar con IsCancellationRequestedconfigurar el token de la excepción de cancelación directa como pensaba anteriormente: stackoverflow.com/q/29319086/62600
Todd Menier
2
@testing No se comportan de manera diferente. Es solo que tiene un token que representará la solicitud de cancelación del usuario y un interno (no puede acceder y no necesita) que represente el tiempo de espera del cliente. El caso de uso es diferente
Sir Rufo,
59

Estoy reproduciendo el mismo problema y es realmente molesto. He encontrado estos útiles:

HttpClient: manejo de excepciones agregadas

Error en HttpClient.GetAsync debería lanzar WebException, no TaskCanceledException

Algún código en caso de que los enlaces no vayan a ninguna 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!?)
    }
}
vezenkov
fuente
En mi experiencia, WebException no puede ser atrapado en ninguna circunstancia. ¿Están otros experimentando algo diferente?
aplastar
1
@crush WebException puede ser atrapado. Quizás esto ayude.
DavidRR
Esto no funciona para mí si no estoy usando cts. Solo estoy usando la tarea <T> task = SomeTask () try {T result = task.Result} catch (TaskCanceledException) {} catch (Exception e) {} Solo se detecta la excepción general, no la TaskCanceledException. ¿Qué hay de malo en mi versión de código?
Naomi
1
Creé un nuevo informe de error ya que el original parece estar en una publicación archivada del foro: connect.microsoft.com/VisualStudio/feedback/details/3141135
StriplingWarrior
1
Si el token se pasa desde afuera, no es default(CancellationToken)antes de compararlo ex.CancellationToken.
SerG
25

Descubrí que la mejor manera de determinar si la llamada de servicio ha excedido el tiempo de espera es usar un token de cancelación y no la propiedad de tiempo de espera de HttpClient:

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

Y luego maneje la excepción de cancelación durante la llamada de servicio ...

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

Por supuesto, si el tiempo de espera se produce en el lado del servicio de las cosas, eso debería ser manejado por una WebException.

Jack
fuente
1
Hmm, supongo que el operador de negación (que se agregó en una edición) debería eliminarse para que esta muestra tenga sentido. Si cts.Token.IsCancellationRequestedes trueque debe significar que un tiempo de espera ha ocurrido?
Lasse Christiansen
9

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

Una consulta del Sistema de nombres de dominio (DNS) puede demorar hasta 15 segundos en regresar o expirar. Si su solicitud contiene un nombre de host que requiere resolución y establece el Tiempo de espera en un valor inferior a 15 segundos, puede tomar 15 segundos o más antes de que se produzca una WebException para indicar un tiempo de espera en su solicitud .

Luego obtiene acceso a la Statuspropiedad, consulte WebExceptionStatus

usuario247702
fuente
3
Hm, estoy volviendo AggregateExceptioncon un TaskCancelledExceptionadentro. Debo estar haciendo algo mal ...
Benjol
Estas usando catch(WebException e)?
user247702
No, y si lo intento, el AggregateExceptionno se maneja. Si crea un proyecto de consola VS, agregue una referencia System.Net.Httpy suelte el código main, puede verlo usted mismo (si lo desea).
Benjol
55
Si el período de espera excede el tiempo de espera de la tarea, obtendrá un TaskCanceledException. Esto parece ser lanzado por el manejo interno del tiempo de espera del TPL, a un nivel más alto que el HttpWebClient. No parece haber una buena manera de distinguir entre una cancelación de tiempo de espera y una cancelación de usuario. El resultado de esto es que es posible que no obtenga un WebExceptiondentro de su AggregateException.
JT.
1
Como han dicho otros, debe suponer que TaskCanceledException fue el tiempo de espera. Estoy usando try {// Code here} catch (AggregateException exception) {if (exception.InnerExceptions.OfType <TaskCanceledException> () .Any ()) {// Handle timeout here}}
Vdex
8

Básicamente, debe capturar OperationCanceledExceptiony verificar el estado del token de cancelación que se pasó a SendAsync(o GetAsync, o cualquier HttpClientmétodo que esté utilizando):

  • si se canceló ( IsCancellationRequestedes cierto), significa que la solicitud realmente se canceló
  • si no, significa que la solicitud expiró

Por supuesto, esto no es muy conveniente ... sería mejor recibir un TimeoutExceptionen caso de tiempo de espera. Propongo una solución aquí basada en un controlador de mensajes HTTP personalizado: mejor manejo del tiempo de espera con HttpClient

Thomas Levesque
fuente
ah! ¡eres tu! Hoy escribí un comentario en tu blog. Pero WRT a esta respuesta, creo que su punto sobre IsCancellationRequested no es cierto, porque parece que siempre es verdad para mí, cuando yo no cancelo yo mismo
knocte
@knocte eso es extraño ... Pero en ese caso, la solución de mi publicación de blog no lo ayudará, ya que también se basa en esto
Thomas Levesque
1
en el tema de github sobre esto, muchos afirman lo que dije: que IsCancellationRequested es cierto cuando hay un tiempo de espera; así que estoy tentado a rechazar su respuesta;)
llamado el
@knocte, no sé qué decirte ... He estado usando esto durante mucho tiempo y siempre funcionó para mí. ¿Estableciste el HttpClient.Timeoutinfinito?
Thomas Levesque
no, no lo hice porque no puedo controlar HttpClient yo mismo, es una biblioteca de terceros la que estoy usando, la que la usa
tocada el
-1
_httpClient = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(5)};

es lo que suelo hacer, parece funcionar bastante bien para mí, es especialmente bueno cuando uso proxies.

Desarrollo Syv
fuente
1
Así es como configura el tiempo de espera de httpclient. Esto no responde a la pregunta, que era cómo saber cuándo httpclient ha excedido el tiempo de espera.
Ethan Fischer