Back to: ASP.NET Core Tutorials For Beginners and Professionals
Custom Result Filter in ASP.NET Core MVC
In this article, I will discuss How to Create a Custom Result Filter in an ASP.NET Core MVC Application with Real-Time Examples. Please read our previous article discussing Result Filter in ASP.NET Core MVC Application.
Custom Result Filter in ASP.NET Core MVC
In ASP.NET Core MVC, Result Filters are a specific type of filter that runs after the action method has been executed but before the result is processed and sent to the client, i.e., before or after executing action results. Action results are what an action method returns to generate a response. This could be a view, a file, a redirect, or a JSON result, among other things. Custom Result Filters in ASP.NET Core MVC can be useful for:
- Modifying or Replacing the Result.
- Adding HTTP Headers to the Response.
- Logging or Auditing Response Data.
- Handling Errors.
- Caching Responses.
How Do We Create a Custom Result Filter in ASP.NET Core MVC?
In ASP.NET Core, we can create a Custom Result Filter in two ways: First, we can create a class implementing the IResultFilter interface and providing implementations for the OnResultExecuting and OnResultExecuted methods. Second, we can create a class that inherits from the ResultFilterAttribute class and overrides the OnResultExecuting and OnResultExecuted methods. So, creating and using a Custom Result Filter in ASP.NET Core MVC involves the following steps:
- Define the Filter: Create a class that implements the IResultFilter/IAsyncResultFilter interface or inherits from the ResultFilterAttribute class. For synchronous operations, use IResultFilter, and for asynchronous, use IAsyncResultFilter.
- Implement Required Methods: For IResultFilter, you need to implement OnResultExecuting and OnResultExecuted. For IAsyncResultFilter, you need to implement OnResultExecutionAsync. If you are inheriting the ResultFilterAttribute class, you must override the OnResultExecuting and OnResultExecuted methods.
- Apply the Filter: Once the Custom Result Filter is created, apply it to actions or controllers using attributes or add it globally in the Program class.
Approach 1: Inheriting from ResultFilterAttribute Class
Create a Custom Result Filter by inheriting from the ResultFilterAttribute class and overriding its methods: OnResultExecuting and OnResultExecuted. So, create a class file named CustomResultFilterAttribute.cs and copy and paste the following code. In the example below, we log when the OnResultExecuting and OnResultExecuted methods are executed.
using Microsoft.AspNetCore.Mvc.Filters; namespace FiltersDemo.Models { public class CustomResultFilterAttribute : ResultFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { // This method is called before the result is executed. LogMessage("Executing custom result filter attribute before result execution.\n"); } public override void OnResultExecuted(ResultExecutedContext context) { // This method is called after the result has been executed. LogMessage("Executing custom result filter attribute after result execution.\n"); } private void LogMessage(string message) { string filePath = Path.Combine(Directory.GetCurrentDirectory(), "Log", "Log.txt"); //saving the data in a text file called Log.txt File.AppendAllText(filePath, message); } } }
Applying the Custom Result Filter to an Action Method:
Let us modify the Home Controller as follows to apply the Custom Result Filter on the Index Action method:
using FiltersDemo.Models; using Microsoft.AspNetCore.Mvc; namespace FiltersDemo.Controllers { public class HomeController : Controller { [CustomResultFilterAttribute] public IActionResult Index() { return View(); } } }
With the above code in place, run the application, navigate to the Index action method, and verify the Log.txt file, which should be generated within the Log folder.
Registering the Filter Globally
We can also register the filter globally to apply to all actions in the application. This can be done in the Program.cs class file as follows:
builder.Services.AddControllersWithViews(options => { options.Filters.Add(new CustomResultFilterAttribute()); });
Approach 2: Implementing IResultFilter (Synchronous Result Filter)
Let’s say we want to create a result filter that logs the type of ActionResult returned by our action methods. So, create a Custom Result Filter by implementing the IResultFilter interface. You need to implement two methods: OnResultExecuting and OnResultExecuted. These methods are called before and after the result is executed. So, modify the CustomResultFilter.cs as follows.
using Microsoft.AspNetCore.Mvc.Filters; namespace FiltersDemo.Models { public class CustomResultFilter : IResultFilter { public void OnResultExecuting(ResultExecutingContext context) { // This method is called before the result is executed. // Log information before the action result starts executing LogMessage($"Result type is about to be executed: {context.Result.GetType().Name}\n"); LogMessage("Executing custom result filter attribute before result execution.\n"); } public void OnResultExecuted(ResultExecutedContext context) { // This method is called after the result has been executed. // Log information after the action result has finished executing LogMessage($"Result type has been executed: {context.Result.GetType().Name}\n"); LogMessage("Executing custom result filter attribute after result execution.\n"); } private void LogMessage(string message) { string filePath = Path.Combine(Directory.GetCurrentDirectory(), "Log", "Log.txt"); //saving the data in a text file called Log.txt File.AppendAllText(filePath, message); } } }
Register the CustomResultFilter as a service in your application’s Program.cs file. You can specify the filter’s scope (e.g., Transient, Scoped, Singleton) based on your requirements.
builder.Services.AddScoped<CustomResultFilter>();
Apply the CustomResultFilter to your controller action methods using the [ServiceFilter] attribute or add it globally in the Program.cs file. For example, apply on to the Action method as follows:
using FiltersDemo.Models; using Microsoft.AspNetCore.Mvc; namespace FiltersDemo.Controllers { public class HomeController : Controller { [ServiceFilter(typeof(CustomResultFilter))] public IActionResult Index() { return View(); } } }
Adding Globally:
Adding the following code in the Program.cs class file:
//Adding Custom Result Filter Globally builder.Services.AddControllersWithViews(options => { options.Filters.Add(new CustomResultFilterAttribute()); }); builder.Services.AddScoped<CustomResultFilter>();
Approach 3: Implementing IAsyncResultFilter (Asynchronous Result Filter)
Assume you want to compress the response’s content asynchronously in certain conditions. You would implement an asynchronous result filter to compress the response stream.
using Microsoft.AspNetCore.Mvc.Filters; using System.IO.Compression; namespace FiltersDemo.Models { public class CompressResultFilter : Attribute, IAsyncResultFilter { public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { // Check if the response should be compressed based on some logic var shouldCompress = ShouldCompressResponse(context); if (shouldCompress) { var originalBodyStream = context.HttpContext.Response.Body; using (var compressedStream = new MemoryStream()) { using (var compressionStream = new GZipStream(compressedStream, CompressionMode.Compress, leaveOpen: true)) { context.HttpContext.Response.Body = compressionStream; // Execute the result (action filters, action, result filters, result) await next(); // Flush the remaining data await compressionStream.FlushAsync(); } // Copy the compressed data to the original stream compressedStream.Seek(0, SeekOrigin.Begin); await compressedStream.CopyToAsync(originalBodyStream); } context.HttpContext.Response.Body = originalBodyStream; context.HttpContext.Response.Headers.Add("Content-Encoding", "gzip"); } else { // If we don't want to compress, just call the next delegate/middleware in the pipeline await next(); } } private bool ShouldCompressResponse(ResultExecutingContext context) { // Your logic to determine if the response should be compressed return true; // For this example, we're just compressing every response } } }
In this asynchronous result filter example, the filter checks if the response should be compressed. If so, it sets up a new response body stream that compresses the content. After executing the action result (with await next();), it copies the compressed content to the original response stream and sets the appropriate HTTP header.
Then, apply this filter:
using FiltersDemo.Models; using Microsoft.AspNetCore.Mvc; namespace FiltersDemo.Controllers { public class HomeController : Controller { [CompressResultFilter] public IActionResult Index() { return View(); } } }
In the next article, I will discuss Response Caching in ASP.NET Core Applications. In this article, I try to explain Custom Result Filter in ASP.NET Core MVC Application with Examples. I hope you enjoy this Custom Result Filter in ASP.NET Core MVC article.