深度探索.NET Feature Management功能开关的魔法

这篇具有很好参考价值的文章主要介绍了深度探索.NET Feature Management功能开关的魔法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

.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 包管理 管理工具安装等

依赖注入

.Net 功能管理器是通过框架的本机配置系统配置的,简单来说只要是.Net 的配置系统支持的数据源都可以用做功能管理(FeatureManagement)的配置源

.NET 中的配置是使用一个或多个配置提供程序执行的。 配置提供程序使用各种配置源从键值对读取配置数据:

  • 设置文件,例如 appsettings.json
  • 环境变量
  • Azure Key Vault
  • Azure 应用配置
  • 命令行参数
  • 已安装或已创建的自定义提供程序
  • 目录文件
  • 内存中的 .NET 对象
  • 第三方提供程序

.NET 中的配置提供程序

依赖注入:

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
  }

功能开关注册成 Scoped

AddFeatureManagement 方法将特性管理服务作为单例添加到应用程序中,但有些情况下可能需要将特性管理服务添加为Scoped(作用域服务)。例如,我们可能希望使用 Scoped 以获取上下文信息的功能过滤器。在这种情况下,应该使用 AddScopedFeatureManagement 方法, 这将确保功能管理服务(包括功能过滤器)被添加为 Scoped 服务。

//功能管理注册 Scoped 作用域
service.AddScopedFeatureManagement();

功能管理的基本形式是检查功能标志是否已启用,然后根据结果执行操作。这通过 IFeatureManagerIsEnabledAsync 方法来实现。

对我们上面的 FeatureManager 的配置来做一个验证

  • sayhello 功能开关标志测试
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,符合预期,功能未开启。

上面的示例简单讲解了一下功能开关的使用,接下来深入了解功能开关的配置

功能开关的定义

功能开关的标志由两部分组成:名称和用于启用功能的过滤器列表。

功能过滤器(Feature filters)定义了功能应何时启用的场景。在评估特性是开启还是关闭时,会遍历其功能过滤器列表,直到其中一个过滤器决定启用该特性。如果一个过滤器都没有标识改功能应该开启,那此功能标志是关闭的状态。

内置过滤器

  • AlwaysOn: 总是开启
  • PercentageFilter:根据百分比随机启用/禁用功能。这个过滤器允许您基于一个百分比值来决定功能被启用的概率,提供了一种简单而灵活的机制来控制特性的曝光范围。
  • TimeWindowFilter:在预定义的时间窗口内启用特性。这个过滤器允许您指定特性的开始和结束时间,确保特性只在特定的时间段内可用。这对于限时活动或测试场景非常有用。
  • TargetingFilter:(这个主要是在Azure 用为目标受众启用功能的分阶段推出针对特定用户或用户组启用特性。这个过滤器允许您根据用户属性或标识来启用特性,例如基于用户 ID、角色、地区等。此外,对于此过滤器,您还可以设置一个百分比值,以进一步控制特性在目标用户中的启用概率。

详细信息可以参考注册功能筛选器 Docs

过滤器的配置指南

需要注意的是在功能标志名称中禁止使用冒号:,这是为了遵循一定的命名规范,避免与现有的或未来的功能管理系统产生冲突或造成解析错误。在定义功能标志名称时,请确保使用合法和合适的字符组合,以确保系统的稳定性和可维护性。
功能使用 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,符合预期


TimeWindow 过滤器

  "FeatureManagement": {
    "featureTimeWindow": {
      "EnabledFor": [
        {
          "Name": "TimeWindow",
          "Parameters": {
            "Start": "2024-03-26 13:30:00",
            "End": "2024-03-27 13:30:00"
          }
        }
      ]
    }
  }

指定了一个名为 TimeWindow 的功能过滤器。这是一个可配置的功能过滤,具有 Parameters 属性,配置了功能活动的开始和结束时间 。

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)它根据指定的百分比值随机启用或禁用某个特性。这种过滤器允许您控制特性的曝光率,以便在不同的用户群体中测试特性的效果,或者在逐步推广新特性时控制其影响范围。

  "FeatureManagement": {
    "featurePercentage": {
      "EnabledFor": [
        {
          "Name": "Percentage",
          "Parameters": {
            "Value": "50"
          }
        }
      ]
    }
  },

app.MapGet("/featurePercentage", async Task<IResult> (IFeatureManager manager) =>
{
    if (await manager.IsEnabledAsync("featurePercentage"))
    {
        return TypedResults.Ok($"featurePercentage is enabled !");
    }
    return TypedResults.NotFound();
}).WithSummary("Percentage 过滤器测试");

连续测两次

第一次测试结果: 返回 200

第二次测试结果:返回 404

通过测试结果可以看出有百分之五十的几率成功,符合预期。

RequirementType

