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

基于 这篇文章,我试图为 ASP.NET Core 创建一个 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 中读取 action 方法的属性?

41353 次浏览

您可以通过 ControllerActionDescriptor类访问操作的 MethodInfo:

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

MVC 5 ActionDescriptor类用于实现 ICustomAttributeProvider接口,该接口提供对属性的访问。由于某种原因,这在 ASP.NET Core MVC ActionDescriptor类中被删除了。

我创建了一个扩展方法,模仿原来的 GetCustomAttributes基于 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>();
}

希望能有帮助。

我的自定义属性是从 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;
}
}
}
}

答案是 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);
}
}

此外,还可以使用 GetCustomAttritribute 函数的重载函数来获取特定的属性

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

对方法和/或类调用 GetCustomAttributes慢慢来(er)。之后的每个请求都应该调用 GetCustomAttributes。Net core 2.2,@Henk Mollema 建议。(有一个例外,我稍后会解释)

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

然后,您可以通过 ActionDescriptorEndpointMetadata财产在 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(例如: 因为您使用的是中间件而不是过滤器) ,那么您可以使用 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>();
}
}

如上所述,Endpoint Metadata 包含 action 方法及其定义控制器类的属性。这意味着,如果要显式忽略应用于控制器类或操作方法的属性,则必须使用 GetCustomAttributes。在 asp.net 核心中几乎从未出现过这种情况。