Adding your own feature flags in ASP.NET

In ASP.NET, you can add your own feature flags by using the feature management functionality.

In this article, I’ll show how to configure and use feature management in ASP.NET and how to use this to feature gate an endpoint using your own feature flag.

Configure feature management

You need to do a few configuration steps to be able to use feature management. You need to add the feature management nuget package, add your feature flags to appsettings.json, and enable feature management in Startup.ConfigureServices. These steps are shown below.

Install the feature management nuget package

Install the Microsoft.FeatureManagement.AspNetCore nuget package. Note: This is using (View > Other Windows > Package Manager Console).

Install-Package Microsoft.FeatureManagement.AspNetCore
Code language: PowerShell (powershell)

Add feature flags to appsettings.json

By default, ASP.NET reads in feature flags from a property in appsettings.json called FeatureManagement. So add this property and all of your feature flags to appsettings.json, like this:

{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "FeatureManagement": { "Movies": true, "Shows": true } }
Code language: JSON / JSON with Comments (json)

In this example, I have two features – Movies and Shows.

When you’re checking if a feature is enabled, you have to pass in the name of the feature. Instead of passing in string literals, you can put the feature names in an enum, like this:

public enum Features { Movies, Shows }
Code language: C# (cs)

Note: You can also use constants for the feature names if that’s what you prefer.

Turn on feature management

In Startup.ConfigureServices, add services.AddFeatureManagement(), like this:

using Microsoft.FeatureManagement; public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddFeatureManagement(); //other things in ConfigureServices } //other things in Startup
Code language: C# (cs)

Once you turn on feature management, the framework will do a lot of heavy-lifting for you. It’ll read in the feature flags from appsettings.json and you can immediately start using them in the service.

Feature gating controllers and endpoints

Once you have feature management configured, you can use the feature flags in many ways.

One common use of feature flags is to feature gate your controllers and endpoints. This means when your feature is off, requests will return a 404 (Not Found) response. This is a convenient way to disable endpoints.

Note: You can change the behavior of what it returns when a feature is disabled. I’ll show that below as well.

Feature gate a controller

To feature gate a controller, use the FeatureGate attribute on the controller and specify the feature, like this:

using Microsoft.FeatureManagement.Mvc; [FeatureGate(Features.Movies)] [ApiController] [Route("[controller]")] public class MovieController : ControllerBase { [HttpGet] public IEnumerable<Movie> Get() { return new List<Movie>() { new Movie() { Name = "Office Space", Description = "A relatable comedy about a programmer who hates works", RuntimeMinutes = 89, ReleaseYear = 1999 } }; } }
Code language: C# (cs)

Try sending a GET request to this controller with the feature flag enabled. When it’s enabled, you’ll get back a 200 response code and the JSON the endpoint returns, as expected.

Now see what happens when you disable the feature flag. Set the feature flag to false in appsettings.json:

{ "FeatureManagement": { "Movies": false, "Shows": true } }
Code language: JSON / JSON with Comments (json)

When you send a GET request with the feature flag disabled, you’ll get HTTP status code 404 (Not Found).

Feature gate an individual endpoint

You can feature gate individual endpoints. This is useful if you don’t want to feature gate the whole controller. It works the same as feature gating a controller: when the feature is disabled, requests to the endpoint will return a 404 (Not Found).

Here’s how to feature gate the endpoint:

using Microsoft.FeatureManagement.Mvc; [ApiController] [Route("[controller]")] public class MovieController : ControllerBase { [FeatureGate(Features.Movies)] [HttpGet] public IEnumerable<Movie> Get() { return new List<Movie>() { new Movie() { Name = "Office Space", Description = "A relatable comedy about a programmer who hates works", RuntimeMinutes = 89, ReleaseYear = 1999 } }; } }
Code language: C# (cs)

When you have the feature flag enabled, requests will go through to the endpoint and it’ll return as expected.

When the feature flag is disabled, the endpoint will return a 404 (Not Found).

Change what’s returned when a feature is disabled

The default behavior for disabled features is reasonable. It returns a 404 (Not Found) response.

But what if you want to change that default behavior? For example, let’s say you want to return a 500 status code and return a user friendly response message that says exactly which disabled features are disallowing the action.

First, add a class that implements the IDisabledFeaturesHandler interface, like this:

using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.FeatureManagement.Mvc; public class DisabledFeatureHandler : IDisabledFeaturesHandler { public Task HandleDisabledFeatures(IEnumerable<string> features, ActionExecutingContext context) { context.Result = new ObjectResult($"Action disallowed. Feature(s) are disabled: {string.Join(", ", features)}") { StatusCode = 500 }; return Task.CompletedTask; } }
Code language: C# (cs)

Then in Startup.ConfigureServices, call UseDisableFeaturesHandler() like this:

using Microsoft.FeatureManagement; public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddFeatureManagement() .UseDisabledFeaturesHandler(new DisabledFeatureHandler()); //Other things in ConfigureServices } //other things in Startup
Code language: C# (cs)

To see this working, turn off a feature in appsettings.json:

{ "FeatureManagement": { "Movies": false, "Shows": true } }
Code language: JSON / JSON with Comments (json)

When you try to hit the endpoint with the disabled feature, you’ll get back:

Status Code: 500 Body: "Feature(s) are disabled: Movies"
Code language: plaintext (plaintext)

Passing in the FeatureManager to a controller

Feature management in ASP.NET is very flexible. You can pass the FeatureManager anywhere in your code and check if a feature flag is enabled.

Let’s say you have an endpoint that you want to feature gate, but you don’t want to use the FeatureGate attribute. And when the feature is disabled, you want to return a very specific error response.

First, dependency inject the IFeatureManager into your controller by adding it as a constructor parameter. Then use FeatureManager.IsEnabledAsync() and specify the name of your feature, like this:

using Microsoft.FeatureManagement; [ApiController] [Route("[controller]")] public class ShowController : ControllerBase { private readonly IFeatureManager FeatureManager; public ShowController(IFeatureManager featureManager) { FeatureManager = featureManager; } [HttpGet] public async Task<IActionResult> Get() { if (!await FeatureManager.IsEnabledAsync(nameof(Features.Shows))) return Problem($"{nameof(Features.Shows)} feature is not enabled"); return Ok(new List<Show>() { new Show() { Name = "The Office", Description = "A relatable comedy about what office workers do to survive the boredom of pointless work", NumberOfEpisodes = 201, NumberOfSeasons = 9, FirstYear = 2005, LastYear = 2013 } }); } }
Code language: C# (cs)

Disable the feature in appsettings.json:

{ "FeatureManagement": { "Movies": false, "Shows": false } }
Code language: JSON / JSON with Comments (json)

Now send the request and you’ll get back a 500 error code with the response message specified by the endpoint code:

{ "type": "https://tools.ietf.org/html/rfc7231#section-6.6.1", "title": "An error occured while processing your request.", "status": 500, "detail": "Shows feature is not enabled", "traceId": "|2b03078e-4ca4f4728d2f1a37." }
Code language: JSON / JSON with Comments (json)

Leave a Comment