功能标志的 RequirementType 属性用于确定在评估功能状态时,过滤器应该使用任何(Any)还是全部(All)逻辑。如果未指定 RequirementType,则默认值为 Any

  • Any 表示只需一个过滤器评估为 true,特性就会被启用。
  • All 表示每个过滤器都必须评估为 true,特性才会被启用。
    RequirementTypeAll 会改变遍历方式。首先,如果没有过滤器,则功能将被禁用。然后,遍历特性过滤器,直到其中一个过滤器决定应将功能禁用。如果没有过滤器指示应禁用功能,则该功能将被视为已启用。
  "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当前时间小于这个日期

无论调用几次还是还是 404,结果符合我们的预期。

自定义过滤器

要实现一个功能过滤器,必须要实现的是一个IFeatureFilter接口,接口包含了一个EvaluateAsync的方法。当功能标志指定启用该过滤器时,将调用 EvaluateAsync方法,如果方法返回的是true,则表示应该启用功能。

定义一个中间件接口只对某个用户组做开放,这个场景在 C 端的产品上比较常见,比如说部分功能的内测。

[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,取决于用户是否已通过身份验证
    }
}

FilterAlias是定义过滤器的别名,我们在配置文件中指定时需要用别名,IFeatureFilter接口返回的信息决定功能是否启用,IFeatureFilterMetadata是一个空的标记接口,用于评估功能状态的特征过滤器的标记接口,IFilterParametersBinder 接口用于参数绑定。

  • json 配置
  "FeatureManagement": {
    "featureAuthencatedGroup": {
      "EnabledFor": [
        {
          "Name": "AuthenticatedGroup",
          "Parameters": {
            "Groups": [ "AdminGroup", "GroupOne" ]
          }
        }
      ]
    }
  }
  • 依赖注入
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 ,符合预期

一个小 tips;如果多个过滤器有同一个别名是,可以用命名空间加别名的方式来定义唯一一个过滤器,例如,Microsoft.Percentage 是一个完全限定的别名,它明确指出了 Percentage过滤器位于 Microsoft 命名空间下

自定义开启中间件

  "FeatureManagement": {
    "featureMiddleWare": {
      "EnabledFor": [
        {
          "Name": "Percentage",
          "Parameters": {
            "Value": "50"
          }
        }
      ]
    }
  }

自定义中间件

public class FeatureMiddleWare(RequestDelegate next)
{
    public async Task Invoke(HttpContext context)
    {
        Console.WriteLine("FeatureMiddleWare管道执行之前~");
        await next(context);
        Console.WriteLine("FeatureMiddleWare管道执行之后~");
    }
}

添加扩展方法

//测试中间件的功能开启
app.UseMiddlewareForFeature<FeatureMiddleWare>("featureMiddleWare");

随便调用一个接口测试一下,可以看到管道根据百分比触发成功

通过上述调用,应用程序添加了一个中间件组件,只有在特性“featureMiddleWare”被启用时才会出现在请求管道中。如果在运行时启用/禁用特性,中间件管道可以动态更改。

这是建立在基于特性对整个应用程序进行分支的更通用能力之上。

app.UseForFeature(featureName, appBuilder =>
{
appBuilder.UseMiddleware<T>();
});

MinimalApis 集成

在我们的 MVC 或者 Razor Pages 中有如下方案来启用工农的开关,不过多介绍大家可以官方浏览学习。

FeatureManagement-Dotnet

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 的封装借助最小 ApiMapGroup 可以对一组相关的 Api 进行功能管理,简化了我们一个个 Api 注册。

最后

在本文中,我们深入探讨了.NET Feature Management 库的安装、配置和使用方法,以及如何利用功能开关来动态管理应用程序的功能。以下是关键点的总结和提炼:

  • 安装与依赖注入:通过.NET CLINuGet Package Manager 安装等方式 Microsoft.FeatureManagement.AspNetCore 库,并在应用程序中添加功能管理服务的依赖注入。

  • 功能定义与配置:通过.NET 的配置系统,在 appsettings.json 中定义功能标志,指定功能的启用和禁用状态,以及可选的功能过滤器配置。

  • 自定义功能过滤器:实现 IFeatureFilter 接口来定义自定义功能过滤器,根据特定条件决定功能是否启用,例如基于用户组、时间窗口或百分比等条件。

  • 功能开关的使用:利用 IFeatureManagerIsEnabledAsync 方法检查功能是否启用,根据不同的功能状态执行相应的逻辑,实现功能的动态控制。

  • RequirementType 设置:可以通过 RequirementType 属性指定功能过滤器的逻辑要求,是 Any 还是 All,决定多个过滤器的组合逻辑。

  • 自定义中间件的动态切换:通过自定义功能过滤器和中间件,可以根据功能状态动态调整请求管道,实现功能开关对中间件的控制。

  • 最小 API 集成:在 Minimal APIs 中,利用 IEndpointFilter 接口来简化功能开关的应用,将功能管理应用到最小 API 的端点上,实现对一组相关 API 的功能管理。

