You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:13:33