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

Solution 1:

You can access the MethodInfo of the action through the ControllerActionDescriptor class:

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

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

Solution 2:

Invoking GetCustomAttributes on a method and/or class is slow(er). You should not invoke GetCustomAttributes every request since .net core 2.2, which @Henk Mollema is suggesting. (There is one exception which I will explain later)

Instead, on application startup time, the asp.net core framework will invoke GetCustomAttributes on the action method and controller for you and store the result in the EndPoint metadata.

You can then access this metadata in your asp.net core filters via the EndpointMetadata property of the ActionDescriptor class.

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

If you do not have access to the ActionDescriptor (for example: because you are operating from a Middleware instead of an filter) from asp.net core 3.0 you can use the GetEndpoint extension method to access it's Metadata. For more info see this github issue.

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

Like stated above, Endpoint Metadata contains the attributes for the action method and its defining controller class. This means that if you would want to explicitly IGNORE the attributes applied on either the controller class or the action method, you have to use GetCustomAttributes. This is almost never the case in asp.net core.