Get list of custom attributes for current action/controller in ASP.NET MVC

24,163

Solution 1

This seems to work... is there a better / more proper way in ASP.NET MVC 1?

if (filterContext.Controller.GetType().GetCustomAttributes(typeof(RequireHttpsAttribute), true).Length > 0)
    return;
string action = (string)filterContext.RouteData.Values["action"];
if (!string.IsNullOrEmpty(action) && filterContext.Controller.GetType().GetMethod(action).GetCustomAttributes(typeof(RequireHttpsAttribute), true).Length > 0)
    return;

Solution 2

Even better and more reliable* approach:

filterContext.ActionDescriptor.GetCustomAttributes(
    typeof(RequireHttpsAttribute), true).Count> 0

Though this might be MVC 3.0+ only.

Solution 3

Goldplated edition, works on MVC5, probably 4/3:

filterContext.HasMarkerAttribute<RequireHttpsAttribute>()

Uses this set of helper extensions:

public static class MarkerAttributeExtensions
{
    public static bool HasMarkerAttribute<T>(this AuthorizationContext that) {
        return that.Controller.HasMarkerAttribute<T>()
            || that.ActionDescriptor.HasMarkerAttribute<T>();
    }

    public static bool HasMarkerAttribute<T>(this ActionExecutingContext that) {
        return that.Controller.HasMarkerAttribute<T>()
            || that.ActionDescriptor.HasMarkerAttribute<T>();
    }

    public static bool HasMarkerAttribute<T>(this ControllerBase that) {
        return that.GetType().HasMarkerAttribute<T>();
    }

    public static bool HasMarkerAttribute<T>(this Type that) {
        return that.IsDefined(typeof(T), false);
    }

    public static IEnumerable<T> GetCustomAttributes<T>(this Type that) {
        return that.GetCustomAttributes(typeof(T), false).Cast<T>();
    }

    public static bool HasMarkerAttribute<T>(this ActionDescriptor that) {
        return that.IsDefined(typeof(T), false);
    }

    public static IEnumerable<T> GetCustomAttributes<T>(this ActionDescriptor that) {
        return that.GetCustomAttributes(typeof(T), false).Cast<T>();
    }
}

Solution 4

this worked for me in .NET Core 2.2:

var controllerActionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;

if (controllerActionDescriptor != null)
{
    // Check if the attribute exists on the action method
    if (controllerActionDescriptor.MethodInfo?.GetCustomAttributes(inherit: true)?.Any(a => a.GetType().Equals(typeof(CustomAttribute))) ?? false)
        return true;

    // Check if the attribute exists on the controller
    if (controllerActionDescriptor.ControllerTypeInfo?.GetCustomAttributes(typeof(CustomAttribute), true)?.Any() ?? false)
        return true;
}

Solution 5

I'm using MVC5 and had to use the following to check from within a class that inherits from ActionFilterAttribute and implements IAuthenticationFilter.

If filterContext.ActionDescriptor.GetCustomAttributes(GetType(RequireHttpsAttribute), True).Any() OrElse filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(GetType(RequireHttpsAttribute), True).Any() Then
' .. the given attribute is present ..
End If

I couldn't get Ruben's solution to work for me but it was probably because I messed up in the conversion from C# to VB.

Share:
24,163

Related videos on Youtube

nuthan ratnam vara
Author by

nuthan ratnam vara

Web programmer with a background in usability.

Updated on July 09, 2022

Comments

  • nuthan ratnam vara
    nuthan ratnam vara almost 2 years

    Checking out the sample code from http://lukesampson.com/post/471548689/entering-and-exiting-https-with-asp-net-mvc written for ASP.NET MVC2, I noticed they can check if a custom attribute is applied to the current action or controller by accessing filterContext.ActionDescriptor and filterContext.ActionDescriptor.ControllerDescriptor respectively:

    public class ExitHttpsIfNotRequiredAttribute : FilterAttribute, IAuthorizationFilter {
        public void OnAuthorization(AuthorizationContext filterContext) {
            // snip
    
            // abort if a [RequireHttps] attribute is applied to controller or action
            if(filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Length > 0) return;
            if(filterContext.ActionDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Length > 0) return;
    
            // snip
        }
    }
    

    What would be the ASP.NET MVC 1 method of checking the action and controller for a custom attribute? In ASP.NET MVC 1 there is no filterContext.ActionDescriptor that I can tell.

  • David Omid
    David Omid almost 9 years
    This doesn't seem to do the same thing. I tried this with an attribute on the controller and this returned false. Using the code from the original question works fine though.
  • muttley91
    muttley91 over 8 years
    I need to know - what do these returns do? Why are you just returning?
  • nuthan ratnam vara
    nuthan ratnam vara over 8 years
    You can see in the original code in the question that this snippet is inside of an attribute's "public void OnAuthorization" method. If the RequireHttpsAttribute is on the current controller or action, then the best thing to do is the 'return' out of the method and continuing on like nothing happened. But if RequireHttps is NOT on the controller or action then it will execute some code, in this case probably redirecting the browser to a non-HTTPS protocol.
  • nuthan ratnam vara
    nuthan ratnam vara over 8 years
    Note that this snippet was for ASP.NET MVC 1, and maybe MVC 2. Since MVC 3, there have been better ways to deal with the issue of checking for the existence of controller and action filters as Sunday Ironfoot pointed out.
  • PvtVandals
    PvtVandals over 8 years
    Using both together work very well in MVC 4 if (filterContext.Controller.GetType().GetCustomAttributes(type‌​of(SkipLocationFilte‌​rAttribute), true).Any()) { return; } if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(S‌​kipLocationFilterAtt‌​ribute), false).Any()) { return; }
  • muttley91
    muttley91 over 8 years
    Yes I noticed the MVC3+ answer after posting my comment, and it worked for me.
  • Raymond
    Raymond over 8 years
    @PvtVandals I was looking for somewhere to put a goldplated edition of yours (I independently derived), tada
  • agrath
    agrath almost 8 years
    Thank you for posting this!
  • elexis
    elexis almost 8 years
    For anyone who reads this, you can change AuthorizationContext to ActionExecutingContext to make these extension methods work with regular ActionFilterAttributes
  • Kasparov92
    Kasparov92 almost 8 years
    replace => with return and make the function body :D
  • Raymond
    Raymond almost 8 years
    @Kasparov92 :( Its valid C#6 and is way harder to read now IMO (I had it the long winded way before). Tidying... (Yes, the Java bracing is extremely ugly - here's hoping people move to C#6 [and acceptance of expression bodied members] ASAP)
  • Raymond
    Raymond almost 7 years
    For anyone looking, click on edited and go to version 4 if you want a C#6 expression bodied members version ;)
  • Johan Maes
    Johan Maes almost 4 years
    I'm wondering why there isn't a method to get all the custom attributes for an action, including those inherited from the controller.