如何使用 Microsoft.FeatureManagement 實現 Feature Toggle

在軟體生命週期地演進的過程中,在不同的階段可能需要將某一些功能啟用或停用,這時候就可以選擇 Feature Toggle,微軟的 Microsoft.FeatureManagement 正好是選擇之一,除了基本的 Feature Toggle Flags 之外,也整合到 ASP.NET Core 的生命週期裡面,還有整合 Azure。下圖出自 針對目標受眾啟用分段推出功能 - Azure App Configuration | Microsoft Learn

條件式功能旗標

開發環境

Feature Management

安裝 Microsoft.FeatureManagement

dotnet add package Microsoft.FeatureManagement --version 2.5.1

預設 Feature Management 會從組態設定的 FeatureManagement 區段來讀取 Flags 設定,他必須要搭配 Microsoft.Extensions.DependencyInjection 一起服用 (延伸閱讀)。

public void ConfigureServices(IServiceCollection services)
{
	...
	services.AddFeatureManagement();
}

 

appsettings.json 組態內容:

{
  "FeatureManagement": {
    "FeatureA": true,
    "FeatureB": false,
    "FeatureC": {
      "EnabledFor": [
        {
          "Name": "Percentage",
          "Parameters": {
            "Value": 50
          }
        }
      ]
    }
  }
}

 

或者注入 IConfiguration,在這裡可以自訂自己想要的組態區段,不見得是 FeatureManagement區段

public void ConfigureServices(IServiceCollection services)
{
	...
	services.AddFeatureManagement(Configuration.GetSection("MyFeatureFlags"));
}

 

最後,在建構子開一個洞依賴 IFeatureManager,根據 Flags 決定要不要執行該功能, 如下範例

public class Demo
{
    private readonly IFeatureManager _featureManager;

    public Demo(IFeatureManager featureManager)
    {
        this._featureManager = featureManager;
    }

    public async Task<string> CreateFeatureA()
    {
        if (await this._featureManager.IsEnabledAsync(FeatureFlags.FeatureA))
        {
 			//do something       
            return "OK";
        }

        return null;
    }
}

 

FeatureFlags 類別,裝載定義 Feature 的常數

public static class FeatureFlags
{
    public const string FeatureA = "FeatureA";
    public const string FeatureB = "FeatureB";
    public const string FeatureC = "FeatureC";
}

 

使用範例如下:

[TestMethod]
public async Task CreateFeatureA()
{
    var configBuilder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json");
    var services = new ServiceCollection();
    services.AddSingleton<Demo>();
    services.AddFeatureManagement(configBuilder.Build());
    var serviceProvider = services.BuildServiceProvider();
    var target = serviceProvider.GetService<Demo>();
    var actual = await target.CreateFeatureA();
    Assert.AreEqual("OK", actual);
}

 

整合 ASP.NET

在 ASP.NET 專案安裝

dotnet add package Microsoft.FeatureManagement.AspNetCore --version 2.5.1

FeatureGateAttribute 可以放在 Controller 以及 Action 上,這個 ActionFilter 已經使用 IFeatureManager 處理 Feature Toggle Flags,只要 Flags == false,預設會得到 HttpStatusCode = 404 ,原因是它有呼叫 IDisabledFeaturesHandler,我們可以利用這個功能來決定端點要不要開放,支援多個 Flags。

用法如下

套用 Controller

[FeatureGate(FeatureFlags.FeatureA)]
public class DemoController : ControllerBase
{
	//do something
	...
}

 

套用 Action

[HttpGet]
[FeatureGate(FeatureFlags.FeatureA)]
public async Task<ActionResult> Get()
{
	// do something
	...
}

 

執行效果,如下:

 

自訂 DisabledFeaturesHandler

預設的 Handler 回傳的內容不是我需要的,也可以透過 UseDisabledFeaturesHandler 擴充方法,填上我們想要的內容

builder.Services.AddFeatureManagement().UseDisabledFeaturesHandler((features, context) =>
{
    context.Result = new ObjectResult(new
    {
        FailureCode = "FeatureDisabled",
        FailureMessage = $"The feature {features.First()} is disabled.",
        TraceId = context.HttpContext.TraceIdentifier
    })
    {
        StatusCode = 404
    };
});

 

執行效果,如下:

 

套用 ActionFilter

當功能是寫在 ActionFilter 時,可以透過 AddForFeature() 擴充方法設定 Feature Toggle Flags

builder.Services.AddControllers(p => p.Filters.AddForFeature<DemoAsyncActionFilter>(FeatureFlags.FeatureA));

 

不過,目前僅支援 IAsyncActionFilter範例如下:

public class DemoAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        Console.WriteLine("on action execution");

        // Do something before the action executes.
        await next();

        // Do something after the action executes.
    }
}

 

套用 Middleware

Middleware 支援兩個方法 UseForFeature()、UseMiddlewareForFeature<T>(),這裡一次只能一個 Feature Flags

app.UseForFeature(FeatureFlags.FeatureA, appBuilder =>
{
    appBuilder.Use(async (context, next) =>
    {
        Console.WriteLine("on middleware execution");

        // Do something with the request
        await next.Invoke();

        // Do something with the response
    });
})

 

套用 MVC 的 View

這裡需要 TagHelper,可以選擇單一頁面或是所有頁面

@addTagHelper *, Microsoft.FeatureManagement.AspNetCore

 

⁠⁠⁠下圖出自:如何在 .NET Core 應用程式中使用功能旗標的教學課程 | Microsoft Learn

條件式過濾

除了上述使用 Flags 起/停用功能之外,還可以使用條件式過濾啟用,比如新功能推出的時候,讓部份的用戶可以搶先使用

Microsoft.FeatureManagement 程式庫包含三個功能篩選:

  • PercentageFilter:根據百分比啟用功能旗標。
  • TimeWindowFilter:在指定的時段內啟用功能旗標。
  • TargetingFilter:為指定的使用者和群組啟用功能旗標。

或者是實作 Microsoft.FeatureManagement.IFeatureFilter 介面

public class DemoFeatureFilter : IFeatureFilter
{
    public async Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
    {
        // Your implementation here
        return true;
    }
}

 

套用方式如下

builder.Services.AddFeatureManagement().AddFeatureFilter<DemoFeatureFilter>();

 

參考

如何在 .NET Core 應用程式中使用功能旗標的教學課程 | Microsoft Learn

延伸閱讀

有關 MS DI Container 參考 

有關組態設定參考

範例專案

sample.dotblog/Feature Toggle/Lab.FeatureToggle at 856015c5dd7d2a00007e8f949498ac89e20b3ceb · yaochangyu/sample.dotblog (github.com)

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo