ASP.NET Core -  Usando CancellationToken
 Hoje vamos recordar como usar o recurso CancellationToken em uma Web API.

 

O recurso CancellationToken é uma struct que é utilizada para propagar a notificação do cancelamento de uma operação e pode ser usado para permitir o cancelamento cooperativo entre threads, itens de trabalho do pool de threads ou de objetos Task.
 

 

Geralmente criamos um token de cancelamento instanciando um objeto CancellationTokenSource que gerencia tokens de cancelamento recuperados de propriedade CancellationTokenSource.Token.

 

A seguir passamos o token de cancelamento para qualquer número de threads, tarefas ou operações que devem receber aviso de cancelamento. O token não pode ser usado para iniciar o cancelamento.

 

Quando o objeto proprietário chama CancellationTokenSource.Cancel, a IsCancellationRequested propriedade em cada cópia do token de cancelamento é definida como true. Os objetos que recebem a notificação podem responder de qualquer maneira apropriada.

 

Para mostrar na prática o uso do CancellationToken de forma bem simples, vamos criar uma Web API usando o template padrão do Visual Studio 2022 : ASP.NET Core Web API.
 

Teremos um projeto padrão criado que vai exibir a previsão do tempo de forma aleatória.

 

O código do controlador WeatherForecastController gerado é exibido a seguir:

 

using Microsoft.AspNetCore.Mvc;

namespace ApiCancellationToken.Controllers;
 

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

 

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

 

    [HttpGet(Name = "GetWeatherForecast")]
    public async Task<IEnumerable<WeatherForecast>> Get()
    {
        _logger.LogInformation("Iniciando o Request...");
       

        await Task.Delay(10_000);

        _logger.LogInformation("Finalizando o Request...");

        var result = Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
 

        _logger.LogInformation($"Resultados :  {result.Length}");


        return result;
    }
}


As linhas de código em destaque foram incluídas para testar a execução do código.

 

Vamos agora abrir duas janelas no navegador padrão e definir a url para acionar o endpoint.

 

Para este exemplo o endpoint usado é :

https://localhost:7017/WeatherForecast

Após informar a url completa em cada aba do navegador pressione enter na primeira aba, a seguir abra a segunda aba e pressione enter mas a seguir feche o navegador.

 

Como temos um delay de 10 segundos podemos verificar na janela Output o seguinte resultado:
 

 

Observe que embora tenhamos cancelado a execução do segundo request, cada request foi completado exibindo o resultado final da operação.

 

Assim, se nossa aplicação usasse um banco de dados a requisição seria enviada para o banco, mesmo cancelando o request por qualquer motivo.

 

Para evitar este problema podemos usar o CancellationToken alterando o código o método Get conforme abaixo:
 

    [HttpGet(Name = "GetWeatherForecast")]
    public async Task<IEnumerable<WeatherForecast>>
Get(CancellationToken ct)
    {
        try
        {

            _logger.LogInformation("Iniciando o Request...");

            await Task.Delay(5_000, ct);

            _logger.LogInformation("Finalizando o Request...");

            var result = Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();

            _logger.LogInformation($"Resultados :  {result.Length}");

            return result;
        }
      
  catch(TaskCanceledException)
        {

         
 
throw new Exception(cte.Message);
       
//throw;

        }

    }

 

O código em destaque mostra o uso do CancellationToken e lança uma TaskCanceledException.

 

Vamos executar o projeto e repetir o procedimento realizado e ver o resultado.

 

 

Observe que agora temos a notificação de cancelamento  propagada causando o lançamento da exceção do tipo TaskCanceledException.

 

Embora o exemplo seja o mais básico possível ele mostra os benefícios em usar o recurso CancellationToken nestes cenários.  Dentre outras vantagens podemos citar:

Em resumo, fornecer um token de cancelamento é uma boa prática, pois economiza recursos e tempo.



Isaías 9:6
Porque um menino nos nasceu, um filho se nos deu, e o principado está sobre os seus ombros, e se chamará o seu nome: Maravilhoso, Conselheiro, Deus Forte, Pai da Eternidade, Príncipe da Paz.

Isaías 9:6

Referências:


José Carlos Macoratti