如何在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环境可见"); } }
扩展:支持更复杂的条件
如果你的隐藏条件不止环境(比如基于配置、用户角色、自定义逻辑),可以扩展特性支持自定义判断逻辑:
- 定义一个条件检查接口:
public interface IConditionChecker { bool ShouldHide(ActionModel action, IServiceProvider serviceProvider); }
- 修改自定义特性:
[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; } }
- 更新约定逻辑:
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
相关产品推荐
相关产品推荐

