In an ASP.NET Core 2.0 application, I am trying to execute a global filter's OnActionExecuting
before executing the Controller's variant. Expected behaviour is that I can prepare something in the global before and pass along the result value to the controller(s). The current behaviour, however, is that the order of execution is reversed by design.
The docs tell me about the default order of execution:
Every controller that inherits from the Controller base class includes OnActionExecuting and OnActionExecuted methods. These methods wrap the filters that run for a given action: OnActionExecuting is called before any of the filters, and OnActionExecuted is called after all of the filters.
Which leads me to interpret that the Controller's OnActionExecuting
is executed before any of the filters. Makes sense. But the docs also states that the default order can be overridden by implementing IOrderedFilter
.
My attempt to implement this in a filter is like so:
public class FooActionFilter : IActionFilter, IOrderedFilter
{
// Setting the order to 0, using IOrderedFilter, to attempt executing
// this filter *before* the BaseController's OnActionExecuting.
public int Order => 0;
public void OnActionExecuting(ActionExecutingContext context)
{
// removed logic for brevity
var foo = "bar";
// Pass the extracted value back to the controller
context.RouteData.Values.Add("foo", foo);
}
}
This filter is registered at startup as:
services.AddMvc(options => options.Filters.Add(new FooActionFilter()));
Finally, my BaseController looks like the sample below. This best explains what I'm trying to achieve:
public class BaseController : Controller
{
public override void OnActionExecuting(ActionExecutingContext context)
{
// The problem: this gets executed *before* the global filter.
// I actually want the FooActionFilter to prepare this value for me.
var foo = context.RouteData.Values.GetValueOrDefault("foo").ToString();
}
}
Setting the Order
to 0, or even a non-zero value like -1, does not seem to have any effect on the order of execution.
My question: what can I do to make my global filter execute the OnActionExecuting
before the (Base)Controller's OnActionExecuting
?
You're almost there. Your small mistake is that default order of controller filter execution is not 0
. This order is defined in ControllerActionFilter
class as int.MinValue
(source code):
public class ControllerActionFilter : IAsyncActionFilter, IOrderedFilter
{
// Controller-filter methods run farthest from the action by default.
/// <inheritdoc />
public int Order { get; set; } = int.MinValue;
// ...
}
So the only change you should make to your current code is to set FooActionFilter.Order
to int.MinValue
:
public class FooActionFilter : IActionFilter, IOrderedFilter
{
public int Order => int.MinValue;
// ...
}
Now FooActionFilter
and ControllerActionFilter
have the same order. But FooActionFilter
is a global filter, while ControllerActionFilter
is Controller-level filter. That's why FooActionFilter
will be executed the first, based on this statement:
The Order property trumps scope when determining the order in which filters will run. Filters are sorted first by order, then scope is used to break ties.