Filters in ASP.NET Core are used to run code before or after certain stages in the request processing pipeline. There are many built-in filters for authorization, logging, caching, exception handling and so on. Filters also help to avoid code-duplication across action methods.
Page Contents
You can also create custom filters to handle concerns for your application in the way you want.
I will create an example project to understand Filters, so use ASP.NET Core Web Application (.NET Core) template to create a new Empty Project and name it as Filters. Remember to select the framework as .NET Core and version as ASP.NET Core 3.1.
Next, go to the Startup.cs file and enable the MVC framework and other Middlewares required for the project, see the below highlighted code.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Filters { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } } }
Bootstrap is a CSS framework directed at responsive, mobile-first front-end web development. I will use it to create a nice looking UI. So install the Bootstrap package in your project. You can check How to Install Bootstrap Package in ASP.NET Core Application in Visual Studio
In the root folder of your project, create a new folder called Views. Then add Razor View Imports file inside the Views folder to include the built-in tag helpers for the Views. The code which you have to add to this file is shown below.
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
The [RequireHttps] attribute is a built-in filter which on applying to an action method restricts only HTTPS request to invoke it.
In order to understand the working of the RequireHttps attribute I need to Enable SSL for the project. To enable SSL, right click on the project name in the Solution Explorer and select Properties.
A new window will open, here click on the Debug tab. Under Web Server Setting, un-check the checkbox that says Enable SSL and click the Ctrl+S to save the changes.
The SSL URL of the project will be different from the non-SSL URL.
See the below image:
Next, create a folder called Controllers in the root of the project, and add to it a new empty Controller called HomeController.cs.
Now change the Index Action method to return a string as shown below:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; namespace Filters.Controllers { public class HomeController : Controller { public string Index() { return "This is the Index action on the Home controller"; } } }
Now run your application and you will see the returned string from this action method gets displayed on the browser, see the below image:
To make this action method to be accessed only with HTTPS request I can add the [RequireHttps] attribute to it as shown below:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; namespace Filters.Controllers { public class HomeController : Controller { [RequireHttps] public string Index() { return "This is the Index action on the Home controller"; } } }
Now when you run your application you will see a mostly white screen with a message – This site can’t be reached, as shown in the image given below:
This is because you are trying to invoke this action method with a Non-HTTPS request.
Behind the scene the [RequireHttps] attribute is actually redirecting the non-https URL to the Https based ULR. So a request to the http://localhost:50523/Home/Index is redirected to https://localhost:50523/Home/Index, the change in these 2 URLs is only the ‘https’ while the port is the same.
Since in development the http and https are on different port therefore you see the message This site can’t be reached. However on the live site, where the site URL does not contains a port, it will work properly because if the requested URL is http://www.yogihosting.com then you will be redirected to https://www.yogihosting.com.
The [RequireHttps] built-in filter can also be put on the controller class name which makes it to be applied to every action method contained by the controller.
Example:
[RequireHttps] public class HomeController : Controller { public string Index() { return "This is the Index action"; } public string List() { return "This is the List action"; } public string Hello() { return "This is the Hello action"; } }
Filters implement the IFilterMetadata interface of the Microsoft.AspNetCore.Mvc.Filters namespace.
namespace Microsoft.AspNetCore.Mvc.Filters {
public interface IFilterMetadata { }
}
There are many different types of filters, important ones are defined in the below table along with the interface that contains them.
Filter | Interfaces that contains them | Description |
---|---|---|
Authorization | IAuthorizationFilter, IAsyncAuthorizationFilter | Used to apply authorization and security policy |
Action | IActionFilter, IAsyncActionFilter | Used to perform a specify work immediately before or after an action method is performed |
Result | IResultFilter, IAsyncResultFilter | Used to perform a specify work immediately before or after the result from an action method is processed |
Exception | IExceptionFilter, IAsyncExceptionFilter | Used to handle exceptions |
Notice that each of the filter has 2 interfaces (Synchronous & Asynchronous) as filters can work both in synchronous and asynchronous manner.
Filters are executed in the following order:
The Exception Filters are executed only when an exception occurs.
The Authorization Filters are used for authorization purpose and creating security policy. These filters execute before any other filter and even before the action method is executed.
Authorization Filters are contained either by IAuthorizationFilter or IAsyncAuthorizationFilter interface.
Definition of IAuthorizationFilter interface:
namespace Microsoft.AspNetCore.Mvc.Filters { public interface IAuthorizationFilter : IFilterMetadata { void OnAuthorization(AuthorizationFilterContext context); } }
Definition of IAsyncAuthorizationFilter interface:
using System.Threading.Tasks; namespace Microsoft.AspNetCore.Mvc.Filters { public interface IAsyncAuthorizationFilter : IFilterMetadata { Task OnAuthorizationAsync(AuthorizationFilterContext context); } }
The IAsyncAuthorizationFilter interface is used for creating Asynchronous Authorization Filters.
The methods called OnAuthorization() and OnAuthorizationAsync() are used to write code so that the filter can authorize incoming requests.
The parameter called AuthorizationFilterContext context, receives the context data describing the request. The object of type AuthorizationFilterContext contains a property called Result (of type IActionResult) which is set by the authorization filter.
If the Result property is set then ASP.NET Core renders the IActionResult instead of invoking the action method.
I will now create an Authorization Filter which can be applied to an action method or to the Controller class itself. This filter will restrict the requests that are not made with Https protocol.
I would add all my custom filters including this one in a folder called CustomFilters. So create this folder in the root of the project and add a new class called HttpsOnly.cs to it. Next add the following code to this class:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Filters.CustomFilters { public class HttpsOnly : Attribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { if (!context.HttpContext.Request.IsHttps) context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden); } } }
Since the HttpsOnly class will be applied as an attribute, to an action method or the controller, therefore it is derived from the Attribute class. Also, since this class will be used to create an Authorization Filter therefore it is derived from the IAuthorizationFilter interface.
The main work is done inside the OnAuthorization method which checks if the request is made from an Https Protocol or not.
If the request is not made from Https then I am setting the Result property of the AuthorizationFilterContext object to 403 forbidden. This prevents further execution from happening and provides ASP.NET Core with a result to return to the client.
Now apply this HttpsOnly filter to the Home Controller’s Index action method:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Filters.CustomFilters; using Microsoft.AspNetCore.Mvc; namespace Filters.Controllers { public class HomeController : Controller { [HttpsOnly] public string Index() { return "This is the Index action on the Home controller"; } } }
Now run your application and you will see the message Status Code: 403; Forbidden on the browser. The reason being the request coming from a non-https URL.
This is shown by the image below:
If you open the HTTPS version of the URL in the browser then the Authorization filter will find the request is made with Https protocol. So the Result property of the AuthorizationFilterContext object is not set and the Index Action method is executed.
The below image shows the result from the Index Action gets displayed when the request is made from HTTPS protocol:
This example very well explains the working of an Authorization filter. Next, I will create an Action Filter.
The Action Filters are executed after the Authorization Filters. They are called just before and just after an Action method is called.
They are derived either from the IActionFilter or asynchronous IAsyncActionFilter interface.
Definition of IActionFilter interface:
namespace Microsoft.AspNetCore.Mvc.Filters { public interface IActionFilter : IFilterMetadata { void OnActionExecuting(ActionExecutingContext context); void OnActionExecuted(ActionExecutedContext context); } }
On applying an Action Filter to an Action method, the OnActionExecuting method is called just before the action method is invoked, and the OnActionExecuted method is called just after the action method has finished executing.
The OnActionExecuting method has a parameter of ActionExecutingContext class type. The important properties of the ActionExecutingContext class are:
Name | Description |
---|---|
Controller | The name of the controller whose action method is about to be invoked. |
Result | This property is of type IActionResult. If this property is set a value of type IActionResult then MVC renders the IActionResult instead of invoking the action method. |
The OnActionExecuted method has a parameter of ActionExecutedContext class type. The important properties of the ActionExecutedContext class are:
Name | Description |
---|---|
Controller | The name of the controller whose action method was invoked. |
Exception | This property contains the Exception that occurred in the Action method. |
ExceptionHandled | When you set it property to true then the exceptions will not be propagated further. |
Result | This property returns the IActionResult returned by the action method, and you can change or replace it if you have a need. |
I will now create an Action filter that will measure the number of milliseconds an action method takes to execute. For this I will start a timer in the OnActionExecuting method and stop it in the OnActionExecuted method.
So create a class called TimeElapsed.cs inside the CustomFilters folder and add the following code to it:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Filters.CustomFilters { public class TimeElapsed : Attribute, IActionFilter { private Stopwatch timer; public void OnActionExecuting(ActionExecutingContext context) { timer = Stopwatch.StartNew(); } public void OnActionExecuted(ActionExecutedContext context) { timer.Stop(); string result = " Elapsed time: " + $"{timer.Elapsed.TotalMilliseconds} ms"; IActionResult iActionResult = context.Result; ((ObjectResult)iActionResult).Value += result; } } }
I have created an object of Stopwatch class to measure the time. I start the time in the OnActionExecuting method and stop the time in the OnActionExecuted method.
Then with the Result property of the ActionExecutedContext object (i.e. context.Result) I get the IActionResult returned by the action method. Note that it contains the string value returned by the action which is – This is the Index action on the Home controller.
Finally I cast it to ObjectResult type and add the Elapsed time to it’s value property.
To apply this filter, add the [TimeElapsed] attribute to the Index method of the Home Controller class as shown below:
public class HomeController : Controller { [TimeElapsed] public string Index() { return "This is the Index action on the Home controller"; } }
Run your application and you will see the message that will tell the number of milliseconds the action method takes to execute, as shown in the image below:
The Action Filter can also be created by deriving them from IAsyncActionFilter interface. This interface is given below:
using System.Threading.Tasks; namespace Microsoft.AspNetCore.Mvc.Filters { public interface IAsyncActionFilter : IFilterMetadata { Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next); } }
There is only 1 method which runs before and after the action method has been executed in this IAsyncActionFilter interface.
The ActionExecutingContext object provides context data to the filter, and the ActionExectionDelegate object contains the action method (or the next filter) to be executed.
Using the IAsyncActionFilter interface I can create the asynchronous version of the TimeElapsed filter as shown in the below code:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Diagnostics; using System.Text; using Microsoft.AspNetCore.Mvc.Filters; namespace Filters.CustomFilters { public class TimeElapsedAsync : Attribute, IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { Stopwatch timer = Stopwatch.StartNew(); await next(); timer.Stop(); string result = "<div>Elapsed time: " + $"{timer.Elapsed.TotalMilliseconds} ms</div>"; byte[] bytes = Encoding.ASCII.GetBytes(result); await context.HttpContext.Response.Body.WriteAsync(bytes, 0, bytes.Length); } } }
The Result Filters are executed before and after the result from the Action method is processed. They are executed after the Action Filters.
The Result Filters can be created by implementing the IResultFilter or IAsyncResultFilter interface.
The definition of IResultFilter interface:
namespace Microsoft.AspNetCore.Mvc.Filters { public interface IResultFilter : IFilterMetadata { void OnResultExecuting(ResultExecutingContext context); void OnResultExecuted(ResultExecutedContext context); } }
The Result Filters have the same pattern like the Action Filters.
The IResultFilter interface has 2 methods – OnResultExecuting & OnResultExecuted. The OnResultExecuting method is called just before the result from the action method is processed while the OnResultExecuted method is called just after the result from the action method is processed.
The OnResultExecuting method has the parameter of type ResultExecutingContext with properties given in the below table:
Name | Description |
---|---|
Controller | The name of the controller whose action method is invoked. |
Result | This property is of type IActionResult and contains the IActionResult object returned by the action method |
Cancel | Setting this property to true will stop the processing of action result and will give 404 response. |
The OnResultExecuted method has the parameter of type ResultExecutedContext with properties given in the below table:
Name | Description |
---|---|
Controller | The name of the controller whose action method is invoked. |
Canceled | A read-only property that tells if the request is cancelled |
Exception | Contains exceptions that are thrown in the action method |
ExceptionHandled | When this property is set to true then exceptions are not propagated further |
Result | A read-only property that contains the IActionResult generated by the Action method |
Here I will create a Result Filter that will change the rendered View when invoked by the Action method.
Create a Result Filter class called ChangeView.cs inside the CustomFilters folder with the code shown below:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Filters.CustomFilters { public class ChangeView : Attribute, IResultFilter { public void OnResultExecuting(ResultExecutingContext context) { context.Result = new ViewResult { ViewName = "List" }; } public void OnResultExecuted(ResultExecutedContext context) { } } }
The above Result Filter is a simple one as it just sets the ViewName to ‘List’ in the OnResultExecuting() method. This means when this filter is applied to an action method then List view will be rendered every time.
Now create a new action method called Message in the Home controller as shown below, this action is applied with the [ChangeView] filter:
[ChangeView] public ViewResult Message() => View();
You can clearly see that this action method is invoking the default view which is Message.
Now create 2 Views in the Views ➤ Shared folder. These are:
<h2>Message</h2>
This is Message View
<h2>List</h2>
This is List View
Now it’s time to run your application and see what my Result Filter will do. So run the project and go to the URL – /Home/Message. You will see the List View gets invoked instead of Message View, as shown by the below image:
This happens because of the ChangeView Result Filter who changed the rendered View to list.
Now I can recreate my ChangeView Result Filter by implementing the asynchronous IAsyncResultFilter interface.
The definition of IAsyncResultFilter is given below:
using System.Threading.Tasks; namespace Microsoft.AspNetCore.Mvc.Filters { public interface IAsyncResultFilter : IFilterMetadata { Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next); } }
Now, add a new class file called ChangeViewAsync with the code shown below:
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Filters.CustomFilters { public class ChangeViewAsync : Attribute, IAsyncResultFilter { public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { context.Result = new ViewResult { ViewName = "List" }; await next(); } } }
I have implemented the IAsyncResultFilter interface which only contains one method which is OnResultExecutionAsync.
The parameter of type ResultExecutingContext is the one which I use to set the ViewName to ‘List’.
The parameter of type ResultExecutionDelegate is a delegate that asynchronously returns an Microsoft.AspNetCore.Mvc.Filters.ResultExecutedContext indicating the action result or the next result filter has executed.
I had manually invoked the delegate with await next() so that the action result can be rendered.
Now go to the Home controller and change the attribute from [ChangeView] to [ChangeViewAsync] for this to be applied on the action method.
[ChangeViewAsync] public ViewResult Message() => View();
A Hybrid Action / Result Filter is the one that is both an Action and a Result Filter. A Hybrid Filter can easily share data from the Action to the Result stage.
The simplest way to create a Hybrid Action / Result filter is to inherit the ActionFilterAttribute class that implements the interfaces for both kinds of filters.
Create a new class file called HybridActRes.cs inside the CustomFilters folder with the following code:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ViewFeatures; namespace Filters.CustomFilters { public class HybridActRes : ActionFilterAttribute { private Stopwatch timer; public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { timer = Stopwatch.StartNew(); await next(); } public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { timer.Stop(); context.Result = new ViewResult { ViewName = "ShowTime", ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = "Elapsed time: " + $"{timer.Elapsed.TotalMilliseconds} ms" } }; await next(); } } }
This hybrid filter has 2 methods:
Next, create a List action method inside the Home controller with the code shown below:
[HybridActRes] public IActionResult List() => View();
The action method has the [HybridActRes] attribute applied to it and invokes the List view with empty model.
Finally create a view called ShowTime inside the Views ➤ Shared folder with the following code:
@model string <h2>Show Time</h2> @Model
Now I can check the working of this Hybrid Filter by running the project and visiting the URL – /Home/List.
You will see the ShowTime View being invoked instead of List View and it shows the elapsed time.
The below image shows it:
So with this hybrid filter I am able to do both the functionality in a single file.
Exception Filters allow catching exceptions without having to write try & catch block. They implement the IExceptionFilter or IAsyncExceptionFilter interface. The IAsyncExceptionFilter interface is used for creating Asynchronous Exception Filters.
The definition of IExceptionFilter interface:
namespace Microsoft.AspNetCore.Mvc.Filters { public interface IExceptionFilter : IFilterMetadata { void OnException(ExceptionContext context); } }
The definition of IAsyncExceptionFilter interface:
using System.Threading.Tasks; namespace Microsoft.AspNetCore.Mvc.Filters { public interface IAsyncExceptionFilter : IFilterMetadata { Task OnExceptionAsync(ExceptionContext context); } }
For both interfaces, context data is provided through the ExceptionContext class, which is a parameter to the methods – OnException & OnExceptionAsync.
The properties of the ExceptionContext class are given in the table below:
Name | Description |
---|---|
Exception | The property contains the Exceptions that are thrown |
ExceptionDispatchInfo | It contains the stack trace details of the exception |
ExceptionHandled | A read-only property that tells if the exception is handled |
Result | This property sets the IActionResult that will be used to generate the response |
Create a class called CatchError.cs inside the CustomFilters folder. Add the following code to this class:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ViewFeatures; namespace Filters.CustomFilters { public class CatchError : Attribute, IExceptionFilter { public void OnException(ExceptionContext context) { context.Result = new ViewResult() { ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = context.Exception.Message } }; } } }
The class called CatchError is an Exception Filter as it implements the IExceptionFilter Interface.
The OnException() method will be called whenever there is some error occurred in the action method having [CatchError] attribute.
Inside the OnException() method I am just setting the Model with the context.Exception.Message property. The Model will be shown on the View.
Now create a new action method called Exception in the Home Controller and add the attribute [CatchError] to it:
[CatchError] public IActionResult Exception(int? id) { if (id == null) throw new Exception("Error Id cannot be null"); else return View((object)$"The value is {id}"); }
This action method raises an exception if Id value is null else it returns the default View with a string value – The value is {id}.
Finally create Exception view inside the Views ➤ Home folder with the following code:
@model string <h2>Exception</h2> @Model
You can now check the working of this Exception Filter by running your application and going to the URL – /Home/Exception. It will show you the message – Error Id cannot be null as an exception is raised which is captured by this filter.
Now go to the URL – /Home/Exception/5 where no exception occurs and you will see the message – The value is 5.
This is illustrated by the below image:
You can download the source code using the below link:
You now have the basic knowledge of filters and are ready to use it in your project.
Share this article -