HttpClient: ¿Se canceló una tarea?
Funciona bien cuando tenemos una o dos tareas, sin embargo, arroja el error "Se canceló una tarea" cuando tenemos más de una tarea en la lista.
List<Task> allTasks = new List<Task>();
allTasks.Add(....);
allTasks.Add(....);
Task.WaitAll(allTasks.ToArray(), configuration.CancellationToken);
private static Task<T> HttpClientSendAsync<T>(string url, object data, HttpMethod method, string contentType, CancellationToken token)
{
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(method, url);
HttpClient httpClient = new HttpClient();
httpClient.Timeout = new TimeSpan(Constants.TimeOut);
if (data != null)
{
byte[] byteArray = Encoding.ASCII.GetBytes(Helper.ToJSON(data));
MemoryStream memoryStream = new MemoryStream(byteArray);
httpRequestMessage.Content = new StringContent(new StreamReader(memoryStream).ReadToEnd(), Encoding.UTF8, contentType);
}
return httpClient.SendAsync(httpRequestMessage).ContinueWith(task =>
{
var response = task.Result;
return response.Content.ReadAsStringAsync().ContinueWith(stringTask =>
{
var json = stringTask.Result;
return Helper.FromJSON<T>(json);
});
}).Unwrap();
}
Hay 2 razones probables por las que TaskCanceledException
se lanzaría un:
- Algo llamó
Cancel()
alCancellationTokenSource
token de cancelación asociado antes de que se completara la tarea. - Se agotó el tiempo de espera de la solicitud, es decir, no se completó dentro del lapso de tiempo especificado en
HttpClient.Timeout
.
Supongo que fue un tiempo muerto. (Si se tratara de una cancelación explícita, probablemente lo habría descubierto). Puede estar más seguro inspeccionando la excepción:
try
{
var response = task.Result;
}
catch (TaskCanceledException ex)
{
// Check ex.CancellationToken.IsCancellationRequested here.
// If false, it's pretty safe to assume it was a timeout.
}
Me encontré con este problema porque mi Main()
método no esperaba a que se completara la tarea antes de regresar, por lo que se Task<HttpResponseMessage>
canceló cuando se cerró el programa de mi consola.
C# ≥ 7,1
Puede hacer que el método principal sea asincrónico y esperar la tarea.
public static async Task Main(){
Task<HttpResponseMessage> myTask = sendRequest(); // however you create the Task
HttpResponseMessage response = await myTask;
// process the response
}
C#<7.1
La solución fue llamar myTask.GetAwaiter().GetResult()
( Main()
de esta respuesta ).
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);
Lo anterior es el mejor enfoque para esperar una solicitud grande. Estás confundido unos 30 minutos; Es un tiempo aleatorio y puedes dar el tiempo que quieras.
En otras palabras, la solicitud no esperará 30 minutos si obtienen resultados antes de los 30 minutos. 30 min significa que el tiempo de procesamiento de la solicitud es de 30 min. Cuando se produjo el error "La tarea fue cancelada" o se solicitó una gran cantidad de datos.
Otra posibilidad es que no se espere el resultado por parte del cliente. Esto puede suceder si algún método en la pila de llamadas no usa la palabra clave await para esperar a que se complete la llamada.
Promocionando el comentario de @JobaDiniz a una respuesta:
No haga lo obvio y deseche la HttpClient
instancia, aunque el código "parezca correcto":
async Task<HttpResponseMessage> Method() {
using (var client = new HttpClient())
return client.GetAsync(request);
}
¡ La eliminación de la instancia puede provocar que se cancelen HttpClient
las siguientes solicitudes HTTP iniciadas por otras instancias !HttpClient
Lo mismo ocurre con la nueva sintaxis RIAA de C#; un poco menos obvio:
async Task<HttpResponseMessage> Method() {
using var client = new HttpClient();
return client.GetAsync(request);
}
En cambio, HttpClient
la interfaz es un poco tosca y la forma correcta de usarla en dotnet core 2.1+ es:
Utilice una instancia estática o singleton
HttpClient
conPooledConnectionLifetime
...
Básicamente, almacene en caché una instancia estática de HttpClient
su aplicación o biblioteca y reutilícela:
static HttpClient client = new HttpClient();
async Task<HttpResponseMessage> Method() {
return client.GetAsync(request);
}
(Todos los Async()
métodos de solicitud son seguros para subprocesos ).
Sin embargo, Microsoft recomienda no utilizar directamente new HttpClient()
! En su lugar, utilice .NET Framework tanto para dotnet core como para .NET Framework IHttpClientFactory
. Una advertencia: los documentos IHttpClientFactory
suelen ser para aplicaciones ASP.NET, pero es posible usarlos con cualquier aplicación CLI/GUI ).
(Para .NET Framework, "puede agregar el paquete Microsoft.Extensions.Http NuGet a cualquier proyecto compatible con .NET Standard 2.0"; consulte esta respuesta ).