Handling aborted requests in ASP.NET Core
When a user makes an HTTP request to an ASP.NET Core application, the server parses the request, generates a response, and sends the result to the client. The user can abort the request while the server is processing it. For instance, the user can navigate to another page or close the page. In this case, you may want to stop all the work in progress to avoid consuming resources. For instance, you may want to cancel SQL requests, HTTP calls, CPU intensive operations, etc.
ASP.NET Core provides the HttpContext.RequestAborted
property to detect when the client disconnect. You can check the property IsCancellationRequested
to know if the client has aborted the connection. You can also use the cancellation token when you query the database, call an HTTP service, etc.
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public async Task<IReadOnlyCollection<WeatherForecast>> Get()
{
CancellationToken cancellationToken = HttpContext.RequestAborted;
if (cancellationToken.IsCancellationRequested)
{
// The client has aborted the request
}
return await GetData(cancellationToken);
}
private async Task<IReadOnlyCollection<WeatherForecast>> GetData(CancellationToken cancellationToken)
{
await Task.Delay(1000, cancellationToken); // Simulate a long process
return Array.Empty<WeatherForecast>();
}
}
You can also add a parameter of type CancellationToken
to the action. The ModelBinder will set the value of any parameter of type CancellationToken
to HttpContext.RequestAborted
.
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
// Get the cancellation token from ModelBinding
[HttpGet("Get")]
public async Task<IReadOnlyCollection<WeatherForecast>> Get(CancellationToken cancellationToken)
{
// cancellationToken == HttpContext.RequestAborted
return await GetData(cancellationToken);
}
private async Task<IReadOnlyCollection<WeatherForecast>> GetData(CancellationToken cancellationToken)
{
// Simulate a long process
await Task.Delay(1000, cancellationToken);
return Array.Empty<WeatherForecast>();
}
}
This also works in a Razor page:
@page
<div>
@* Using HttpContext.RequestAborted in a page *@
@await Model.GetData(HttpContext.RequestAborted);
</div>
@functions {
// Using a CancellationToken parameter
public async Task OnGet(System.Threading.CancellationToken cancellationToken)
{
await GetData(cancellationToken);
}
public async Task<string> GetData(System.Threading.CancellationToken cancellationToken)
{
await Task.Delay(1000, cancellationToken);
return "test";
}
}
In a custom class, you can use the IHttpContextAccessor
interface to get an instance of the current HttpContext
as explained in this page.
public class UserRepository : IUserRepository
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserRepository(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void GetCurrentUser()
{
var cancellationToken = _httpContextAccessor.HttpContext.RequestAborted;
cancellationToken.ThrowIfCancellationRequested();
// TODO Actual logic
}
}
#Detecting missing CancellationToken using Meziantou.Analyzer
You can detect missing CancellationToken
s in your applications using a Roslyn analyzer. Meziantou.Analyzer contains a rule to detect missing cancellation tokens.
You can install the Visual Studio extension or the NuGet package to analyze your code:
public async Task<string> Get(CancellationToken cancellationToken)
{
// MA0040 - Specify a CancellationToken (cancellationToken, HttpContext.RequestAborted)
await GetDataAsync();
return "";
}
static Task GetDataAsync(CancellationToken cancellationToken = default) => throw null;
Do you have a question or a suggestion about this post? Contact me!