Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute global filter before controller's OnActionExecuting, in ASP.NET Core

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?

like image 567
Juliën Avatar asked Apr 18 '18 19:04

Juliën


People also ask

Which filter will execute first in MVC?

as you can see from the below diagram, as soon as the controller starts execution through Action Invoker, Authentication and authorization filters are the very first filters to be triggered, followed by model binding which maps request and route data to action parameters.

How do I register a global filter in Web API?

To apply the filter to all Web API controllers, add it to GlobalConfiguration. Filters. public static class WebApiConfig { public static void Register(HttpConfiguration config) { config. Filters.

What is the order of the filters that get executed if multiple filters are implemented?

Filters get executed in the following order for an action: Globally Defined Filters -> Controller-specific Filters -> Action-specific Filters.

What is Action Filter in ASP NET 4x?

ASP. NET 4.x ASP.NET MVC provides Action Filters for executing filtering logic either before or after an action method is called. Action Filters are custom attributes that provide declarative means to add pre-action and post-action behavior to the controller's action methods.

How do I use the onactionexecuting Action Filter?

The OnActionExecuting action filter can be used to: Validate model state. Return an error if the state is invalid. Controllers annotated with the [ApiController] attribute automatically validate model state and return a 400 response. For more information, see Automatic HTTP 400 responses.

What is filters in ASP NET Core World?

In ASP.NET core world , a user request is routed to appropriate controller and then action method to be executed. In some cases, user may need to run some code before or after during execution pipeline. Filters allows some code to run before and after specific stage in request processing pipeline.

What is actionfilter actionexecutiondelegate in Java?

For example, ActionFilter ActionExecutionDelegate calls the action method and we can write the code before and after we call action method. We can implement interfaces for multiple filter types (stage) in single class. We can either implement synchronous or the async version of a filter interface, not both.


1 Answers

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.

like image 113
CodeFuller Avatar answered Oct 01 '22 03:10

CodeFuller