While browsing through the ASP.NET Core documentation, I accidently stumbled over the IStartupFilter interface. I had no idea what it did but it triggered my interest. Let's find out together in this post what this interface does and when it can be useful.
What is IStartupFilter?
IStartupFilter
is an interface that allows you to intercept and modify how the application's request pipeline is built. It's particularly useful when you need to:
- Ensure certain middleware runs before any other middleware
- Add middleware consistently across multiple applications
- Create reusable middleware configurations
- Modify the order of middleware registration
Here's the interface definition:
public interface IStartupFilter | |
{ | |
Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next); | |
} |
How IStartupFilter Works
When ASP.NET Core builds the middleware pipeline, it executes all registered IStartupFilter
instances in the order they were registered. Each filter can add middleware before and after calling the next
delegate, creating a wrapper around the existing pipeline configuration.
Here's a practical example:
public class RequestTimingStartupFilter : IStartupFilter | |
{ | |
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) | |
{ | |
return builder => | |
{ | |
// Add middleware before the existing pipeline | |
builder.Use(async (context, nextMiddleware) => | |
{ | |
var sw = Stopwatch.StartNew(); | |
// Register a callback to add the timing header before the response starts | |
context.Response.OnStarting(() => | |
{ | |
sw.Stop(); | |
context.Response.Headers["X-Request-Time-Ms"] = sw.ElapsedMilliseconds.ToString(); | |
return Task.CompletedTask; | |
}); | |
// Call the next middleware | |
await nextMiddleware(); | |
}); | |
// Call the existing pipeline configuration | |
next(builder); | |
}; | |
} | |
} |
To use our IStartupFilter
, we need register it in our Program.cs
:
var builder = WebApplication.CreateBuilder(args); | |
// Add services to the container. | |
builder.Services.AddControllers(); | |
builder.Services.AddOpenApi(); | |
// Register the RequestTimingStartupFilter | |
builder.Services.AddTransient<IStartupFilter, RequestTimingStartupFilter>(); | |
var app = builder.Build(); |
Remark: Multiple IStartupFilter
implementations might interact with the same objects. If ordering is important, order their IStartupFilter
service registrations to match the order that their middlewares should run.
When should I use it?
I donāt see that many use cases where you need the IStartupFilter
. I think this is mostly useful for library authors that want to ensure that their middleware runs at the beginning (or end) of the middleware pipeline. For example, youāve create a custom error handling middleware. In that case you want a filter that ensures error handling middleware is always first in the pipeline across all your applications.
If you are using the IStartupFilter
in some smart ways, please let me know. Always interested to learn from your experiences.