Как прочитать атрибуты метода действия в ASP.NET Core MVC?

На основании этой статьи я пытаюсь создать IActionFilter реализация для ASP.NET Core, которая может обрабатывать атрибуты, отмеченные на контроллере, и действия контроллера. Хотя чтение атрибутов контроллера легко, я не могу найти способ прочитать атрибуты, определенные в методе действия.

Вот код, который я сейчас имею:

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

Мой вопрос: как мне прочитать атрибуты метода действия в ASP.NET Core MVC?

3 ответа

Решение

Вы можете получить доступ к MethodInfo действия через ControllerActionDescriptorучебный класс:

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

MVC 5 ActionDescriptor класс, используемый для реализации ICustomAttributeProvider Интерфейс, который дал доступ к атрибутам. По какой-то причине это было удалено в ASP.NET Core MVC ActionDescriptor учебный класс.

Вызов GetCustomAttributesв методе и / или классе работает медленно(эр). Вы не должны вызыватьGetCustomAttributesкаждый запрос, начиная с ядра.net 2.2, который предлагает @Henk Mollema. (Есть одно исключение, которое я объясню позже)

Вместо этого во время запуска приложения основная структура asp.net будет вызывать GetCustomAttributes для метода действия и контроллера и сохраните результат в EndPoint метаданные.

Затем вы можете получить доступ к этим метаданным в основных фильтрах asp.net через EndpointMetadataСвойство из ActionDescriptorкласс.

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

Если у вас нет доступа к ActionDescriptor(например: поскольку вы работаете с промежуточным программным обеспечением вместо фильтра) из asp.net core 3.0 вы можете использовать GetEndpointметод расширения для доступа к нему Metadata. Для получения дополнительной информации см. Эту проблему с github.

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

Как указано выше, метаданные конечной точки содержат атрибуты для метода действия и определяющего его класса контроллера. Это означает, что если вы хотите явно ИГНОРИРОВАТЬ атрибуты, применяемые либо к классу контроллера, либо к методу действия, вы должны использоватьGetCustomAttributes. В ядре asp.net этого почти никогда не бывает.

Мой пользовательский атрибут унаследован от ActionFilterAttribute. Я положил его на свой контроллер, но есть одно действие не нужно. Я хочу использовать AllowAnonymous атрибут игнорировать это, но это не работает. Поэтому я добавляю этот фрагмент в свой пользовательский атрибут, чтобы найти AllowAnonymous и пропустить это. Вы можете получить другие в цикле for.

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

Я создал метод расширения, который имитирует оригинал GetCustomAttributes основанный на решении Хенка Моллема.

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

Надеюсь, поможет.

Как ответил Henk Mollema

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

является правильным способом, если вы хотите проверить наличие атрибута, примененного к действию.

Я просто хочу добавить к его ответу в случае, если вы хотите проверить наличие атрибута, примененного к контроллеру

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

Также вы можете использовать перегруженную функцию функций GetCustomAttributes, чтобы получить ваши конкретные атрибуты

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

Другие вопросы по тегам