tags:

views:

324

answers:

2

Since asp.net mvc has changed a lot since November, does anyone have a solution to this question:

http://stackoverflow.com/questions/274039/resolve-filterattributes-on-controller-and-action

Phil said an ActionFilter on a controller is just shorthand for applying the attribute to all action methods of the controller, and it is true, if I put the same ActionFilter attribute on the controller and on an action method, it will run twice. But this doesn't seem like natural behavior since the compiler won't even let you put the same attribute directly on a method multiple times.

+3  A: 

A filter can take precedence over another filter by specifing the Order property on each filter. For example...

[MyFilter(Order=2)]
public class MyController : Controller
{
    [MyFilter(Order=1)]
    public ActionResult MyAction()
    {
     //...
    }
}

In this example the filter on the action method would execute before the filer on the controller.

HTH

robertz
Is there any way of an Action filter overriding a Controller filter so that a filter can be applied to all Actions exception for 1 specific Action>
David Liddle
Yes, there are ways for an Action filter to override a Controller filter. If you order the filters, then you could have your Action filter execute before your Controller filter and cancel the action. Cancelling an action can be performed by setting the ActionExecutingContext.Result to a non-null value. (Note: ActionExecutingContext.Cancel has been deprecated)
robertz
It seems that setting the Result of the ActionExecutingContext keeps all other OnActionExecuting events from firing, not just ones of the same ActionFilter
jayrdub
You're right Jayrdub. Setting the Result property will prevent all subsequent filters from firing, but I think it's the only asp.net mvc supported way to have overriding filters. Another way would be to insert some by-pass logic (based on the filterContext) into OnActionExecuting before your filtering code. BTW, did I answer your original question?
robertz
If that's the best that can be done in the framework, then you answered it, thanks.
jayrdub
A: 

I found one way to do it by "cheating" a bit with the ordering, inheritance and the AttributeUsage parameter

First, define your ActionFilter for the controller

[AttributeUsage(AttributeTargets.Class)]
public class FilterController : ActionFilterAttribute
{
    public FilterController()
    {
        this.Order = 2;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (!filterContext.HttpContext.Items.Contains("WeAlreadyWentThroughThis"))
        {
                // do our thing
            filterContext.HttpContext.Items.Add("WeAlreadyWentThroughThis", "yep");
            base.OnActionExecuted(filterContext);
        }
    }
}

Then inherit the class for your action attribute

[AttributeUsage(AttributeTargets.Method)]
public class FilterAction : FilterController
{
    public FilterAction()
    {
        this.Order = 1;
    }
}

It's far from perfect since you have to rely on HttpContext and two classes (though you could use namespaces to name both classes the same). But you get compiler-enforced check of the attribute scope for class or action and you won't forget an order parameter when typing the code.

samy