Exception Filter in ASP.NET CORE MVC

Exception Filter is used to handling expectation which occurs in action method this filter can be applied on the controller or action method.

We have 2 type of Exception filter one is Sync and another is Async, for implementing Sync type we need to inherit IExceptionFilter and for implementing Sync type we need to inherit IAsyncExceptionFilter Filter.

  • IExceptionFilter
  • IAsyncExceptionFilter

Let’s create a Custom AuthorizationFilter with name CustomExceptionFilterAttribute and we are going to inherit it with Attribute class and IExceptionFilter interface and implement the OnException method.

Implementation

using Microsoft.AspNetCore.Mvc.Filters;
using System;

namespace WebApplication4.Filters
{
    public class CustomExceptionFilterAttribute : Attribute, IExceptionFilter
    {
        public void OnException(ExceptionContext context)
        {
            throw new NotImplementedException();
        }
    }
}

In this filter, we are going to log expectation in a text file which will be stored in ErrorLogs folder which is inside the www-root folder. If you see CustomExceptionFilterAttribute constructor you will find 2 interface IHostingEnvironment, IModelMetadataProvider we are using IHostingEnvironment for getting WebRootPath which will be used for storing log in ErrorLogs folder, IModelMetadataProvider is to forgetting model metadata. From ExceptionContext we get all details related to error (Message, Source, Stack Trace, Target Site) this error details we are going to write in a text file.

Code Snippet of IExceptionFilter

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.Globalization;
using System.IO;
using System.Text;

namespace WebApplication4.Filters
{
    public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
    {
        private readonly IHostingEnvironment _hostingEnvironment;
        private readonly IModelMetadataProvider _modelMetadataProvider;

        public CustomExceptionFilterAttribute(
            IHostingEnvironment hostingEnvironment,
            IModelMetadataProvider modelMetadataProvider)
        {
            _hostingEnvironment = hostingEnvironment;
            _modelMetadataProvider = modelMetadataProvider;
        }

        public override void OnException(ExceptionContext context)
        {
            if (!_hostingEnvironment.IsDevelopment())
            {
                return;
            }
            context.ExceptionHandled = true;

            var errorFolder = Path.Combine(_hostingEnvironment.WebRootPath, "ErrorLogs");

            if (!System.IO.Directory.Exists(errorFolder))
            {
                System.IO.Directory.CreateDirectory(errorFolder);
            }

            string timestamp = DateTime.Now.ToString("d-MMMM-yyyy", CultureInfo.InvariantCulture);
            var newFileName = $"Log_{timestamp}.txt";
            var filepath = Path.Combine(_hostingEnvironment.WebRootPath, "ErrorLogs") + $@"\{newFileName}";


            StringBuilder expectionStringBuilder = new StringBuilder();
            expectionStringBuilder.AppendLine("Message ---\n{0}" + context.Exception.Message);
            expectionStringBuilder.AppendLine("Source ---\n{0}" + context.Exception.Source);
            expectionStringBuilder.AppendLine("StackTrace ---\n{0}" + context.Exception.StackTrace);
            expectionStringBuilder.AppendLine("TargetSite ---\n{0}" + context.Exception.TargetSite);

            if (!File.Exists(filepath))
            {
                using (var writer = File.CreateText(filepath))
                {
                    var controllerName = context.RouteData.Values["controller"];
                    var actionName = context.RouteData.Values["action"];
                    writer.WriteLine($"ControllerName :-{controllerName}");
                    writer.WriteLine($"ActionName :- {actionName}");
                    writer.WriteLine("Exception");
                    writer.WriteLine(expectionStringBuilder);
                }
            }
            else
            {
                using (var writer = File.AppendText(filepath))
                {
                    var controllerName = context.RouteData.Values["controller"];
                    var actionName = context.RouteData.Values["action"];
                    writer.WriteLine($"ControllerName :-{controllerName}");
                    writer.WriteLine($"ActionName :- {actionName}");
                    writer.WriteLine("Exception");
                    writer.WriteLine(expectionStringBuilder);
                }
            }

            var result = new ViewResult { ViewName = "Error" };
            result.ViewData = new ViewDataDictionary(_modelMetadataProvider, context.ModelState);
            result.ViewData.Add("Exception", context.Exception);

            context.Result = result;
        }
    }
}

