18

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.

5 Answers 5

21

Even better and more reliable* approach:

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

Though this might be MVC 3.0+ only.

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

3 Comments

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.
Using both together work very well in MVC 4 if (filterContext.Controller.GetType().GetCustomAttributes(typeof(SkipLocationFilterAttribute), true).Any()) { return; } if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(SkipLocationFilterAttribute), false).Any()) { return; }
@PvtVandals I was looking for somewhere to put a goldplated edition of yours (I independently derived), tada
17

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

4 Comments

For anyone who reads this, you can change AuthorizationContext to ActionExecutingContext to make these extension methods work with regular ActionFilterAttributes
replace => with return and make the function body :D
@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)
For anyone looking, click on edited and go to version 4 if you want a C#6 expression bodied members version ;)
11

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;

4 Comments

I need to know - what do these returns do? Why are you just returning?
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.
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.
Yes I noticed the MVC3+ answer after posting my comment, and it worked for me.
3

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

1 Comment

I'm wondering why there isn't a method to get all the custom attributes for an action, including those inherited from the controller.
2

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.

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.