Yes, you can cancel ongoing HTTP requests with HttpClient
in C# using CancellationToken
. This is essential for responsive applications and preventing resource waste from long-running or stuck requests.
How Request Cancellation Works
CancellationToken
from the System.Threading
namespace provides a cooperative cancellation mechanism. When a cancellation is requested, the HttpClient
operations check the token and throw a TaskCanceledException
.
Basic Cancellation Example
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var httpClient = new HttpClient();
using var cts = new CancellationTokenSource();
try
{
// Pass the cancellation token to the HTTP request
HttpResponseMessage response = await httpClient.GetAsync(
"https://httpbin.org/delay/10",
cts.Token);
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Response received: {content.Length} characters");
}
catch (TaskCanceledException ex) when (ex.CancellationToken.IsCancellationRequested)
{
Console.WriteLine("Request was canceled.");
}
catch (TaskCanceledException)
{
Console.WriteLine("Request timed out.");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
Timeout-Based Cancellation
Set automatic timeouts using CancelAfter()
:
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5)); // Cancel after 5 seconds
try
{
var response = await httpClient.GetAsync("https://httpbin.org/delay/10", cts.Token);
// This will be canceled after 5 seconds
}
catch (TaskCanceledException)
{
Console.WriteLine("Request timed out after 5 seconds");
}
Manual Cancellation from Another Thread
public class HttpService
{
private CancellationTokenSource _cts;
private readonly HttpClient _httpClient = new HttpClient();
public async Task<string> FetchDataAsync()
{
_cts = new CancellationTokenSource();
try
{
var response = await _httpClient.GetAsync(
"https://httpbin.org/delay/10",
_cts.Token);
return await response.Content.ReadAsStringAsync();
}
catch (TaskCanceledException)
{
return "Request was canceled";
}
}
public void CancelRequest()
{
_cts?.Cancel(); // Cancel from UI button, timer, etc.
}
}
Cancellation with Multiple HTTP Operations
When chaining multiple HTTP operations, pass the same token to all:
public async Task<UserData> GetUserDataAsync(int userId, CancellationToken cancellationToken)
{
// All operations use the same cancellation token
var userResponse = await httpClient.GetAsync(
$"https://api.example.com/users/{userId}",
cancellationToken);
var user = await userResponse.Content.ReadAsAsync<User>(cancellationToken);
var profileResponse = await httpClient.GetAsync(
$"https://api.example.com/profiles/{user.ProfileId}",
cancellationToken);
var profile = await profileResponse.Content.ReadAsAsync<Profile>(cancellationToken);
return new UserData { User = user, Profile = profile };
}
Best Practices
1. Always Handle TaskCanceledException
try
{
var response = await httpClient.GetAsync(url, cancellationToken);
}
catch (TaskCanceledException ex) when (ex.CancellationToken.IsCancellationRequested)
{
// Explicit cancellation
_logger.LogInformation("Request canceled by user");
}
catch (TaskCanceledException)
{
// Timeout
_logger.LogWarning("Request timed out");
}
2. Set Reasonable Timeouts
// For quick API calls
cts.CancelAfter(TimeSpan.FromSeconds(10));
// For file downloads
cts.CancelAfter(TimeSpan.FromMinutes(5));
3. Dispose CancellationTokenSource
using var cts = new CancellationTokenSource();
// Automatically disposed at end of scope
4. Use with ASP.NET Core
In web applications, use the request's cancellation token:
[HttpGet]
public async Task<IActionResult> GetData(CancellationToken cancellationToken)
{
try
{
var data = await httpClient.GetStringAsync("https://api.example.com/data", cancellationToken);
return Ok(data);
}
catch (TaskCanceledException)
{
return StatusCode(499, "Client disconnected");
}
}
HttpClient Timeout vs CancellationToken
HttpClient
has a built-in Timeout
property, but CancellationToken
offers more flexibility:
// HttpClient timeout (applies to entire request)
httpClient.Timeout = TimeSpan.FromSeconds(30);
// CancellationToken (can be shared across operations)
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
Use CancellationToken
when you need:
- Manual cancellation control
- Shared cancellation across multiple operations
- Integration with cancellation-aware APIs
- More granular timeout handling
Request cancellation is crucial for building responsive, resource-efficient applications that handle network delays and user interactions gracefully.