Mastering Cancellation Tokens in ASP.NET Core
Gracefully handle client disconnects and timeouts
What is a Cancellation Token?
A CancellationToken in ASP.NET Core is a lightweight, cooperative mechanism that enables you to signal cancellation requests to asynchronous operations. It acts as a notification system rather than forcing cancellation—your code must actively check the token and respond appropriately.
Think of it like a "stop" button for long-running tasks. When a client disconnects from your API or a timeout occurs, ASP.NET Core automatically creates a CancellationToken via HttpContext.RequestAborted. Your methods can monitor this token to exit early, preventing wasted server resources.
public async Task ProcessAsync(CancellationToken cancellationToken)
{
// Your code checks the token periodically
cancellationToken.ThrowIfCancellationRequested();
}
Pros, Cons, and When to Use Cancellation Tokens
Pros:
Resource efficiency: Stops unnecessary work when clients disconnect
Scalability: Frees up threads and memory during high load
Built-in support: Works seamlessly with Task.Delay(), HttpClient, database queries
Predictable behavior: Graceful shutdowns instead of abrupt failures
Cons:
Cooperative nature: Requires explicit checks in your code—easy to forget
Loop complexity: Adds ThrowIfCancellationRequested() calls everywhere
Debugging overhead: Cancellation logic can complicate stack traces
Not for CPU-bound work: Doesn't interrupt synchronous operations effectively
When to use:
Long-running async APIs (file uploads, report generation)
External service calls (HTTP, databases with timeouts)
Background processing that should respect client lifecycle
When NOT to use:
Short, fast operations (< 100ms)
Pure CPU-bound synchronous work
Fire-and-forget scenarios where cancellation doesn't matter
Real-World Scenario: Long-Processing API
Consider a reporting API that generates complex data over several seconds. Clients might close their browser mid-process, but your server keeps running unnecessarily.
API Controller:
[HttpGet("long-process")]
public async Task<IActionResult> LongProcess(CancellationToken cancellationToken)
{
// Use HttpContext.RequestAborted for automatic client disconnect detection
var result = await _getDataService.ProcessAsync(cancellationToken);
return result == "Done"
? Ok("Process completed")
: StatusCode(500, "Process failed");
}
Service Implementation:
public class GetDataService
{
public async Task<string> ProcessAsync(CancellationToken cancellationToken)
{
for (int i = 0; i < 5; i++)
{
// Check for cancellation at each step
cancellationToken.ThrowIfCancellationRequested();
// Simulate work (respects cancellation)
await Task.Delay(2000, cancellationToken);
// Log progress (in real apps)
Console.WriteLine($"Step {i + 1}/5 completed");
}
return "Done";
}
}
What happens:
Client calls /api/check/long-process
Each 2-second delay checks if client disconnected
If client closes browser → ThrowIfCancellationRequested() throws OperationCanceledException
API returns TaskCanceledException (handled as 499 by most gateways)
Best Practices for Production
✅ DO: Pass CancellationToken through all layers
✅ DO: Check token before/after expensive async operations
✅ DO: Use with HttpClient.Timeout and DB timeouts
❌ DON'T: Catch OperationCanceledException and treat as error
❌ DON'T: Forget to pass cancellationToken to Task.Delay()
❌ DON'T: Use for synchronous CPU work
Conclusion
Cancellation Tokens transform how you handle long-running operations in ASP.NET Core. They provide a clean, standardized way to respect client intent while protecting server resources. The key is consistency—pass tokens everywhere and check them religiously.
Implement this pattern in your next long-running API, and you'll immediately notice improved scalability and resource utilization.
Source Code
You can find the complete working demo here:
GitHub Repository: JwtCancellationTokenDemo
src/Controllers/CheckController.cs | src/Services/GetDataService.cs