Request Timeouts Middleware in .NET 8

Chaitanya (Chey) Penmetsa
CodeNx
Published in
4 min readMay 17, 2024

In this blog we will learn how to request timeouts middleware works in .NET 8 using Microsoft.AspNetCore.Http.Timeouts namespace. This namespace provides flexibility to apply the timeouts per request. When the timeout limit is reached, the IsCancellationRequested property of the CancellationToken in HttpContext.RequestAborted is set to true. However, the request is not automatically aborted, allowing the application to still generate either a success or failure response. If the application does not handle the exception and produce a response, the default behavior is to return a status code 504.

Author created image

How can we configure the RequestTimeout Middleware?

Let us see how we can configure Timeout middleware for standard .NET Web API and Minimal API. Now let us see how we can add to Program.cs

using Microsoft.AspNetCore.Http.Timeouts;
using Newtonsoft.Json;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddRequestTimeouts();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();
app.UseRequestTimeouts();
app.MapControllers();

app.Run();

Now let us see how we can configure the endpoints for setting RequestTimeouts for both Web API and Minimal API.

[HttpGet("twosecondtimeout/{waitSeconds:int}")]
[RequestTimeout(2000)]
public async Task<IActionResult> GetCustomerWithTwoSecondTimeoutAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/twosecondtimeout/{waitSeconds:int}", async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
}).WithRequestTimeout(TimeSpan.FromSeconds(2));

When you exceed the configured timeout, you will see the API returning below status code.

Note: When an application is running in debug mode, the timeout middleware does not activate. This behavior is consistent with Kestrel timeouts. To test timeouts, run the application without attaching the debugger (ctrl + F5).

Now let us dig deep into some advanced options like named policies, custom status code and custom response.

Configure named policies

We can define named RequestTimeout Policies and use that for configuring the endpoints as shown below:

builder.Services.AddRequestTimeouts(options => 
{
options.AddPolicy("threesecondpolicy", TimeSpan.FromSeconds(3));
});
[HttpGet("threesecondtimeout/{waitSeconds:int}")]
[RequestTimeout("threesecondpolicy")]
public async Task<IActionResult> GetCustomerWithThreeSecondTimeoutPolicyAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/threesecondtimeout/{waitSeconds:int}", async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
}).WithRequestTimeout("threesecondpolicy");

Configure Custom Status Code

While defining the policies we can define custom status code to return as shown below:

builder.Services.AddRequestTimeouts(options => 
{
options.AddPolicy("customstatuscode", new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromSeconds(3),
TimeoutStatusCode = 503
});
});
[HttpGet("customstatuscode/{waitSeconds:int}")]
[RequestTimeout("customstatuscode")]
public async Task<IActionResult> GetCustomerWithCustomStatusCodePolicyAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/customstatuscode/{waitSeconds:int}", [RequestTimeout("customstatuscode")] async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
});

When you the request time out exceeds the timeout configured, then we can see the API return the configured status code.

Configure Custom Delegate for customizing response

While defining the policies we can define custom delegate to customize the return response as shown below:

builder.Services.AddRequestTimeouts(options => 
{
options.AddPolicy("customdelegatepolicy", new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromSeconds(3),
TimeoutStatusCode = 503,
WriteTimeoutResponse = async (HttpContext context) => {
context.Response.ContentType = "application/json";
var errorResponse = new
{
error = "Request time out from custome delegate policy",
status = 503
};
var jsonResponse = JsonConvert.SerializeObject(errorResponse);
await context.Response.WriteAsync(jsonResponse);
}
});
});
[HttpGet("customdelegatepolicy/{waitSeconds:int}")]
[RequestTimeout("customdelegatepolicy")]
public async Task<IActionResult> GetCustomerWithCustomDelegateAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/customdelegatepolicy/{waitSeconds:int}", async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
}).WithRequestTimeout("customdelegatepolicy");

When you the request time out exceeds the timeout configured, then we can see the API return below response.

Global default Request Timeouts Policy

Like how we defined policies and configured them at each endpoint, we can define global timeout policy as catch all policy as shown below:

builder.Services.AddRequestTimeouts(options => 
{
options.DefaultPolicy = new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromMilliseconds(1500)
};
options.AddPolicy("threesecondpolicy", TimeSpan.FromSeconds(3));
options.AddPolicy("customstatuscode", new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromSeconds(3),
TimeoutStatusCode = 503
});
options.AddPolicy("customdelegatepolicy", new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromSeconds(3),
TimeoutStatusCode = 503,
WriteTimeoutResponse = async (HttpContext context) => {
context.Response.ContentType = "application/json";
var errorResponse = new
{
error = "Request time out from custome delegate policy",
status = 503
};
var jsonResponse = JsonConvert.SerializeObject(errorResponse);
await context.Response.WriteAsync(jsonResponse);
}
});
});

Disable Request Timeouts

Once we configure the global default timeout policy then all the endpoints will be enforced with that policy, but in some cases, we might need to exclude some endpoints from applying that policy. We can achieve this using DisableRequestTimeout attribute as shown below:

[HttpGet("disableTimeout/{waitSeconds:int}")]
[DisableRequestTimeout]
public async Task<IActionResult> GetCustomerWithNoTimeoutAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/disablerequestTimeout/{waitSeconds:int}", async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
}).DisableRequestTimeout();

We covered everything regarding Request Timeout Middleware in this blog. We will cover more topics on .NET Web APIs in future blogs.

Source code for this blog can be found below:

🙏Thanks for taking the time to read the article. If you found it helpful and would like to show support, please consider:

  1. 👏👏👏👏👏👏Clap for the story and bookmark for future reference
  2. Follow me on Chaitanya (Chey) Penmetsa for more content
  3. Stay connected on LinkedIn.

Wishing you a happy learning journey 📈, and I look forward to sharing new articles with you soon.

--

--

Chaitanya (Chey) Penmetsa
CodeNx
Editor for

👨🏽‍💻Experienced and passionate software enterprise architect helping solve real-life business problems with innovative, futuristic, and economical solutions.