如何在 ASP.Net MVC 中有选择地禁用全局过滤器

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

36224 次浏览

你可以写一个标记属性:

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();
}

你可以像这样改变你的过滤器代码:

 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"})]

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

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);
}

嗯,我想我已经让它为 ASP.NET 核心工作了。
密码是这样的:

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);
}

相关代码位于“ IsExclused”方法中。

至少现在,这很容易: 从一个操作中排除所有操作过滤器,只需添加 覆盖 ActionFiltersAttribute

其他过滤器也有类似的属性: 覆盖身份验证属性覆盖授权属性覆盖 ExceptionAttribute

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

不过,达林 · 迪米特洛夫给出的公认答案很好,效果也不错,但对我来说,最简单、最有效的答案是 给你

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

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();
}

在 AspNetCore 中,@darin-dimitrov 给出的公认答案可以改编如下:

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

public class SkipMyGlobalActionFilterAttribute : Attribute, IFilterMetadata
{
}

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

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


// etc
}
}