Як вимкнути глобальний фільтр у ASP.Net MVC вибірково


77

Я встановив глобальний фільтр для всіх своїх дій контролера, в якому я відкриваю та закриваю сеанси NHibernate. 95% цих дій потребують доступу до бази даних, а 5% - ні. Чи є простий спосіб вимкнути цей глобальний фільтр для тих 5%. Я міг би піти навпаки і прикрасити лише ті дії, які потребують бази даних, але це було б набагато більше роботи.


1
А як щодо створення ще однієї дії і прикрасьте цим 5%. Щось на зразок NHibernateNotRequiredAttribute ()?
dreza

Відповіді:


151

Ви можете написати атрибут маркера:

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

а потім, якщо ви хочете виключити якусь дію із глобального фільтра, просто прикрасьте її атрибутом marker:

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

Дійсно чудове рішення. Цікаво, як я не думав про це сам. Дякую.
zszep

5
Це було корисно, дякую. Щоб допомогти майбутнім людям, ви також можете помістити те саме на Controller і використовувати filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes, щоб застосувати до всіх дій.
Дарил Тео,

4
у кожного після рішення api синтаксис дещо інший: actionContext.ActionDescriptor.GetCustomAttributes <SkipMyGlobalActionFilter> ()
.Any

А якщо ви також хочете пропустити певний атрибут контролера, тоді слід додати || в умові filterContext.ControllerContext.ControllerDescriptor.GetCustomAttributes <SkipMyGlobalActionFilter> ()
.Any

3
як писав Леніель, в ASP.NET Core, filterContext.ActionDescriptor не має методу GetCustomAttributes. Як це зробити в ядрі ASP.NET?
ashilon

19

Ви також можете зробити те, що описано в цьому чудовому дописі:

Виключити фільтр

Просто впровадьте спеціальний, ExcludeFilterAttributeа потім спеціальнийExcludeFilterProvider .

Чисте рішення і чудово працювало для мене!


1
Будь-яке подібне рішення для ядра aspnet?
smg

1
@smg, будь ласка, дивіться відповідь @ gt нижче
Алі Хасан,

11

Хоча прийнята відповідь Даріна Димитрова чудова і працює добре, але для мене найпростіша і найефективніша відповідь, заснована тут .

Вам просто потрібно додати логічне властивість до вашого атрибута та перевірити його, безпосередньо перед тим, як ваша логіка почнеться:

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);
Jon Story

8

У AspNetCore прийнята відповідь @ darin-dimitrov може бути адаптована до роботи наступним чином:

Спочатку реалізуємо IFilterMetadataна атрибуті marker:

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

3

Принаймні в наш час це досить просто: щоб виключити з дії всі фільтри дій, просто додайте OverrideActionFiltersAttribute .

Існують подібні атрибути для інших фільтрів: OverrideAuthenticationAttribute , OverrideAuthorizationAttribute і OverrideExceptionAttribute .

Дивіться також https://www.strathweb.com/2013/06/overriding-filters-in-asp-net-web-api-vnext/


Сьогодні 2013+? Не впевнений, чому це не прийнята відповідь, оскільки це здається канонічним.
Мартін Каподічі

2

Створіть власний постачальник фільтрів. Напишіть клас, який реалізує 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);
}

2

Ну, думаю, я змусив це працювати для 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'.


1

Ви можете змінити код фільтра таким чином:

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

2
Це один із способів це зробити, але трохи важко підтримувати з часом.
zszep

1
Якщо ви зміните назву дії та забудете змінити її у використанні атрибутів, компілятор не попередить вас. Це дуже ймовірно може спричинити підтримуючі болі. Я віддаю перевагу відповіді Даріна, оскільки вам не потрібно вказувати дії вручну.
Нашенас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.