如何在 ASP.NET 核心 MVC 中读取操作方法的属性?

How to read action method's attributes in ASP.NET Core MVC?

基于 this article 我正在尝试为 ASP.NET 核心创建一个 IActionFilter 实现,它可以处理控制器上标记的属性和控制器的操作。虽然读取控制器的属性很容易,但我无法找到一种方法来读取定义在操作方法上的属性。

这是我现在拥有的代码:

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 中读取操作方法的属性?

您可以通过 ControllerActionDescriptor class:

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

MVC 5 ActionDescriptor class used to implement the ICustomAttributeProvider interface which gave access to the attributes. For some reason this was removed in the ASP.NET Core MVC ActionDescriptor class.

我根据 Henk Mollema 的解决方案创建了一个模仿原始 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>();
    }

希望对您有所帮助。

我的自定义属性继承自 ActionFilterAttribute。我把它放在我的控制器上,但有一个动作不需要它。我想使用 AllowAnonymous 属性来忽略它,但它不起作用。所以我在我的自定义属性中添加了这个片段以找到 AllowAnonymous 并跳过它。你可以在for循环中获取other。

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

Henk Mollena

的回答
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()

在方法 and/or class 上调用 GetCustomAttributesslow(er)。自 .net core 2.2 以来,您应该 而不是 调用 GetCustomAttributes 每个请求,@Henk Mollema 建议这样做。 (有一个例外,我稍后会解释)

相反,在应用程序启动时,asp.net 核心框架将为您调用 GetCustomAttributes 操作方法和控制器,并将结果存储在 EndPoint metadata.

然后您可以通过 EndpointMetadata property of the ActionDescriptor class 在您的 asp.net 核心过滤器中访问此元数据。

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 核心 3.0你可以使用GetEndpoint extension method to access it's Metadata。 有关详细信息,请参阅 this 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>();
    }
}

如上所述,端点元数据包含操作方法及其定义控制器的属性 class。这意味着如果您想要显式地忽略应用在控制器 class 或操作方法上的属性,您必须使用 GetCustomAttributes。在 asp.net core.

中几乎从来没有这种情况