So deaktivieren Sie einen globalen Filter in ASP.Net MVC selektiv

76

Ich habe einen globalen Filter für alle meine Controller-Aktionen eingerichtet, in denen ich NHibernate-Sitzungen öffne und schließe. 95% dieser Aktionen benötigen Datenbankzugriff, 5% jedoch nicht. Gibt es eine einfache Möglichkeit, diesen globalen Filter für diese 5% zu deaktivieren? Ich könnte umgekehrt vorgehen und nur die Aktionen dekorieren, die die Datenbank benötigen, aber das wäre weitaus mehr Arbeit.

zszep
quelle
1
Was ist mit einer weiteren Aktion und dekorieren Sie die 5% damit? So etwas wie NHibernateNotRequiredAttribute ()?
Dreza

Antworten:

149

Sie könnten ein Markerattribut schreiben:

public class SkipMyGlobalActionFilterAttribute : Attribute
{
}

und dann in Ihrem globalen Aktionsfilter auf das Vorhandensein dieses Markers in der Aktion testen:

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

Wenn Sie dann eine Aktion aus dem globalen Filter ausschließen möchten, dekorieren Sie sie einfach mit dem Marker-Attribut:

[SkipMyGlobalActionFilter]
public ActionResult Index()
{
    return View();
}
Darin Dimitrov
quelle
Wirklich tolle Lösung. Ich frage mich, warum ich das nicht alleine gedacht habe. Vielen Dank.
Zszep
5
Das war hilfreich, danke. Um zukünftigen Personen zu helfen, können Sie dasselbe auch auf einen Controller übertragen und filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes verwenden, um auf alle Aktionen anzuwenden.
Daryl Teo
4
Bei jeder API-Lösung ist die Syntax etwas anders: actionContext.ActionDescriptor.GetCustomAttributes <SkipMyGlobalActionFilter> () .Any ()
akd
Wenn Sie auch ein bestimmtes Controller-Attribut überspringen möchten, sollten Sie || hinzufügen in der Bedingung filterContext.ControllerContext.ControllerDescriptor.GetCustomAttributes <SkipMyGlobalActionFilter> () .Any ()
akd
3
Wie Leniel schrieb, verfügt filterContext.ActionDescriptor in ASP.NET Core nicht über die GetCustomAttributes-Methode. Wie geht das in ASP.NET Core?
Ashilon
19

Sie können auch das tun, was in diesem fantastischen Beitrag beschrieben wird:

Filter ausschließen

Implementieren Sie einfach eine benutzerdefinierte ExcludeFilterAttributeund dann eine benutzerdefinierte ExcludeFilterProvider.

Saubere Lösung und hat super für mich funktioniert!

Leniel Maccaferri
quelle
1
Eine ähnliche Lösung für Aspnet Core?
smg
@smg siehe die Antwort von @ gt unten
Ali Hasan
9

Die akzeptierte Antwort von Darin Dimitrov ist zwar in Ordnung und funktioniert gut, aber für mich ist die einfachste und effizienteste Antwort hier begründet .

Sie müssen Ihrem Attribut lediglich eine boolesche Eigenschaft hinzufügen und diese überprüfen, bevor Ihre Logik beginnt:

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

Verwenden Sie es dann bei Ihren 5% -Aktionen einfach so:

[DataAccessAttribute(Disable=true)]
public ActionResult Index()
{            
    return View();
}
Dr. MAF
quelle
Ein kleines "Gotcha" ist mir hier aufgefallen: Wenn Sie in der globalen Deklaration des Filters eine Reihenfolge angeben, müssen Sie auch dieselbe Reihenfolge für das Attribut angeben. ZB [DataAccessAttribute(Disable=true,Order=2)]undfilters.Add(new DataAccessAttribute(), 2);
Jon Story
7

In AspNetCore kann die akzeptierte Antwort von @ darin-dimitrov wie folgt angepasst werden:

Implementieren Sie zunächst IFilterMetadatadas Markerattribut:

public class SkipMyGlobalActionFilterAttribute : Attribute, IFilterMetadata
{
}

Suchen Sie dann in der FiltersEigenschaft nach diesem Attribut in ActionExecutingContext:

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

        // etc
    }
}
gt
quelle
2

Erstellen Sie einen benutzerdefinierten Filteranbieter. Schreiben Sie eine Klasse, die IFilterProvider implementiert. Diese IFilterProvider-Schnittstelle verfügt über eine Methode GetFilters, die Filter zurückgibt, die ausgeführt werden müssen.

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

================================================== ===========================
In 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);
}
Swapnil
quelle
2

Nun, ich glaube, ich habe es für ASP.NET Core zum Laufen gebracht.
Hier ist der Code:

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

Der relevante Code befindet sich in der Methode 'IsExcluded'.

Ashilon
quelle
2

Zumindest heutzutage ist dies recht einfach: Um alle Aktionsfilter von einer Aktion auszuschließen, fügen Sie einfach das OverrideActionFiltersAttribute hinzu .

Für andere Filter gibt es ähnliche Attribute: OverrideAuthenticationAttribute , OverrideAuthorizationAttribute und OverrideExceptionAttribute .

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

Markus Wagner
quelle
Heutzutage 2013+? Ich bin mir nicht sicher, warum dies nicht die akzeptierte Antwort ist, da es kanonisch erscheint.
Martin Capodici
1

Sie können Ihren Filtercode folgendermaßen ändern:

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

Und benutze es:

[NHibernateActionFilter(new[] { "SkipFilterAction1 ", "Action2"})]
Natalya
quelle
2
Das ist eine Möglichkeit, aber im Laufe der Zeit etwas schwierig zu pflegen.
Zszep
1
Wenn Sie Ihren Aktionsnamen ändern und vergessen, ihn in der Attributverwendung zu ändern, werden Sie vom Compiler nicht gewarnt. Dies kann sehr wahrscheinlich zu Wartungsproblemen führen. Ich bevorzuge Darins Antwort, da Sie die Aktionen nicht manuell angeben müssen.
Nashenas