Feature Toggle 這個議題最近挺夯的,它達到的效果就是我們透過設定,就可以輕易地開關應用程式上的功能,讓開發好的功能可以真正地發佈到 Production 上,但是不相關的使用者不會受到該功能的影響,也方便我們去測試只有在 Production 上才能測試的案例,而 ASP.NET Core 已經有套件支援 Feature Toggle,我們來看一下怎麼做?
Microsoft.FeatureManagement.AspNetCore
這個套件名稱就叫做 Microsoft.FeatureManagement.AspNetCore,是真正由微軟官方所開發的,我們從 NuGet 上安裝之後,就在 Startup.cs
裡面加上 AddFeatureManagement()
,就可以開始使用了。
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
services.AddFeatureManagement();
...
}
...
}
設定
接下來,我們要在 appsettings.json
新增一個名稱為 FeatureManagement
的區段,FeatureManagement 是預設的名稱,如果想要取另一個名稱,就要在 AddFeatureManagement() 方法中去另外指定。
public class Startup
{
...
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddFeatureManagement(this.Configuration.GetSection("MyFeatures"));
...
}
...
}
再來我們就在 FeatureManagement 這個區段裡面,去設定幾組我們想要 Feature Flags。
{
"FeatureManagement": {
"FeatureA": true,
"FeatureB": false,
"FeatureC": true
},
"Logging": {
...
},
"AllowedHosts": "*"
}
一個 Feature Flag 可以是一個功能或是一組功能,它是抽象的,上面這樣的設定就表示 FeatureA 跟 FeatureC 開啟,FeatureB 則關閉,這些 Feature Flags 我們可以套用在 Controller 及 Action
上,也可以套用在 MVC View
裡面,甚至是套用在 Middleware
上,或是我們可以自己寫套用的邏輯
。
套用在 Controller 及 Action 上
要套用在 Controller 或 Action 上,我們需要借用一個內建的 Action Filter - FeatureGate
,它可以填入多個 Feature Flags,預設要全部開啟,該 Controller 或 Action 才會開啟,否則預設會回傳 404。
[FeatureGate("FeatureA")]
public class HomeController : Controller
{
...
[FeatureGate("FeatureB", "FeatureC")] // 404
public IActionResult Index()
{
return View();
}
...
}
如果我們要調成任一 Feature Flag 開啟,該功能就開啟的話,那就填入 RequirementType.Any
參數即可。
[FeatureGate("FeatureA")]
public class HomeController : Controller
{
...
[FeatureGate(RequirementType.Any, "FeatureB", "FeatureC")]
public IActionResult Index()
{
return View();
}
...
}
還有,如果我們不想要回傳 404,我們有自己的回應邏輯,那可以使用 UseDisabledFeaturesHandler()
方法覆寫它,UseDisabledFeaturesHandler() 方法允許我們傳入一個 Action<IEnumerable<string>, ActionExecutingContext>
的委派方法。
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
services.AddFeatureManagement().UseDisabledFeaturesHandler(
(features, context) =>
{
context.Result = new StatusCodeResult(500);
});
...
}
...
}
或是,也可以傳入一個實作了 IDisabledFeaturesHandler
的類別實例。
public class MyDisabledFeaturesHandler : IDisabledFeaturesHandler
{
public async Task HandleDisabledFeatures(IEnumerable<string> features, ActionExecutingContext context)
{
context.Result = new StatusCodeResult(500);
await Task.CompletedTask;
}
}
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
services.AddFeatureManagement().UseDisabledFeaturesHandler(new MyDisabledFeaturesHandler());
...
}
...
}
套用在 MVC View
Microsoft.FeatureManagement.AspNetCore 套件內建有 TagHelper,可以讓我們在 View 裡面套用 Feature Flag,首先,我們就在 View 裡面透過 @addTagHelper
把 TagHelper 加進來,我們可以選擇加在單一的 View 上,或是加在 _ViewImports.cshtml
裡面,讓所有的 View 都能使用。
之後,我們就可以用 feature
這個 TagHelper 把 View 的部分內容包起來,標上 Feature Flags,然後一樣可以設定 RequirementType,而且它還多了一個 negate
屬性可以用,代表反向的意思。
套用在 Middleware
Microsoft.FeatureManagement.AspNetCore 套件提供兩個方法讓我們套用在 Middleware 上:UseForFeature()
、UseMiddlewareForFeature<T>
,不過要注意的是,它目前只能指定單一個 Feature Flag,不支援多個,使用方式如下範例:
public class Startup
{
...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseForFeature(
"FeatureA",
appBuilder =>
{
appBuilder.Use(
async (ctx, next) =>
{
await next();
await ctx.Response.WriteAsync("<!--Enable FeatureA-->");
});
});
app.UseMiddlewareForFeature<SomeMiddleware>("FeatureB");
...
}
...
}
自行撰寫套用邏輯
如果我們想在我們的服務裡面套用 Feature Flag,只要將 IFeatureManager
注入進來,透過它來判斷 Feature Flag 的開啟或關閉,使用起來大概就像下面這樣,不過它目前只有非同步的 API,最好在非同步的環境下使用:
public class HomeController : Controller
{
private readonly IFeatureManager _featureManager;
public HomeController(IFeatureManager featureManager)
{
_featureManager = featureManager;
}
public async Task<IActionResult> Index()
{
if (await _featureManager.IsEnabledAsync("FeatureB"))
{
return new ContentResult { Content = "Enable FeatureB" };
}
return View();
}
...
}
以上,Microsoft.FeatureManagement.AspNetCore 這個套件最基本的使用方式分享給大家,希望對大家有幫助。