Hoe lees ik de kenmerken van een actiemethode in ASP.NET Core MVC?

Op basis van dit artikelprobeer ik om een ​​IActionFilter-implementatie voor ASP.NET Core te maken die attributen kan verwerken die zijn gemarkeerd op de controller en de actie van de controller. Hoewel het gemakkelijk is om de attributen van de controller te lezen, kan ik geen manier vinden om de attributen te lezen die zijn gedefinieerd in de actiemethode.

Dit is de code die ik nu heb:

public sealed class ActionFilterDispatcher : IActionFilter
{
    private readonly Func<Type, IEnumerable> container;
    public ActionFilterDispatcher(Func<Type, IEnumerable> container)
    {
        this.container = container;
    }
    public void OnActionExecuting(ActionExecutingContext context)
    {
        var attributes = context.Controller.GetType().GetCustomAttributes(true);
        attributes = attributes.Append(/* how to read attributes from action method? */);
        foreach (var attribute in attributes)
        {
            Type filterType = typeof(IActionFilter<>).MakeGenericType(attribute.GetType());
            IEnumerable filters = this.container.Invoke(filterType);
            foreach (dynamic actionFilter in filters)
            {
                actionFilter.OnActionExecuting((dynamic)attribute, context);
            }
        }
    }
    public void OnActionExecuted(ActionExecutedContext context)
    {
        throw new NotImplementedException();
    }
}

Mijn vraag is: hoe lees ik de attributen van de actiemethode in ASP.NET Core MVC?


Antwoord 1, autoriteit 100%

Je hebt toegang tot de MethodInfovan de actie via de ControllerActionDescriptorklasse:

public void OnActionExecuting(ActionExecutingContext context)
{
    if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
    {
        var actionAttributes = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true);
    }
}

De MVC 5 ActionDescriptorklasse gebruikt om de ICustomAttributeProviderinterface te implementeren die toegang gaf tot de attributen. Om de een of andere reden is dit verwijderd in de ASP.NET Core MVC ActionDescriptorklasse.


Antwoord 2, autoriteit 32%

Het aanroepen van GetCustomAttributesop een methode en/of klasse is traag(er). Je moet nietelk verzoek GetCustomAttributesaanroepen sinds .net core 2.2, wat @Henk Mollema suggereert. (Er is één uitzondering die ik later zal uitleggen)

In plaats daarvan zal het asp.net core-framework tijdens het opstarten van de applicatie GetCustomAttributesaanroepen voor de actiemethode en controller voor u en het resultaat opslaan in de EndPointmetagegevens.

Je hebt dan toegang tot deze metadata in je asp.net-kernfilters via de EndpointMetadata-eigenschapvan de ActionDescriptorklasse.

public class CustomFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Get attributes on the executing action method and it's defining controller class
        var attributes = context.ActionDescriptor.EndpointMetadata.OfType<MyCustomAttribute>();
    }
    public void OnActionExecuted(ActionExecutedContext context)
    {
    }
}

Als u geen toegang heeft tot de ActionDescriptor(bijvoorbeeld: omdat u werkt vanuit een middleware in plaats van een filter) van asp.net core 3.0kunt u gebruik de GetEndpoint-extensiemethodeom toegang te krijgen tot zijn Metadata.
Zie ditgithub-probleem voor meer informatie.

public class CustomMiddleware
{
    private readonly RequestDelegate next;
    public CustomMiddleware(RequestDelegate next)
    {
        this.next = next;
    }
    public async Task Invoke(HttpContext context)
    {
        // Get the enpoint which is executing (asp.net core 3.0 only)
        var executingEnpoint = context.GetEndpoint();
        // Get attributes on the executing action method and it's defining controller class
        var attributes = executingEnpoint.Metadata.OfType<MyCustomAttribute>();
        await next(context);
        // Get the enpoint which was executed (asp.net core 2.2 possible after call to await next(context))
        var executingEnpoint2 = context.GetEndpoint();
        // Get attributes on the executing action method and it's defining controller class
        var attributes2 = executingEnpoint.Metadata.OfType<MyCustomAttribute>();
    }
}

Zoals hierboven vermeld, bevatten Endpoint Metadata de attributen voor de actiemethode en de bepalende controllerklasse. Dit betekent dat als je expliciet de attributen wilt NEGEREN die zijn toegepast op de controllerklasse of de actiemethode, je GetCustomAttributesmoet gebruiken. Dit is bijna nooit het geval in asp.net core.


Antwoord 3, autoriteit 9%

Mijn aangepaste kenmerk is overgenomen van ActionFilterAttribute. Ik heb het op mijn controller gezet, maar er is een actie die het niet nodig heeft. Ik wil het kenmerk AllowAnonymousgebruiken om dat te negeren, maar het werkt niet. Dus ik voeg dit fragment toe aan mijn aangepaste kenmerk om de AllowAnonymouste vinden en sla het over. Je kunt anderen in de for-lus krijgen.

   public class PermissionAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            foreach (var filterDescriptors in context.ActionDescriptor.FilterDescriptors)
            {
                if (filterDescriptors.Filter.GetType() == typeof(AllowAnonymousFilter))
                {
                    return;
                }
            }
        }
    }

Antwoord 4, autoriteit 8%

Ik heb een extensiemethode gemaakt die de originele GetCustomAttributesnabootst op basis van de oplossing van Henk Mollema.

   public static IEnumerable<T> GetCustomAttributes<T>(this Microsoft.AspNet.Mvc.Abstractions.ActionDescriptor actionDescriptor) where T : Attribute
    {
        var controllerActionDescriptor = actionDescriptor as ControllerActionDescriptor;
        if (controllerActionDescriptor != null)
        {
            return controllerActionDescriptor.MethodInfo.GetCustomAttributes<T>();
        }
        return Enumerable.Empty<T>();
    }

Hopelijk helpt het.


Antwoord 5

Als beantwoord door Henk Mollena

public void OnActionExecuting(ActionExecutingContext context)
{
    var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
    if (controllerActionDescriptor != null)
    {
        var controllerAttributes = controllerActionDescriptor
                               .MethodInfo
                               .GetCustomAttributes(inherit: true);
    }
}

is de juiste manier als u de aanwezigheid wilt controleren van een kenmerk toegepast op een actie.

Ik wil alleen iets toevoegen aan zijn antwoord voor het geval je de aanwezigheid wilt controleren van een attribuut toegepast op de controller

public void OnActionExecuting(ActionExecutingContext context)
{
    var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
    if (controllerActionDescriptor != null)
    {
        var actionAttributes = controllerActionDescriptor.ControllerTypeInfo.GetCustomAttributes(inherit: true);
    }
}

U kunt ook de overbelaste functie van de GetCustomAttributes-functies gebruiken om uw specifieke kenmerk(en) te krijgen

var specificAttribute = GetCustomAttributes(typeof(YourSpecificAttribute), true).FirstOrDefault()

Other episodes