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);
}
por favor una pieza mínima de código que muestra este comportamiento. –
¿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 –
@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. –