2

I'm implementing custom authentication filter and using "Passive Attributes" approach described here: http://blog.ploeh.dk/2014/06/13/passive-attributes/

DI works as expected but I can't figure out how to read custom attributes from controller itself? I would like both individual actions and whole controllers to support this functionality.

Example of controller:

[TokenAuth] // This attribute not "visible"
public class SupportController : ApiController
{
    private ISecurityService SecurityService { get; }

    public SupportController(ISecurityService securityService)
    {
        this.SecurityService = securityService;
    }

    [TokenAuth] // This attribute works
    [HttpGet]
    public object StartupData()
    {
        return "Startup data";
    }
}

This is portion of filter code where I read custom attribute:

public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
    var tokenAuthAttribute = actionContext.ActionDescriptor.GetCustomAttributes<TokenAuthAttribute>(true).SingleOrDefault();

    // This line below exists unless attribute placed on method/action

    if (tokenAuthAttribute == null) return continuation();

    var req = actionContext.Request;

Is there any way to access controller's attributes?

2
  • 1
    doesn't the action descriptor give you access to the controller type via the ControllerDescriptor property? Commented Nov 28, 2018 at 0:53
  • Yes, I see it. Do you want to write an answer so I can accept it? Commented Nov 28, 2018 at 0:57

2 Answers 2

3

The action descriptor should give you access to the controller type via the ControllerDescriptor property

var actionDescriptor = actionContext.ActionDescriptor;

var tokenAuthAttribute = 
    actionDescriptor.GetCustomAttributes<TokenAuthAttribute>(true).SingleOrDefault() ??
    actionDescriptor.ControllerDescriptor.GetCustomAttributes<TokenAuthAttribute>(true).SingleOrDefault();

//...

The above checks the action descriptor first and if nothing is found then checks the controller descriptor.

Sign up to request clarification or add additional context in comments.

Comments

1

Here's a complete abstract class with the attribute as a generic argument. You can inherit from it, specifying your attribute, and then just override ExecuteFilterBehavior. This will check for the attribute on the controller and the method. You can modify it to only do one or the other.

public abstract class ActionFilterBehaviorBase<TAttribute> : IActionFilter where TAttribute : Attribute
{
    public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        if (ControllerHasAttribute(actionContext) || ActionHasAttribute(actionContext))
        {
            return ExecuteFilterBehavior(actionContext, cancellationToken, continuation);
        }
        return continuation();
    }

    protected abstract Task<HttpResponseMessage>  ExecuteFilterBehavior(HttpActionContext actionContext, CancellationToken cancellationToken,
        Func<Task<HttpResponseMessage>> continuation);

    public virtual bool AllowMultiple { get; } = false;

    private bool ControllerHasAttribute(HttpActionContext actionContext)
    {
        return actionContext
                .ControllerContext.Controller.GetType()
                .GetCustomAttributes(false)
                .Any(attribute => attribute.GetType().IsAssignableFrom(typeof(TAttribute)));
    }

    private bool ActionHasAttribute(HttpActionContext actionContext)
    {
        return actionContext
            .ActionDescriptor
            .GetCustomAttributes<TAttribute>()
            .Any();
    }
}

Some more detail here.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.