How to Apply Filter on Controller

using System;
using Microsoft.AspNetCore.Mvc;
using WebApplication4.Filters;

namespace WebApplication4.Controllers
{
    [TypeFilter(typeof(CustomExceptionFilterAttribute))]
    public class DefaultController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

IAsyncExceptionFilter

IAsyncExceptionFilter is an async version of Exception filter to use it we need to implement IAsyncExceptionFilter interface which contains OnExceptionAsync method. 

Let’s Create Custom Async Exception Filter with name CustomAsyncExceptionFilterAttribute and then we are going to inherit with IAsyncExceptionFilter interface and implement OnExceptionAsync method in this method we are going to log Exception and write to a text file which will be stored in ErrorLogs folder which is inside the wwwroot folder.

Code Snippet of IAsyncExceptionFilter

public class CustomAsyncExceptionFilterAttribute : IAsyncExceptionFilter
    {
        private readonly IHostingEnvironment _hostingEnvironment;
        private readonly IModelMetadataProvider _modelMetadataProvider;

        public CustomAsyncExceptionFilterAttribute(
            IHostingEnvironment hostingEnvironment,
            IModelMetadataProvider modelMetadataProvider)
        {
            _hostingEnvironment = hostingEnvironment;
            _modelMetadataProvider = modelMetadataProvider;
        }

        public async Task OnExceptionAsync(ExceptionContext context)
        {

            context.ExceptionHandled = true;

            var errorFolder = Path.Combine(_hostingEnvironment.WebRootPath, "ErrorLogs");

            if (!System.IO.Directory.Exists(errorFolder))
            {
                System.IO.Directory.CreateDirectory(errorFolder);
            }

            string timestamp = DateTime.Now.ToString("d-MMMM-yyyy", CultureInfo.InvariantCulture);
            var newFileName = $"Log_{timestamp}.txt";
            var filepath = Path.Combine(_hostingEnvironment.WebRootPath, "ErrorLogs") + $@"\{newFileName}";


            StringBuilder expectionStringBuilder = new StringBuilder();
            expectionStringBuilder.AppendLine("Message ---\n{0}" + context.Exception.Message);
            expectionStringBuilder.AppendLine("Source ---\n{0}" + context.Exception.Source);
            expectionStringBuilder.AppendLine("StackTrace ---\n{0}" + context.Exception.StackTrace);
            expectionStringBuilder.AppendLine("TargetSite ---\n{0}" + context.Exception.TargetSite);

            if (!File.Exists(filepath))
            {
                using (var writer = File.CreateText(filepath))
                {
                    var controllerName = context.RouteData.Values["controller"];
                    var actionName = context.RouteData.Values["action"];
                    await writer.WriteLineAsync($"ControllerName :-{controllerName}");
                    await writer.WriteLineAsync($"ActionName :- {actionName}");
                    await writer.WriteLineAsync("Exception");
                    await writer.WriteLineAsync(expectionStringBuilder.ToString());
                }
            }
            else
            {
                using (var writer = File.AppendText(filepath))
                {
                    var controllerName = context.RouteData.Values["controller"];
                    var actionName = context.RouteData.Values["action"];
                    await writer.WriteLineAsync($"ControllerName :-{controllerName}");
                    await writer.WriteLineAsync($"ActionName :- {actionName}");
                    await writer.WriteLineAsync("Exception");
                    await writer.WriteLineAsync(expectionStringBuilder.ToString());
                }
            }

            var result = new ViewResult { ViewName = "Error" };
            result.ViewData = new ViewDataDictionary(_modelMetadataProvider, context.ModelState);
            result.ViewData.Add("Exception", context.Exception);

            context.Result = result;
            return;
        }
    }

How to Apply Filter on Controller

using System;
using Microsoft.AspNetCore.Mvc;
using WebApplication4.Filters;

namespace WebApplication4.Controllers
{
    [TypeFilter(typeof(CustomAsyncExceptionFilterAttribute))]
    public class DefaultController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

By Saineshwar Bageri

I am Microsoft MVP | C# Corner MVP | Code Project MVP | FULL STACK .NET Developer and working on .Net Web Technology (Asp.net, Asp.net Core,.Net Core, C#, Sqlserver, MVC, Windows, Console Application, javascript, jquery, json, ORM Dapper) and also a freelance developer.