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

如何在ASP.NET Core中便捷实现Action方法的条件隐藏?

我之前刚好解决过类似的需求——要在ASP.NET Core里根据运行时条件(比如非开发环境)完全隐藏Action,不仅要拦截访问,还要从路由系统、Swagger文档里彻底移除,就像带动态条件的[NonAction]。下面给你一套完整的实现方案,亲测好用:

实现步骤

1. 自定义条件隐藏特性

先创建一个自定义特性,用来标记需要条件隐藏的Action,我们先从环境判断入手,后续可以扩展其他条件:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ConditionalNonActionAttribute : Attribute
{
    // 指定需要隐藏该Action的环境列表
    public string[] EnvironmentsToHide { get; }

    public ConditionalNonActionAttribute(params string[] environmentsToHide)
    {
        EnvironmentsToHide = environmentsToHide;
    }
}

2. 通过IActionModelConvention过滤Action

要让路由系统完全“看不到”这个Action,得在应用模型构建阶段修改Action的状态。我们可以实现IActionModelConvention,它能在路由初始化时干预Action模型:

public class ConditionalNonActionConvention : IActionModelConvention
{
    private readonly IWebHostEnvironment _hostEnv;

    public ConditionalNonActionConvention(IWebHostEnvironment hostEnv)
    {
        _hostEnv = hostEnv;
    }

    public void Apply(ActionModel action)
    {
        // 查找当前Action是否标记了我们的自定义特性
        var conditionalAttr = action.Attributes
            .OfType<ConditionalNonActionAttribute>()
            .FirstOrDefault();

        if (conditionalAttr == null) return;

        // 判断当前环境是否在要隐藏的列表中
        if (conditionalAttr.EnvironmentsToHide.Contains(
            _hostEnv.EnvironmentName, 
            StringComparer.OrdinalIgnoreCase))
        {
            // 标记为NonAction,同时从控制器的Action集合中移除
            action.IsNonAction = true;
            action.Controller.Actions.Remove(action);
        }
    }
}

然后在Program.cs里注册这个约定,注意要注入IWebHostEnvironment:

builder.Services.AddControllers(options =>
{
    // 动态注入服务到约定中
    options.Conventions.Add(serviceProvider => 
        new ConditionalNonActionConvention(
            serviceProvider.GetRequiredService<IWebHostEnvironment>()));
});

3. 处理Swagger文档(确保移除对应API)

虽然上面的步骤已经把Action从路由模型中移除了,但为了保险起见,我们可以加一个Swagger文档过滤器,确保Swagger里也不会出现这个Action:

public class ConditionalNonActionSwaggerFilter : IDocumentFilter
{
    private readonly IWebHostEnvironment _hostEnv;

    public ConditionalNonActionSwaggerFilter(IWebHostEnvironment hostEnv)
    {
        _hostEnv = hostEnv;
    }

    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        foreach (var apiDesc in context.ApiDescriptions)
        {
            var actionDesc = apiDesc.ActionDescriptor as ControllerActionDescriptor;
            if (actionDesc == null) continue;

            var conditionalAttr = actionDesc.MethodInfo
                .GetCustomAttribute<ConditionalNonActionAttribute>();

            if (conditionalAttr != null && 
                conditionalAttr.EnvironmentsToHide.Contains(
                    _hostEnv.EnvironmentName, 
                    StringComparer.OrdinalIgnoreCase))
            {
                // 从Swagger文档中移除对应的路径
                var path = $"/{apiDesc.RelativePath}";
                if (swaggerDoc.Paths.ContainsKey(path))
                {
                    swaggerDoc.Paths.Remove(path);
                }
            }
        }
    }
}

注册Swagger时添加这个过滤器:

builder.Services.AddSwaggerGen(c =>
{
    c.DocumentFilter<ConditionalNonActionSwaggerFilter>();
});

4. 使用示例

现在你可以在控制器的Action上直接标记特性,指定要隐藏的环境:

[ApiController]
[Route("api/[controller]")]
public class DemoController : ControllerBase
{
    [HttpGet("public-endpoint")]
    public IActionResult PublicEndpoint()
    {
        return Ok("这个接口在所有环境都可见");
    }

    [HttpGet("dev-only-endpoint")]
    // 在Production和Staging环境下隐藏这个Action
    [ConditionalNonAction("Production", "Staging")]
    public IActionResult DevelopmentOnlyEndpoint()
    {
        return Ok("这个接口只在Development环境可见");
    }
}

扩展:支持更复杂的条件

如果你的隐藏条件不止环境(比如基于配置、用户角色、自定义逻辑),可以扩展特性支持自定义判断逻辑:

  1. 定义一个条件检查接口:
public interface IConditionChecker
{
    bool ShouldHide(ActionModel action, IServiceProvider serviceProvider);
}
  1. 修改自定义特性:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ConditionalNonActionAttribute : Attribute
{
    public Type ConditionCheckerType { get; }

    public ConditionalNonActionAttribute(Type conditionCheckerType)
    {
        if (!typeof(IConditionChecker).IsAssignableFrom(conditionCheckerType))
        {
            throw new ArgumentException("条件检查类型必须实现IConditionChecker接口");
        }
        ConditionCheckerType = conditionCheckerType;
    }
}
  1. 更新约定逻辑:
public class ConditionalNonActionConvention : IActionModelConvention
{
    private readonly IServiceProvider _serviceProvider;

    public ConditionalNonActionConvention(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void Apply(ActionModel action)
    {
        var conditionalAttr = action.Attributes
            .OfType<ConditionalNonActionAttribute>()
            .FirstOrDefault();

        if (conditionalAttr == null) return;

        // 实例化条件检查器
        var checker = (IConditionChecker)Activator.CreateInstance(conditionalAttr.ConditionCheckerType);
        if (checker.ShouldHide(action, _serviceProvider))
        {
            action.IsNonAction = true;
            action.Controller.Actions.Remove(action);
        }
    }
}

比如你可以写一个基于配置的检查器:

public class ConfigBasedConditionChecker : IConditionChecker
{
    public bool ShouldHide(ActionModel action, IServiceProvider serviceProvider)
    {
        var config = serviceProvider.GetRequiredService<IConfiguration>();
        return config.GetValue<bool>("HideDevEndpoints");
    }
}

然后使用:

[ConditionalNonAction(typeof(ConfigBasedConditionChecker))]
public IActionResult ConfigControlledEndpoint()
{
    return Ok("这个接口的可见性由配置控制");
}

这样就可以灵活应对各种复杂的隐藏条件了。


内容的提问来源于stack exchange,提问作者AndrewSilver

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 10:24:08