2012-10-01 22 views
24

HttpClient tiene una función de tiempo de espera interno (a pesar de ser todo asíncrono, es decir, los tiempos de espera pueden considerarse ortogonales a la funcionalidad de solicitud http y por lo tanto ser manejado por utilidades asíncronas genéricas, pero aparte) y cuando expira el tiempo de espera , lanzará un TaskCanceledException (envuelto en un AggregateException).Distinguir el tiempo de espera de la cancelación del usuario

El TCE contiene un CancellationToken que equivale a CancellationToken.None.

Ahora bien, si proporciono HttpClient con un CancellationToken de mi propia y usarlo para cancelar la operación antes de que termine (o el tiempo de espera), tengo la misma exacta TaskCanceledException, de nuevo con un CancellationToken.None.

¿Existe todavía una forma, mirando solamente la excepción lanzada, para averiguar si un tiempo de espera de la solicitud cancelada, sin tener que hacer mi propia CancellationToken accesible para el código que comprueba la excepción?

P.S. ¿Podría ser un error y CancellationToken se corrigió de alguna manera erróneamente en CancellationToken.None? En el cancelado utilizando el caso Custom CancellationToken, esperaría que TaskCanceledException.CancellationToken iguale ese token personalizado.

Editar Para hacer que el problema un poco más clara, con acceso a la original CancellationTokenSource, es fácil de distinguir de tiempo de espera y el usuario cancelación:

== origCancellationTokenSource.IsCancellationRequested cierto

Obteniendo el CancellationToken de la excepción, da la respuesta incorrecta:

((TaskCanceledException) e.InnerException) .CancellationToken.IsCancellationRequested == falsa

Aquí un ejemplo mínimo, debido a la demanda popular:

public void foo() 
{ 
    makeRequest().ContinueWith(task => 
    { 
     try 
     { 
      var result = task.Result; 
      // do something with the result; 
     } 
     catch (Exception e) 
     { 
      TaskCanceledException innerException = e.InnerException as TaskCanceledException; 
      bool timedOut = innerException != null && innerException.CancellationToken.IsCancellationRequested == false; 

      // Unfortunately, the above .IsCancellationRequested 
      // is always false, no matter if the request was 
      // cancelled using CancellationTaskSource.Cancel() 
      // or if it timed out 
     } 
    }); 
} 

public Task<HttpResponseMessage> makeRequest() 
{ 
    var cts = new CancellationTokenSource(); 
    HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(10) }; 
    HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "url"); 

    passCancellationTokenToOtherPartOfTheCode(cts); 
    return client.SendAsync(httpRequestMessage, cts.Token); 
} 
+0

por favor una pieza mínima de código que muestra este comportamiento. –

+0

¿Por qué no es el código que inicia la tarea que maneja la excepción?Ese trozo de código ya debería tener el CancelToken, podría manejar ese escenario y luego lanzar la excepción que desea que reciba su bloque Try/catch de nivel superior, sin el TokenSource –

+1

@FranciscoNoriega. A veces quiere separar el código, p. para que sea reutilizable, y también en código ejecutado de forma asíncrona. Entonces terminas capturando excepciones en diferentes lugares que donde se define el código ejecutado. No quiero tener que aprobar todo el estado involucrado, especialmente porque la excepción parece proporcionar ese estado cuando es necesario, excepto que no funciona como creo que se supone que debe hacerlo. Eche un vistazo al ejemplo simplificado que muestra aproximadamente cómo lo uso. –

Respuesta

4

Sí, ambos devuelven la misma excepción (posiblemente debido al tiempo de espera internamente usando un token también) pero se puede resolver fácilmente haciendo esto:

catch (OperationCanceledException ex) 
      { 
       if (token.IsCancellationRequested) 
       { 
        return -1; 
       } 

       return -2; 
      } 

así que básicamente si se golpea la excepción, sino que su ficha no es cancelado, así que fue un tiempo de espera de HTTP normal

+0

Hice ** mirando solo la excepción arrojada ** en mi pregunta en negrita y agregué un poco más de detalles para dejar más claro lo que estoy preguntando. –

+0

'OperationCanceledException' también se lanza si el' HttpClient' se elimina mientras la solicitud está pendiente, o si se llama a 'HttpClient.CancelPendingRequests'. Estos casos se pueden distinguir si controla las llamadas a estos métodos. – Ernesto

+0

esta respuesta carece de detalles ... ¿de dónde viene 'token'? ¿es una propiedad de la excepción? downvoting hasta que lo mejore – ympostor

5

La respuesta aceptada es ciertamente cómo este debe trabajo en la teoría, pero, por desgracia, en la práctica IsCancellationRequested hace no (fiable) consiga el sistema en la ficha que se adjunta a la excepción:

Cancelling an HttpClient Request - Why is TaskCanceledException.CancellationToken.IsCancellationRequested false?

+0

es posible que no pueda usar el token adjunto, pero si tiene el token original (que se pasó a 'SendAsync'), puede usarlo. ¿O me estoy perdiendo algo? –

+0

Veo, me falta que la pregunta requirió depender únicamente de los datos de excepción. –

Cuestiones relacionadas