【问题标题】:How to disable a global filter in ASP.Net MVC selectively如何有选择地禁用 ASP.Net MVC 中的全局过滤器
【发布时间】:2012-03-31 06:53:16
【问题描述】:

我已经为我打开和关闭 NHibernate 会话的所有控制器操作设置了一个全局过滤器。这些操作中的 95% 需要一些数据库访问权限,但 5% 不需要。是否有任何简单的方法可以为这 5% 禁用此全局过滤器。我可以反过来,只装饰需要数据库的操作,但这将是更多的工作。

【问题讨论】:

标签: asp.net-mvc actionfilterattribute global-filter


【解决方案1】:

你可以写一个标记属性:

public class SkipMyGlobalActionFilterAttribute : Attribute
{
}

然后在你的全局动作过滤器中测试这个标记在动作上的存在:

public class MyGlobalActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(SkipMyGlobalActionFilterAttribute), false).Any())
        {
            return;
        }

        // here do whatever you were intending to do
    }
}

然后如果你想从全局过滤器中排除一些动作,只需用标记属性装饰它:

[SkipMyGlobalActionFilter]
public ActionResult Index()
{
    return View();
}

【讨论】:

  • 非常棒的解决方案。我想知道我自己怎么没有想到这一点。谢谢。
  • 这很有帮助,谢谢。只是为了帮助未来的人,你也可以把同样的东西放在一个Controller上,并使用filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes来应用到所有的action。
  • 任何人在 api 解决方案之后语法略有不同:actionContext.ActionDescriptor.GetCustomAttributes().Any()
  • 如果您还想跳过特定的控制器属性,那么您应该添加 ||在条件 filterContext.ControllerContext.ControllerDescriptor.GetCustomAttributes().Any()
  • 就像 Leniel 写的,在 ASP.NET Core 中,filterContext.ActionDescriptor 没有 GetCustomAttributes 方法。如何在 ASP.NET 核心中做到这一点?
【解决方案2】:

不过,Darin Dimitrov 接受的答案很好并且效果很好,但对我来说,找到了here 的最简单和最有效的答案。

您只需要在您的逻辑开始之前向您的属性添加一个布尔属性并对其进行检查:

public class DataAccessAttribute: ActionFilterAttribute
{
    public bool Disable { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (Disable) return;

        // Your original logic for your 95% actions goes here.
    }
}

然后在你的 5% 的操作中像这样使用它:

[DataAccessAttribute(Disable=true)]
public ActionResult Index()
{            
    return View();
}

【讨论】:

  • 我在这里注意到了一个小问题——如果你在过滤器的全局声明中指定了一个订单,你还必须在属性上指定相同的订单。例如[DataAccessAttribute(Disable=true,Order=2)]filters.Add(new DataAccessAttribute(), 2);
【解决方案3】:

在 AspNetCore 中,@darin-dimitrov 接受的答案可以适应如下工作:

首先,在标记属性上实现IFilterMetadata

public class SkipMyGlobalActionFilterAttribute : Attribute, IFilterMetadata
{
}

然后在Filters 属性中搜索ActionExecutingContext 上的此属性:

public class MyGlobalActionFilter : IActionFilter
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.Filters.OfType<SkipMyGlobalActionFilterAttribute>().Any())
        {
            return;
        }

        // etc
    }
}

【讨论】:

    【解决方案4】:

    至少现在,这很容易:要从一个动作中排除所有动作过滤器,只需添加 OverrideActionFiltersAttribute

    其他过滤器也有类似的属性:OverrideAuthenticationAttributeOverrideAuthorizationAttributeOverrideExceptionAttribute

    另见https://www.strathweb.com/2013/06/overriding-filters-in-asp-net-web-api-vnext/

    【讨论】:

    • 现在是 2013+?不知道为什么这不是公认的答案,因为它看起来很规范。
    【解决方案5】:

    创建自定义过滤器提供程序。编写一个将实现 IFilterProvider 的类。这个 IFilterProvider 接口有一个 GetFilters 方法,它返回需要执行的过滤器。

    public class MyFilterProvider : IFilterProvider
    {
            private readonly List<Func<ControllerContext, object>> filterconditions = new List<Func<ControllerContext, object>>();
            public void Add(Func<ControllerContext, object> mycondition)
            {
                filterconditions.Add(mycondition);
            }
    
            public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
            {
                return from filtercondition in filterconditions
                       select filtercondition(controllerContext) into ctrlContext
                       where ctrlContext!= null
                       select new Filter(ctrlContext, FilterScope.Global);
            }
    }
    

    ================================================ ===============================
    在 Global.asax.cs 中

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
            {
                MyFilterProvider provider = new MyFilterProvider();
                provider.Add(d => d.RouteData.Values["action"].ToString() != "SkipFilterAction1 " ? new NHibernateActionFilter() : null);
                FilterProviders.Providers.Add(provider);
            }
    
    
    protected void Application_Start()
    {
        RegisterGlobalFilters(GlobalFilters.Filters);
    }
    

    【讨论】:

      【解决方案6】:

      好吧,我想我已经为 ASP.NET Core 工作了。
      代码如下:

      public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
          {
              // Prepare the audit
              _parameters = context.ActionArguments;
      
              await next();
      
              if (IsExcluded(context))
              {
                  return;
              }
      
              var routeData = context.RouteData;
      
              var controllerName = (string)routeData.Values["controller"];
              var actionName = (string)routeData.Values["action"];
      
              // Log action data
              var auditEntry = new AuditEntry
              {
                  ActionName = actionName,
                  EntityType = controllerName,
                  EntityID = GetEntityId(),
                  PerformedAt = DateTime.Now,
                  PersonID = context.HttpContext.Session.GetCurrentUser()?.PersonId.ToString()
              };
      
              _auditHandler.DbContext.Audits.Add(auditEntry);
              await _auditHandler.DbContext.SaveChangesAsync();
          }
      
          private bool IsExcluded(ActionContext context)
          {
              var controllerActionDescriptor = (Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)context.ActionDescriptor;
      
              return controllerActionDescriptor.ControllerTypeInfo.IsDefined(typeof(ExcludeFromAuditing), false) ||
                     controllerActionDescriptor.MethodInfo.IsDefined(typeof(ExcludeFromAuditing), false);
          }
      

      相关代码在'IsExcluded'方法中。

      【讨论】:

        【解决方案7】:

        您可以像这样更改过滤器代码:

         public class NHibernateActionFilter : ActionFilterAttribute
            {
                public IEnumerable<string> ActionsToSkip { get; set; }
        
                public NHibernateActionFilter(params string[] actionsToSkip)
                {
                    ActionsToSkip = actionsToSkip;
                }
        
                public override void OnActionExecuting(ActionExecutingContext filterContext)
                {
                    if (null != ActionsToSkip && ActionsToSkip.Any(a => 
        String.Compare(a,  filterContext.ActionDescriptor.ActionName, true) == 0))
                        {
                            return;
                        }
                   //here you code
                }
            }
        

        并使用它:

        [NHibernateActionFilter(new[] { "SkipFilterAction1 ", "Action2"})]
        

        【讨论】:

        • 这是一种方法,但随着时间的推移有点难以维护。
        • 如果您更改了动作名称并忘记在属性用法中更改它,编译器不会警告您。这很可能会导致维护痛苦。我更喜欢 Darin 的回答,因为您不必手动指定操作。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多