Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

dotnet-feature-flags-aspnetcore

Feature Flags in ASP.NET Core Web API using Microsoft.FeatureManagement v4 β€” simple on/off flags, percentage rollouts, targeting filters, and [FeatureGate] action guards, all wired up with zero custom infrastructure.

Visit CodingDroplets YouTube Patreon Buy Me a Coffee GitHub


πŸš€ Support the Channel β€” Join on Patreon

If this sample saved you time, consider joining our Patreon community. You'll get exclusive .NET tutorials, premium code samples, and early access to new content β€” all for the price of a coffee.

πŸ‘‰ Join CodingDroplets on Patreon

Prefer a one-time tip? Buy us a coffee β˜•


🎯 What You'll Learn

  • How to install and register Microsoft.FeatureManagement.AspNetCore in an ASP.NET Core Web API
  • How to declare simple on/off feature flags in appsettings.json
  • How to configure a percentage-based rollout (Microsoft.Percentage filter) for gradual feature releases
  • How to configure a targeting filter (Microsoft.Targeting) to enable features for specific users or groups
  • How to use IFeatureManager.IsEnabledAsync() inside controller actions to branch logic
  • How to use the [FeatureGate] attribute to declaratively guard an entire endpoint
  • How to centralise all flag names as constants to eliminate magic strings
  • How to override flags per environment using appsettings.Development.json
  • How to unit-test feature-flag-driven controller logic using Moq

πŸ—ΊοΈ Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   HTTP Request                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  ASP.NET Core   β”‚
                    β”‚  Middleware     β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚                  β”‚                  β”‚
  β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Products     β”‚  β”‚ Search         β”‚  β”‚ Dashboard       β”‚
  β”‚ Controller   β”‚  β”‚ Controller     β”‚  β”‚ Controller      β”‚
  β”‚              β”‚  β”‚                β”‚  β”‚ [FeatureGate]   β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                  β”‚                  β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚
                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                   β”‚   IFeatureManager   β”‚
                   β”‚ (IsEnabledAsync)    β”‚
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚
               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β”‚   Feature Configuration     β”‚
               β”‚   appsettings.json          β”‚
               β”‚   "FeatureManagement": {    β”‚
               β”‚     "NewPricingEngine": true β”‚
               β”‚     "BetaSearch": {         β”‚
               β”‚       Percentage: 50%       β”‚
               β”‚     }                       β”‚
               β”‚     "VipDashboard": {       β”‚
               β”‚       Targeting filter      β”‚
               β”‚     }                       β”‚
               β”‚   }                         β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“‹ Feature Flag Types β€” Summary Table

Flag Name Type Default Behaviour
NewPricingEngine Simple on/off true Switches between 10% discount pricing and legacy pricing
DarkModeSupport Simple on/off false Enables a dark-mode preference feature
BetaSearch Percentage rollout (50%) 50% of requests Routes search requests to BetaEngine vs StableEngine
VipDashboard Targeting filter Specific users/groups Guards the VIP dashboard endpoint

πŸ“ Project Structure

dotnet-feature-flags-aspnetcore/
β”œβ”€β”€ dotnet-feature-flags-aspnetcore.sln
β”‚
β”œβ”€β”€ src/
β”‚   └── FeatureFlagsDemo/
β”‚       β”œβ”€β”€ Controllers/
β”‚       β”‚   β”œβ”€β”€ DashboardController.cs   # [FeatureGate] declarative guard example
β”‚       β”‚   β”œβ”€β”€ FeaturesController.cs    # Query flag states via HTTP
β”‚       β”‚   β”œβ”€β”€ ProductsController.cs    # Flag-driven pricing strategy switch
β”‚       β”‚   └── SearchController.cs      # Percentage-rollout routing example
β”‚       β”‚
β”‚       β”œβ”€β”€ Models/
β”‚       β”‚   β”œβ”€β”€ FeatureFlagStatusDto.cs  # Response shape for flag queries
β”‚       β”‚   β”œβ”€β”€ ProductDto.cs            # Product response with pricing engine info
β”‚       β”‚   └── SearchResultDto.cs       # Search response with engine label
β”‚       β”‚
β”‚       β”œβ”€β”€ Services/
β”‚       β”‚   β”œβ”€β”€ IPricingService.cs       # Interface: Calculate + EngineName
β”‚       β”‚   β”œβ”€β”€ LegacyPricingService.cs  # Strategy: no discount (flag OFF)
β”‚       β”‚   └── NewPricingService.cs     # Strategy: 10% discount (flag ON)
β”‚       β”‚
β”‚       β”œβ”€β”€ FeatureFlags.cs              # Centralised flag name constants
β”‚       β”œβ”€β”€ Program.cs                   # App bootstrap + FeatureManagement DI
β”‚       β”œβ”€β”€ appsettings.json             # Production flag config
β”‚       └── appsettings.Development.json # Dev override: all flags ON
β”‚
└── tests/
    └── FeatureFlagsDemo.Tests/
        β”œβ”€β”€ FeaturesControllerTests.cs   # 4 tests
        β”œβ”€β”€ PricingServiceTests.cs       # 11 tests
        β”œβ”€β”€ ProductsControllerTests.cs   # 6 tests
        └── SearchControllerTests.cs     # 5 tests

πŸ› οΈ Prerequisites

Requirement Version
.NET SDK 10.0+
IDE Visual Studio 2022 / Rider / VS Code
NuGet Microsoft.FeatureManagement.AspNetCore 4.5.0

⚑ Quick Start

# 1. Clone the repository
git clone https://github.com/codingdroplets/dotnet-feature-flags-aspnetcore.git
cd dotnet-feature-flags-aspnetcore