通过以上总结和提炼,您可以更好地了解和应用.NET Feature Management 库,实现灵活的功能管理和动态控制应用程序的功能。

有条件的富哥可以体验一下在 Azure 应用程序配置中管理功能标志

更多详细的内容请浏览FeatureManagement-Dotnet

本文测试完整源代码文章来源地址https://www.toymoban.com/news/detail-843704.html

到了这里,关于深度探索.NET Feature Management功能开关的魔法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 循环神经网络解密:探索RNN的魔法

    循环神经网络(Recurrent Neural Network,RNN)是一种神经网络模型,专门用于处理序列数据和时间序列数据。与其他传统神经网络模型不同,RNN具有循环连接,使得信息可以在网络中传递并影响后续的输出。这使得RNN在处理具有时序性的数据时表现出色。 RNN的结构包含以下要素:

    2024年02月09日
    浏览(55)
  • 编织魔法:探索Python字符串的奇幻世界

    在当今计算机编程领域中,Python语言以其简洁、优雅和易读的特性,成为了最受欢迎的编程语言之一。其中的字符串处理是Python编程中不可或缺的技术之一。本篇博客将介绍Python字符串的基本概念,包括字符串的定义、索引和切片操作。我们将深入探讨字符串的常用方法,如

    2024年02月11日
    浏览(47)
  • 探索Python的魔法世界,开启编程奇幻之旅!

      无需魔杖,只需键盘,Python将带你进入一个充满奇幻和创造力的世界。本篇博客将引领你走进Python的魔法大门,从编程新手蜕变为编码魔法师,开启一段激动人心的编程奇幻之旅。 如果你是一名编程初学者,并且想要掌握一门强大且广泛应用的编程语言,那么Python绝对是你

    2024年02月10日
    浏览(56)
  • 探索C语言的内存魔法:动态内存管理解析

    ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C语言学习 贝蒂的主页:Betty‘s blog 通过前面的学习,我们已经掌握了两种开辟内存的方法,分别是: 但是静态开辟的空间明显有两个缺陷: 空间开辟⼤⼩是 固定 的。 数组在申明的时候,

    2024年02月19日
    浏览(45)
  • 开启物联网的魔法之门 - 深入探索发布/订阅模式

    发布订阅模式(Publish-Subscribe Pattern)是一种 消息传递模式 ,它将发送消息的客户端(发布者)与接收消息的客户端(订阅者)解耦,使得两者不需要建立直接的联系也不需要知道对方的存在。 MQTT 发布/订阅模式的精髓在于由一个被称为 代理 (Broker)的中间角色负责所有消

    2024年02月03日
    浏览(46)
  • 探索指针的奇妙世界,程序中的魔法箭头(上)

    指针是内存中最小单元(字节)的编号,也就是地址 我们平时口中所说的指针,通常说的是指针变量。 总结:指针就是地址,平时口头说的指针是指针变量 指针变量:我们通过取地址操作符取出变量的内存起始地址,把地址存放到一个变量中,这个变量就是指针变量。 总结

    2024年01月21日
    浏览(45)
  • (八)穿越多媒体奇境:探索Streamlit的图像、音频与视频魔法

    欢迎各位读者来到“最全Streamlit教程”专栏系列!如果您正在寻找一种简单而强大的方式来创建交互式数据应用程序,那么Streamlit无疑是您的最佳选择。作为该领域的热门框架,Streamlit让数据科学家、开发者和爱好者能够以前所未有的速度构建出引人入胜的数据可视化工具。

    2024年02月13日
    浏览(47)
  • Openlayers的交互功能(三)——Feature的选中Select控件

    前面的两篇文章分别介绍了Openlayers的基本情况和初始化地图的流程以及Feature是什么,从这篇文章开始,将对Feature的交互功能进行介绍。 Select是交互事件的一种,用于选中 矢量图层 上的几何图形,添加选择交互事件后,点击地图上的几何图形,或者将鼠标移动到几何图形上

    2024年02月03日
    浏览(47)
  • “代码驭宠而行“:探索Python的魔法世界,开启编程奇幻之旅!

    无需魔杖,只需键盘,Python将带你进入一个充满奇幻和创造力的世界。本篇博客将引领你走进Python的魔法大门,从编程新手蜕变为编码魔法师,开启一段激动人心的编程奇幻之旅。 如果你是一名编程初学者,并且想要掌握一门强大且广泛应用的编程语言,那么Python绝对是你的

    2024年02月11日
    浏览(40)
  • 探索未来的旋律:AI生成音乐的魔法(附GPT镜像站大全)

    在数字化时代的浪潮中,人工智能(AI)已经触及了我们生活的方方面面,从自动驾驶汽车到智能家居系统,再到高度个性化的推荐算法。然而,AI的魔法并不止步于此。近年来,AI在艺术和创造性领域的应用也日益增多,尤其是在音乐创作方面,AI正以其独特的方式重新定义

    2024年04月12日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包