Azure配置功能管理器:调用IsEnabled时向自定义FeatureFilter传参
解决Azure App Configuration Feature Manager自定义过滤器传递非HttpContext参数的问题
我之前正好在项目里处理过类似的需求,不管是Web应用还是Azure函数/WebJob场景,都有对应的解决方案,下面一步步给你讲清楚:
核心思路
默认的IFeatureManager.IsEnabled方法提供了重载,可以传递额外的运行时参数,这些参数会被传入自定义过滤器的Evaluate方法的FeatureFilterEvaluationContext中。我们只需要调整过滤器的逻辑,既可以兼容原来的HttpContext场景,也能接收手动传递的参数。
步骤1:修改自定义FeatureFilter
首先调整你的AllowedUsersFeatureFilter,让它支持两种参数来源:HttpContext(Web应用)和手动传递的参数(函数/WebJob/后台任务)。这里我们可以把IHttpContextAccessor设为可选注入,同时从context.Parameters中读取额外参数:
using Microsoft.FeatureManagement; using Microsoft.FeatureManagement.FeatureFilters; using System.Security.Claims; using System.Collections.Generic; using System.Linq; using System; // 过滤器的配置参数(从App Configuration读取) public class AllowedUsersFilterSettings { public string Aliases { get; set; } } // 用于传递运行时用户参数的类 public class UserRuntimeParams { public string UserAlias { get; set; } } [FilterAlias("AllowedUsers")] public class AllowedUsersFeatureFilter : IFeatureFilter { private readonly IHttpContextAccessor _httpContextAccessor; // 把IHttpContextAccessor设为可选,兼容非Web场景 public AllowedUsersFeatureFilter(IHttpContextAccessor httpContextAccessor = null) { _httpContextAccessor = httpContextAccessor; } public bool Evaluate(FeatureFilterEvaluationContext context) { // 先读取从App Configuration配置的过滤器参数 var filterSettings = context.Parameters.Get<AllowedUsersFilterSettings>(); if (filterSettings == null || string.IsNullOrWhiteSpace(filterSettings.Aliases)) return false; string userAlias = null; // 优先读取手动传递的运行时参数 var runtimeParams = context.Parameters.Get<UserRuntimeParams>(); if (runtimeParams != null && !string.IsNullOrWhiteSpace(runtimeParams.UserAlias)) { userAlias = runtimeParams.UserAlias; } // 没有传递参数时,再尝试从HttpContext获取(Web应用场景) else if (_httpContextAccessor?.HttpContext != null) { var userEmail = _httpContextAccessor.HttpContext.User?.FindFirst(ClaimTypes.Upn)?.Value; userAlias = userEmail?.Split('@').FirstOrDefault(); } if (string.IsNullOrWhiteSpace(userAlias)) return false; // 检查用户别名是否在允许列表中 return filterSettings.Aliases.Split(',') .Select(a => a.Trim()) .Contains(userAlias, StringComparer.OrdinalIgnoreCase); } }
步骤2:调用IsEnabled时传递参数
根据不同的场景,调用方式略有不同:
Web应用场景(兼容HttpContext和手动传参)
如果还是依赖HttpContext,直接调用无参数的重载即可:
bool isEnabled = await _featureManager.IsEnabledAsync("YourFeatureName");
如果需要手动传参(比如后台任务、测试),使用带参数字典的重载:
// 构造运行时参数 var runtimeParams = new UserRuntimeParams { UserAlias = "john_doe" }; var parameters = new Dictionary<string, string>(); // 用AddJson扩展方法把对象序列化为JSON存入参数(需要引用Microsoft.Extensions.Configuration.AzureAppConfiguration) parameters.AddJson(nameof(UserRuntimeParams), runtimeParams); bool isEnabled = await _featureManager.IsEnabledAsync("YourFeatureName", parameters);
或者直接用键值对传递:
var parameters = new Dictionary<string, string> { { "UserAlias", "john_doe" } }; // 过滤器里可以用context.Parameters["UserAlias"]获取 bool isEnabled = await _featureManager.IsEnabledAsync("YourFeatureName", parameters);
Azure函数应用场景
Azure函数(尤其是隔离进程模型)没有HttpContext,必须手动传递参数。首先在Program.cs中配置Feature Manager:
using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.FeatureManagement; using Microsoft.Extensions.Configuration.AzureAppConfiguration; var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() .ConfigureServices(services => { // 添加Azure App Configuration并启用功能管理 services.AddAzureAppConfiguration(options => { options.Connect("<你的App Configuration连接字符串>") .UseFeatureFlags(); }) .AddFeatureManagement() .AddFeatureFilter<AllowedUsersFeatureFilter>(); // 非Web场景不需要注册IHttpContextAccessor // services.AddHttpContextAccessor(); }) .Build(); host.Run();
然后在函数中注入IFeatureManager并传递参数:
using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.FeatureManagement; using System.Collections.Generic; using System.Threading.Tasks; public class FeatureFlagFunction { private readonly IFeatureManager _featureManager; public FeatureFlagFunction(IFeatureManager featureManager) { _featureManager = featureManager; } [Function("CheckFeature")] public async Task<HttpResponseData> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req) { // 从请求中获取用户别名(比如查询参数) req.Query.TryGetValue("userAlias", out var userAlias); // 构造参数 var parameters = new Dictionary<string, string> { { "UserAlias", userAlias } }; bool isEnabled = await _featureManager.IsEnabledAsync("YourFeatureName", parameters); var response = req.CreateResponse(); await response.WriteAsJsonAsync(new { IsFeatureEnabled = isEnabled }); return response; } }
Azure WebJob场景
WebJob的实现和函数类似,同样在Program.cs中配置Feature Manager,然后在Job类中注入IFeatureManager:
using Microsoft.Extensions.Hosting; using Microsoft.FeatureManagement; using System.Threading.Tasks; public class Program { public static void Main() { var host = new HostBuilder() .ConfigureServices(services => { services.AddAzureAppConfiguration(options => { options.Connect("<你的App Configuration连接字符串>") .UseFeatureFlags(); }) .AddFeatureManagement() .AddFeatureFilter<AllowedUsersFeatureFilter>(); services.AddHostedService<MyWebJob>(); }) .Build(); host.Run(); } } public class MyWebJob : IHostedService { private readonly IFeatureManager _featureManager; private readonly IHostApplicationLifetime _appLifetime; public MyWebJob(IFeatureManager featureManager, IHostApplicationLifetime appLifetime) { _featureManager = featureManager; _appLifetime = appLifetime; } public async Task StartAsync(CancellationToken cancellationToken) { // 假设从队列/数据库获取用户别名 string userAlias = "jane_smith"; var parameters = new Dictionary<string, string> { { "UserAlias", userAlias } }; bool isEnabled = await _featureManager.IsEnabledAsync("YourFeatureName", parameters); if (isEnabled) { // 执行功能启用后的业务逻辑 } _appLifetime.StopApplication(); } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; }
注意事项
- 确保过滤器的构造函数中
IHttpContextAccessor是可选的(设置默认值null),这样在非Web场景下也能正常实例化。 - 如果使用强类型参数传递,记得要确保
FeatureFilterEvaluationContext.Parameters能正确反序列化对象,使用AddJson扩展方法是最简单的方式。 - App Configuration中仍然需要配置过滤器的固定参数(比如
AllowedUsers的Aliases值),手动传递的是运行时的动态参数(比如当前用户别名)。
内容的提问来源于stack exchange,提问作者PraveenLearnsEveryday
相关产品推荐
相关产品推荐

