在軟體生命週期地演進的過程中,在不同的階段可能需要將某一些功能啟用或停用,這時候就可以選擇 Feature Toggle,微軟的 Microsoft.FeatureManagement 正好是選擇之一,除了基本的 Feature Toggle Flags 之外,也整合到 ASP.NET Core 的生命週期裡面,還有整合 Azure。下圖出自 針對目標受眾啟用分段推出功能 - Azure App Configuration | Microsoft Learn
開發環境
- Windows 11 Pro
- ASP.NET Core 7
- Rider 2022.3.2
- Microsoft.FeatureManagement 2.5.1
- Microsoft.FeatureManagement.AspNetCore 2.5.1
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 參考
有關組態設定參考
- .NET 6 應用程式如何切換環境和組態
- 自訂 ConfigurationProvider - 實作 EnvFileConfigurationProvider
- 如何使用組態 Microsoft.Extensions.Configuration
- 如何使用 Options Pattern for Microsoft.Extensions.Options
- 如何使用應用程式秘密組態
範例專案
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET