Implementing a Feature Flag-based Endpoint Filter
Hi all, in Feature Flags in .NET, from simple to more advanced we've touched on implementing Feature Flags within .NET applications. At the time of writing that blog post, it was required to add code within the route handlers of an ASP.NET Minimal API to verify if a feature is enabled. This was a bit cumbersome. It was harder to reuse this logic, and it added noise to the route handlers.
To refresh your mind, the next snippet shows that the IFeatureManager
is injected within the route handler, and it's used to verify if a feature is enabled.
When the feature is disabled, then a NotFound
result is returned.
Otherwise, if the feature is enabled, then the route handler continues to execute and returns the weather forecast.
What I don't like about this implementation is that we're required to repeat this code within all of the route handlers that need to be toggled on or off.
Luckily, with the addition of the EndpointFilter
s in ASP.NET 7, we can refactor it.
In this blog post, we'll extract this logic to end up with a reusable custom endpoint filter.
But before we implement the feature flag filter, let's first take a closer look at EndpointFilter
feature.
Endpoint Filter link
A minimal API endpoint filter can be compared to a middleware that intercepts an HTTP request before it reaches the route handler. The filter can execute application logic before and after the route handler's implementation is invoked.
This is useful for many scenarios, in our case to change the behavior based on whether a feature flag is enabled.
When the feature is disabled, we want to short-circuit the request pipeline and return a NotFound
result.
The route handler is not invoked, and the request is not processed further.
To register a filter for an endpoint, use the AddEndpointFilter()
method.
AddEndpointFilter
takes an EndpointFilterInvocationContext
and anEndpointFilterDelegate
as arguments, and returns a ValueTask<object?>
.
Because we don't rely on those arguments for our feature endpoint filter (see later), I'll skip the details and refer you to the documentation for more info.
A simple implementation of a feature filter looks as follows:
Feature Flag implementation as an EndpointFilter
link
With this knowledge, we're able to implement our feature flag endpoint filter.
As mentioned earlier, we want to end up with a reusable filter. Therefore, we need to wrap the logic of the filter into something reusable, e.g. a class.
To implement the filter, the class needs the implement the IEndpointFilter
interface.
The InvokeAsync()
method contains the filter's logic, and will be called by the request pipeline.
Because it's instantiated by the DI container, it's possible to inject the IFeatureManager
.
Within the InvokeAsync()
method, the feature manager is used to verify if the feature flag is enabled using _featureManager.IsEnabledAsync()
.
In the preceding code, you notice that the class is made abstract. This is done so it's possible to create specific implementations that provide the name of the feature flag. Otherwise, we would still need to repeat some code within each feature filter implementation.
Next, we can register the filter that we've just implemented using AddEndpointFilter<T>()
with the following syntax.
A more functional approach link
For a more functional approach, we can assign the route handler to a variable. This has its advantages and disadvantages, but I think it's more a matter of taste.
Endpoint Groups link
To make it even easier, we can also register the filter on an end endpoint group. Doing this applies the filter to all endpoints within the group. Super easy, because we don't need to repeat the filter registration for each endpoint.
Conclusion link
In this post, we've seen how to implement an endpoint filter that verifies if a feature flag is enabled. Using this approach keeps the Minimal API route handlers clean and compact, and the feature flag logic is reusable.
For now, we need to implement the endpoint filters ourselves, but it could be that this will be provided out-of-the-box in the future - for more info see this GitHub issue.
Incoming links
Outgoing links
Feel free to update this blog post on GitHub, thanks in advance!
Join My Newsletter (WIP)
Join my weekly newsletter to receive my latest blog posts and bits, directly in your inbox.
Support me
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.