前言
.NET Feature Management
是一个用于管理应用程序功能的库,它可以帮助开发人员在应用程序中轻松地添加、移除和管理功能。
使用 Feature Management
,开发人员可以根据不同用户、环境或其他条件来动态地控制应用程序中的功能。
这使得开发人员可以更灵活地管理应用程序的功能,并根据需要快速调整和部署新功能。
Feature Management
还提供了一些方便的工具和 API
,帮助开发人员更轻松地实现功能管理和控制。
安装
.Net CLI
dotnet add package Microsoft.FeatureManagement.AspNetCore --version 4.0.0-preview2
Package Manager
NuGet\Install-Package Microsoft.FeatureManagement.AspNetCore -Version 4.0.0-preview2
或者 Vs
Nuget
包管理 管理工具安装等
https://learn.microsoft.com/zh-cn/dotnet/core/extensions/configuration-providers
依赖注入:
service.AddFeatureManagement();
默认情况下,功能管理器从 .NET
appsettings.json
配置数据的 FeatureManagement
Section
来获取数据
// Define feature flags in config file
"FeatureManagement": {
"sayHello": true, // On feature
"todo": false // Off feature
}
当然也可以自定义 Section
service.AddFeatureManagement(builder.Configuration.GetSection("CustomFeatureManagement"));
// Define feature flags in config file
"CustomFeatureManagement": {
"sayHello": true, // On feature
"todo": false // Off feature
}
app.MapGet("/sayHello", async Task<IResult> ([FromServices] IFeatureManager manager, string name) =>
{
if (await manager.IsEnabledAsync("sayHello"))
{
return TypedResults.Ok($"hello {name}");
}
return TypedResults.NotFound();
}).WithSummary("sayHello");
调用接口查看一下结果,在配置中我们的sayHello
设置为true
状态码为 200,返回信息"hello Ruipeng",符合预期,功能开启正常。
- todo 功能开关标志测试
app.MapGet("/todo", async Task<IResult> ([FromServices] IFeatureManager manager) =>
{
if (await manager.IsEnabledAsync("todo"))
{
return TypedResults.Ok($"todo is enabled !");
}
return TypedResults.NotFound();
}).WithSummary("todo");
调用接口查看一下结果,状态码 404,返回信息 Not Found,符合预期,功能未开启。
上面的示例简单讲解了一下功能开关的使用,接下来深入了解功能开关的配置
功能开关的定义
功能开关的标志由两部分组成:名称和用于启用功能的过滤器列表。
详细信息可以参考注册功能筛选器 Docs
https://learn.microsoft.com/zh-cn/azure/azure-app-configuration/howto-feature-filters-aspnet-core
过滤器的配置指南
需要注意的是在功能标志名称中禁止使用冒号:
,这是为了遵循一定的命名规范,避免与现有的或未来的功能管理系统产生冲突或造成解析错误。在定义功能标志名称时,请确保使用合法和合适的字符组合,以确保系统的稳定性和可维护性。功能使用 EnabledFor
属性来定义它们的功能过滤器
AlwaysOn过滤器
// Define feature flags in config file
"FeatureManagement": {
//始终启用该功能
"featureAlwaysOn": {
"EnabledFor": [
{
"Name": "AlwaysOn"
}
]
}
}
app.MapGet("/featureAlwaysOn", async Task<IResult> (IFeatureManager manager) =>
{
if (await manager.IsEnabledAsync("featureAlwaysOn"))
{
return TypedResults.Ok($"featureAlwaysOn is enabled !");
}
return TypedResults.NotFound();
}).WithSummary("featureAlwaysOn");
调用接口查看测试结果,返回 200,符合预期
app.MapGet("/featureTimeWindow", async Task<IResult> (IFeatureManager manager) =>
{
if (await manager.IsEnabledAsync("featureTimeWindow"))
{
return TypedResults.Ok($"featureTimeWindow is enabled !");
}
return TypedResults.NotFound();
}).WithSummary("TimeWindow 过滤器测试");
调用接口测试:返回 200 符合预期
Percentage过滤器
百分比过滤器(Percentage Filter)它根据指定的百分比值随机启用或禁用某个特性。这种过滤器允许您控制特性的曝光率,以便在不同的用户群体中测试特性的效果,或者在逐步推广新特性时控制其影响范围。
连续测两次
第一次测试结果: 返回 200
第二次测试结果:返回 404
"FeatureManagement": {
"featureRequirementTypeAll": {
"RequirementType": "All",
"EnabledFor": [
{
"Name": "TimeWindow",
"Parameters": {
"Start": "2024-03-27 13:00:00",
"End": "2024-05-01 13:00:00"
}
},
{
"Name": "Percentage",
"Parameters": {
"Value": "50"
}
}
]
}
},
app.MapGet("/featureRequirementTypeAll", async Task<IResult> (IFeatureManager manager) =>
{
if (await manager.IsEnabledAsync("featureRequirementTypeAll"))
{
return TypedResults.Ok($"featureRequirementTypeAll is enabled !");
}
return TypedResults.NotFound();
}).WithSummary("RequirementTypeAll 多过滤器测试");
上面的实例设置为 all
之后此功能标志的过滤器列表必须全部符合要求才能调用成功。
比如上面我设置的开始日期是2024-03-27 13:00:00
当前时间小于这个日期
[FilterAlias("AuthenticatedGroup")]
public class AuthenticatedGroupFilter : IFeatureFilter, IFeatureFilterMetadata, IFilterParametersBinder
{
public object BindParameters(IConfiguration parameters)
{
return parameters.Get<GroupSetting>() ?? new GroupSetting();
}
public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext featureFilterContext)
{
GroupSetting filterSettings = ((GroupSetting)featureFilterContext.Settings) ?? ((GroupSetting)BindParameters(featureFilterContext.Parameters));
// 假设您有一个方法来检查用户是否已通过身份验证
// 例如,这可能是一个从身份验证服务或中间件中获得的属性或方法
bool isAuthenticated = IsGroupAuthenticated(filterSettings);
return Task.FromResult(isAuthenticated);
}
private bool IsGroupAuthenticated(GroupSetting groupSetting)
{
// 在这里编写您的身份验证检查逻辑
// 这可能涉及到检查HTTP请求的上下文、会话状态、令牌等
// 具体的实现将取决于您使用的身份验证机制
// 示例:返回一个硬编码的值,表示用户是否已通过身份验证
// 在实际应用中,您应该实现实际的检查逻辑
return true; // 或者 false,取决于用户是否已通过身份验证
}
}
- 依赖注入
services.AddFeatureManagement() .AddFeatureFilter<AuthenticatedGroupFilter>();
调用 AddFeatureFilter
方法可把自定义的过滤器注册到功能管理器中。
app.MapGet("/featureAuthencatedGroup", async Task<IResult> (IFeatureManager manager) =>
{
if (await manager.IsEnabledAsync("featureAuthencatedGroup"))
{
return TypedResults.Ok($"featureAuthencatedGroup is enabled !");
}
return TypedResults.NotFound();
}).WithSummary("AuthencatedGroup 自定义过滤器测试");
测试一下,返回 200 ,符合预期
自定义中间件
添加扩展方法
//测试中间件的功能开启
app.UseMiddlewareForFeature<FeatureMiddleWare>("featureMiddleWare");
随便调用一个接口测试一下,可以看到管道根据百分比触发成功
services.AddMvc(o =>
{
o.Filters.AddForFeature<SomeMvcFilter>("FeatureX");
});
[FeatureGate("FeatureX")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
在 MinimalAps
中可以利用endpoint filter
来简化公功能的开关,
第一步创建最小 Api 的基类,所有的 MinimalApis 过滤器都要继承它
public abstract class FeatureFlagEndpointFilter(IFeatureManager featureManager) : IEndpointFilter
{
protected abstract string FeatureFlag { get; }
private readonly IFeatureManager _featureManager = featureManager;
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context,
EndpointFilterDelegate next)
{
var isEnabled = await _featureManager.IsEnabledAsync(FeatureFlag);
if (!isEnabled)
{
return TypedResults.NotFound();
}
return await next(context);
}
}
- 定义目标 Json 配置
"FeatureManagement": {
"featureUserApi": {
"EnabledFor": [
{
"Name": "Percentage",
"Parameters": {
"Value": "50"
}
}
]
}
- 定义最小 Api 过滤器
public class UserApiFeatureFilter(IFeatureManager featureManager) : FeatureFlagEndpointFilter(featureManager)
{
protected override string FeatureFlag => "featureUserApi";
}
- 定义 Api 接口测试
//最小Api分组功能添加
{
var userGroup = app.MapGroup("User").WithTags("User").AddEndpointFilter<UserApiFeatureFilter>(); ;
userGroup.MapGet("/featureUserApi", IResult (IFeatureManager manager) =>
{
return TypedResults.Ok($"featureUserApi is enabled !");
}).WithSummary("featureUserApi 最小Api过滤器测试");
}
调用测试,可以看出我们配置的百分比过滤器成功。
通过对 IEndpointFilter
的封装借助最小 Api
的 MapGroup
可以对一组相关的 Api 进行功能管理,简化了我们一个个 Api 注册。
最后
在本文中,我们深入探讨了.NET Feature Management
库的安装、配置和使用方法,以及如何利用功能开关来动态管理应用程序的功能。以下是关键点的总结和提炼:
通过以上总结和提炼,您可以更好地了解和应用.NET Feature Management
库,实现灵活的功能管理和动态控制应用程序的功能。
有条件的富哥可以体验一下在 Azure 应用程序配置中管理功能标志
https://learn.microsoft.com/zh-cn/azure/azure-app-configuration/manage-feature-flags
更多详细的内容请浏览FeatureManagement-Dotnet
https://github.com/microsoft/FeatureManagement-Dotnet
完整源代码
https://github.com/Dong-Ruipeng/dotNetParadise-FeatureManagement