# 2. Build
dotnet build -c Release

# 3. Run (Development β€” all flags ON)
dotnet run --project src/FeatureFlagsDemo

# 4. Open Swagger UI
# http://localhost:5289/swagger

πŸ”§ How It Works

1. Register Feature Management

// Program.cs
builder.Services.AddFeatureManagement(
    builder.Configuration.GetSection("FeatureManagement"));

2. Define flag names as constants (no magic strings)

// FeatureFlags.cs
public static class FeatureFlags
{
    public const string NewPricingEngine = "NewPricingEngine";
    public const string BetaSearch       = "BetaSearch";
    public const string VipDashboard     = "VipDashboard";
    public const string DarkModeSupport  = "DarkModeSupport";
}

3. Configure flags in appsettings.json

"FeatureManagement": {
  // Simple on/off
  "NewPricingEngine": true,
  "DarkModeSupport": false,

  // Percentage-based rollout β€” 50% of requests see BetaSearch
  "BetaSearch": {
    "EnabledFor": [
      { "Name": "Microsoft.Percentage", "Parameters": { "Value": 50 } }
    ]
  },

  // Targeting β€” only specific users / groups
  "VipDashboard": {
    "EnabledFor": [
      {
        "Name": "Microsoft.Targeting",
        "Parameters": {
          "Audience": {
            "Users": [ "alice@example.com" ],
            "Groups": [ { "Name": "VipBeta", "RolloutPercentage": 100 } ],
            "DefaultRolloutPercentage": 0
          }
        }
      }
    ]
  }
}

4. Inject IFeatureManager and branch logic

// ProductsController.cs β€” switch pricing strategy at runtime
private async Task<IPricingService> GetPricingServiceAsync()
{
    bool useNewEngine = await _featureManager.IsEnabledAsync(FeatureFlags.NewPricingEngine);
    return useNewEngine
        ? new NewPricingService()   // 10% discount
        : new LegacyPricingService(); // no discount
}

5. Declaratively guard an endpoint with [FeatureGate]

// DashboardController.cs β€” returns 404 automatically when flag is disabled
[HttpGet("vip")]
[FeatureGate(FeatureFlags.VipDashboard)]
public IActionResult GetVipDashboard()
{
    return Ok(new { Message = "Welcome to the VIP Dashboard!" });
}

6. Override all flags in Development

// appsettings.Development.json β€” all flags ON so you can test every path
"FeatureManagement": {
  "NewPricingEngine": true,
  "DarkModeSupport": true,
  "BetaSearch": true,
  "VipDashboard": true
}

πŸ“‘ API Endpoints

Method Endpoint Description Status Codes
GET /api/features List all feature flags and their current state 200
GET /api/features/{flagName} Get a single flag's state by name 200, 404
GET /api/products Get all products (pricing depends on NewPricingEngine flag) 200
GET /api/products/{id} Get a single product by ID 200, 404
GET /api/search?q={query} Search products (engine depends on BetaSearch flag) 200, 400
GET /api/dashboard/vip VIP dashboard β€” guarded by [FeatureGate(VipDashboard)] 200, 404
GET /api/dashboard/standard Standard dashboard β€” always available 200

πŸ§ͺ Running Tests

dotnet test -c Release

Test Summary

Test Class Tests Covers
PricingServiceTests 11 Discount calculation, rounding, engine names
ProductsControllerTests 6 Flag-driven pricing, 404 handling
SearchControllerTests 5 Engine routing, query validation, response shape
FeaturesControllerTests 4 Flag status listing, single flag lookup
Total 26 26 passed, 0 failed

πŸ€” Key Concepts

Why Microsoft.FeatureManagement over a simple bool in config?

Approach Pros Cons
appsettings bool Simple, no dependency No rollout strategies, no targeting
Custom IFeatureFlag Full control Reinventing the wheel
Microsoft.FeatureManagement Built-in filters, Azure App Config compatible, attribute guards Slight learning curve

Feature flag strategies compared

Strategy Config key Use case
Simple on/off "Flag": true Kill switch, dev toggle
Percentage rollout Microsoft.Percentage Canary releases, A/B testing
Targeting filter Microsoft.Targeting Beta users, VIP groups
Time window filter Microsoft.TimeWindow Scheduled feature launches

IFeatureManager vs [FeatureGate]

Technique When to use
IsEnabledAsync(flag) Branch logic inside a method β€” switch implementations, return different data
[FeatureGate(flag)] Declaratively gate an entire action β€” automatic 404 when disabled

🏷️ Technologies Used

  • ASP.NET Core 10 β€” Web API framework
  • Microsoft.FeatureManagement.AspNetCore 4.5.0 β€” Feature flag engine
  • Swashbuckle.AspNetCore β€” Swagger / OpenAPI documentation
  • xUnit β€” Unit testing framework
  • Moq β€” Mocking framework for IFeatureManager in tests

πŸ“š References


πŸ“„ License

This project is licensed under the MIT License.


πŸ”— Connect with CodingDroplets

Platform Link
🌐 Website https://codingdroplets.com/
πŸ“Ί YouTube https://www.youtube.com/@CodingDroplets
🎁 Patreon https://www.patreon.com/CodingDroplets
β˜• Buy Me a Coffee https://buymeacoffee.com/codingdroplets
πŸ’» GitHub http://github.com/codingdroplets/

Want more samples like this? Support us on Patreon or buy us a coffee β˜• β€” every bit helps keep the content coming!

About

Feature Flags in ASP.NET Core Web API using Microsoft.FeatureManagement v4: on/off flags, percentage rollouts, targeting filters, and FeatureGate action guards.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages