Filters in Dotnet core

Introduction

Filters are essential concepts of ASP.NET Core. Although Filters were present in Legacy ASP.Net Framework, middleware was introduced in ASP.Net Core. In Legacy ASP.NET Framework we had HttpModules and HttpHandlers which were part of the request pipeline but in ASP.NET Core we have middleware which is part of the request pipeline. Both Filters and Middlewares have some common usage like logging the request or response, authenticating the request and error handling for all the requests, etc...

In this article, we will know about filters only and check Middleware in a different article.

Filters

Filters in .Net Framework are commonly used in the MVC context and are used in both Legacy .Net Framework as well as ASP.NET Core as well. It helps us to execute common business logic before and after Action methods execute, Handle exceptions globally across the application and Authorizing the requests, etc...

Following are the different types of Filters used in ASP.NET Core

  1. Authorization Filter:​ This filter executes first which checks whether the user is authorized for the request and returns HTTP Status code 401 if the user is unauthorized for the request.
  2. Resource Filters: This Filter executes after the Authorization Filter and is generally used for caching implementations. OnResourceExecuting and OnResourceExecuted are the two methods for Resource Filter where the former hits before model binding whereas later executes code after the rest of the pipeline is completed.
  3. Action Filters: Used for executing some logic before and after Action Method executes in ASP.Net Core. The synchronous version contains two important methods namely OnActionExecuting and OnActionExecuted where we can execute some business logic before and after the request hit the Action method whereas the asynchronous version provides a single method named OnActionExecutionAsync for executing logic before and after the action method. Common usage of this Action Filter is Logging the request and response, Validating the incoming request for some specific token in the request header, etc...
  4. Exception Filter: This Filter is used to handle exceptions in the application globally. This filter is useful when we don't want the try/catch block in individual methods and want to handle the exceptions globally. It can also be used in ASP.NET Core Web Api where we want to handle exceptions globally and don't want to show the server-side exception details in UI and instead send customized error messages to UI. Check this link Global Exception Filter to find an example of the same.
  5. Result Filter: this Filter executes last in the request pipeline and has two important methods namely OnResultExecuting and OnResultExecuted  which are executed before and after the result from the action method is processed.    

Please find the diagram below which illustrates how requests and response pass from the different types of filters with the order specified.


filter_execution_in_dotnet_core.png

Razor pages have Razor page filters that execute before and after the Razor page handler.

Let's check some code examples of Action Filter which is the most commonly used of all the filters mentioned above.
Suppose we want to validate an incoming request by checking whether the token exists in the header and also check whether the token which exists in the header is valid or not.

public class ActionFilterExample : IActionFilter
{
    private readonly IHttpContextAccessor _httpContextAccessor;


    public ActionFilterExample(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        var token = _httpContextAccessor.HttpContext.Request.Headers["token"].ToString();
        if (string.IsNullOrEmpty(token))
        {
            context.HttpContext.Response.StatusCode = 401; // BadRequest
            context.HttpContext.Response.WriteAsync("Token Missing");
            return;
        }


        if(!ValidateToken(token))
        {
            context.HttpContext.Response.StatusCode = 401; // BadRequest
            context.HttpContext.Response.WriteAsync("Token Missing");
            return;
        }
    }


    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        throw new System.NotImplementedException();
    }
}


The same token can be validated in Asynchronous Filter as well like below


public class AsyncActionFilterExample : IAsyncActionFilter
{
    private readonly IHttpContextAccessor _httpContextAccessor;


    public AsyncActionFilterExample(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
    }


    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Do something before the action executes.


        var token = _httpContextAccessor.HttpContext.Request.Headers["token"].ToString();
        if (string.IsNullOrEmpty(token))
        {
            context.HttpContext.Response.StatusCode = 401; // BadRequest
            await context.HttpContext.Response.WriteAsync("Token Missing");
            return;
        }


        if (!ValidateToken(token))
        {
            context.HttpContext.Response.StatusCode = 401; // BadRequest
            await context.HttpContext.Response.WriteAsync("Token Missing");
            return;
        }


        // next() calls the action method.
        var resultContext = await next();
        // resultContext.Result is set.
        // Do something after the action executes.
    }
}

As you can see in the asynchronous version we have one method and are using ActionExecutionDelegate next, ​which runs the code of Action Methods.

To use the Filter in our application we need to register the filter in the ConfigureServices ​method in ASP.NET Core like below


public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddScoped<ActionFilterExample>();
}

Registering your filter in IoC Container is important otherwise we will get InvalidOperationException.

Now we can use the above Filter either at the entire controller level or individual Action method level.

Controller Level


[Route("api/[controller]")]

[ApiController]
[TypeFilter(typeof(ActionFilterExample))]
public class HomeController: ControllerBase
{
}


Action Method Level

[Route("api/[controller]")]
[ApiController]
public class HomeController: ControllerBase
{
    [TypeFilter(typeof(ActionFilterExample))]
    public ActionResult Get(int id)
    {
        return Ok();
    }


    public ActionResult Post(int id)
    {
        return Ok();
    }
}


Please note we cannot apply both synchronous and asynchronous versions of the same filter in an application.

Let's look into another example where we want to add a header to the response using the ResultFilter attribute.

public class ResultFilterExample : ActionFilterAttribute
{


    private readonly string _name;
    private readonly string _value;


    public ResultFilterExample(string name, string value)
    {
        _name = name;
        _value = value;
    }


    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, new string[] { _value });
        base.OnResultExecuting(context);
    }
}


The above Filter can be used in the Controller like below

[Route("api/[controller]")]
[ApiController]
public class HomeController: ControllerBase
{


    [ResultFilterExample("Filter-Header", "Filter Value")]
    public IActionResult Index() =>
   Content("Examine the response headers using the F12 developer tools.");
}



Filter Default Order of Execution

A filter can be placed at the global level, Controller level, and Action Method level as well, and often it may happen we use multiple filters at multiple levels so the filter runs in the below-mentioned sequence.

The filter sequence is mentioned in the Official Microsoft Link:

  • The before code of global filters.
    • The before code of controller filters.
      • The before code of action method filters.
      • The after code of action method filters.
    • The after code of controller filters.
  • The after code of global filters.
The following example from mentioned Link illustrates the order in which filter methods run for synchronous action filters:

SequenceFilter scopeFilter method
1GlobalOnActionExecuting
2ControllerOnActionExecuting
3ActionOnActionExecuting
4ActionOnActionExecuted
5ControllerOnActionExecuted
6GlobalOnActionExecuted

Conclusion

The filter is important in the request pipeline as it allows us to hook different kinds of logic in a request life cycle like logging the request, validating the request, handling exceptions using the filter, etc... It is used in Classic ASP.Net Framework as well as ASP.Net Core. It can be used in projects types of both ASP.NET Core MVC and ASP.NET Core WebApi



Share This Post

Linkedin
Fb Share
Twitter Share
Reddit Share

Support Me

Buy Me A Coffee