Pavalisoft.ExceptionHandling  1.0.0
An Unified API for ASP.NET Core Global Exception handling with ExceptionManager
Pavalisoft.ExceptionHandling Documentation

Exception Handling

Pavalisoft.ExceptionHandling is an open source ASP.NET Core global exception handler extension complaint with .NET Standard 2.0 written in C#, which provides ExceptionFilter and ExceptionHandlingMiddleware APIs.

The main goal of the Pavalisoft.ExceptionHandling package is to make developer's life easier to handle exceptions handling scenarios at single place and concentrate on functionality. It's additional feature ExceptionManager and inbuilt ExceptionHandlers supports various exception handling mechanisms with configurable ExceptionSettings

By default, Pavalisoft.ExceptionHandling also supports exceptions logging and exception messages localization through ExceptionSettings configuration.

ExceptionRaiser supports to raise the exceptions from the code wherever required which should be handled through ExceptionManager using ErrorDetail.

Provides inbuilt ObjectResult and ViewResult specific creators and result handlers to create HttpResponseMessages for WebApi and WebApp implementations. While creating the ViewResult for the handled exception, the ViewResult creators takes the the Error ViewName from ErrorDetail.ViewName from ExceptionSettings. The ExceptionData object will be returned in ObjectResult as json content and will be returned its properties/attributes as ViewData in ViewResult to the ErrorDetail.ViewNameview.

Documentation & Samples

Complete Documentation is available at https://pavalisoft.github.io/ExceptionHandling/ for Pavalisoft.ExceptionHandling API

Refer https://github.com/pavalisoft/ExceptionHandling/tree/master/Samples for reference implementations

  • Pavalisoft.ExceptionHandling.Sample - Console application : ExceptionHandlingMiddleware with ObjectResultCreator, ObjectResultHandler, Application specific ExceptionCodesDecider, ExceptionSettings in appsettings.json, exceptions logging and exception messages localization.
  • Pavalisoft.ExceptionHandling.NoConfigSample - Console application : ExceptionHandlingMiddleware with ObjectResultCreator, ObjectResultHandler, Application specific ExceptionCodesDecider, ExceptionSettings object creation in Program.cs, exceptions logging and exception messages localization.
  • Pavalisoft.ExceptionHandling.FilterSample - ASP.NET Core MVC WebApp : ExceptionFilter with ViewResultCreator, ViewResultHandler, Application specific ExceptionCodesDecider, ExceptionSettings in appsettings.json, exceptions logging and exception messages localization.
  • Pavalisoft.ExceptionHandling.RestFilterSample - ASP.NET Core MVC WebApi : ExceptionFilter with ObjectResultCreator, ObjectResultHandler, Application specific ExceptionCodesDecider, ExceptionSettings in appsettings.json, exceptions logging and exception messages localization.
  • Pavalisoft.ExceptionHandling.MiddlewareSample - ASP.NET Core MVC WebApp : ExceptionHandlingMiddleware with ViewResultCreator, ViewResultHandler, Application specific ExceptionCodesDecider, ExceptionSettings in appsettings.json, exceptions logging and exception messages localization.
  • Pavalisoft.ExceptionHandling.RestMiddlewareSample - ASP.NET Core MVC WebApi : ExceptionHandlingMiddleware with ObjectResultCreator, ObjectResultHandler, Application specific ExceptionCodesDecider, ExceptionSettings in appsettings.json, exceptions logging and exception messages localization.

Exception Manager Usage with ExceptionFilter

  1. Define the Error Details and Exception Handlers in Exceptions configuration section in appSettings.json.
{
  "Exceptions": {
    "EnableLocalization": "true",
    "EnableLogging": "true",
    "DefaultErrorDetail": "E6000",
    "DefaulExceptiontHandler": "SupressHandler",
    "ErrorDetails": [
      {
        "LogLevel": "Error",
        "ErrorCode": "6000",
        "StatusCode": "500",
        "Message": "{0}",
        "HandlerName": "SupressHandler",
        "EventId": {
          "Id": "1",
          "Name": "General"
        },
        "ViewName": "ErrorResponse"
      },
      {
        "LogLevel": "Error",
        "ErrorCode": "6001",
        "StatusCode": "500",
        "Message": "Unhandled Exception occured.",
        "HandlerName": "SupressHandler",
        "EventId": {
          "Id": "1",
          "Name": "General"
        },
        "ViewName": "ErrorResponse"
      },
      {
        "LogLevel": "Error",
        "ErrorCode": "6002",
        "StatusCode": "200",
        "Message": "Argument {0} is null",
        "HandlerName": "PropagateHandler",
        "EventId": {
          "Id": "1",
          "Name": "General"
        },
        "ViewName": "ErrorResponse"
      },
      {
        "LogLevel": "Error",
        "ErrorCode": "6003",
        "StatusCode": "202",
        "Message": "Unbale to connect to {0} server",
        "WrapMessage": "Username or password is invalid",
        "HandlerName": "WrapHandler",
        "EventId": {
          "Id": "1",
          "Name": "Secured"
        },
        "ViewName": "ErrorResponse"
      },
      {
        "LogLevel": "Error",
        "ErrorCode": "6004",
        "StatusCode": "404",
        "Message": "Argument out of index at {0}",
        "HandlerName": "SupressHandler",
        "EventId": {
          "Id": "1",
          "Name": "General"
        },
        "ViewName": "ErrorResponse"
      }
    ],
    "ExceptionHandlers": [
      {
        "Name": "SupressHandler",
        "Behaviour": "Supress"
      },
      {
        "Name": "WrapHandler",
        "Behaviour": "Wrap"
      },
      {
        "Name": "PropagateHandler",
        "Behaviour": "Propagate"
      }
    ]
  }
}
  1. Add the resx (e.g SharedResource.resx) file with the exception localization test to the Resources folder and a class with the same name as resx file(e.g. SharedResource.cs to support localization.
Pavalisoft.ExceptionHandling.FilterSample
|- Resources
|----- SharedResource.resx
|----- SharedResource.te-in.resx
|- SharedResource.cs

using Microsoft.Extensions.Localization;

namespace Pavalisoft.ExceptionHandling.FilterSample
{
    public interface ISharedResource
    {
    }
    public class SharedResource : ISharedResource
    {
        private readonly IStringLocalizer _localizer;

        public SharedResource(IStringLocalizer<SharedResource> localizer)
        {
            _localizer = localizer;
        }

        public string this[string index]
        {
            get
            {
                return _localizer[index];
            }
        }
    }
}
  1. Add Pavalisoft.ExceptionHandling package to project then add `ExceptionFilter' to MVC services and request pipeline with logging and localization.
...
// Imports Pavalisoft.ExceptionHandling
using Pavalisoft.ExceptionHandling.ActionResultCreators;
using Pavalisoft.ExceptionHandling.ActionResultHandlers;
...

namespace Pavalisoft.ExceptionHandling.FilterSample
{
    public class Startup
    {
		...
        public void ConfigureServices(IServiceCollection services)
        {
            ...
            // Add Logging and Localization Middleware to services
            services.AddLogging();
            services.AddLocalization(options => options.ResourcesPath = "Resources");

            // Adds Pavalisoft.ExceptionHandling Exception Filer to MVC Middleware services with Application Specific Exception Codes decider.
            services.AddExceptionFilter<ViewResultCreator, ViewResultHandler, SharedResources, AppExceptionCodesDecider>();
			...
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
			...
            // Uses Pavalisoft.ExceptionHandling Exception Filer in Request Pipeline
            app.UseExceptionHandlingFilter();            
			...
        }
		...
    }
    /// <summary>
    /// Application Specific Exception Codes provider implementation
    /// </summary>
    public class AppExceptionCodesDecider : ExceptionCodesDecider
    {
        public override ExceptionCodeDetails DecideExceptionCode(Exception ex)
        {
            if(ex is System.ArgumentOutOfRangeException)
            {
                return new ExceptionCodeDetails("E6004", new object[] { "test1" });
            }
            return base.DecideExceptionCode(ex);
        }
    }
}
  1. Use the other services.AddExceptionFilter<ViewResultCreator, ViewResultHandler, SharedResources, AppExceptionCodesDecider>() extension method to pass the ExceptionSettings instead of json in appsettings.json file in services.AddExceptionFilter<ViewResultCreator, ViewResultHandler, SharedResources, AppExceptionCodesDecider>(); method.
...
// Imports Pavalisoft.ExceptionHandling
using Pavalisoft.ExceptionHandling.ActionResultCreators;
using Pavalisoft.ExceptionHandling.ActionResultHandlers;
...

namespace Pavalisoft.ExceptionHandling.FilterSample
{
    public class Startup
    {
		...
        public void ConfigureServices(IServiceCollection services)
        {
            ...
            // Add Logging and Localization Middleware to services
            services.AddLogging();
            services.AddLocalization(options => options.ResourcesPath = "Resources");

            // Adds Pavalisoft.ExceptionHandling Exception Filer to MVC Middleware services with Application Specific Exception Codes decider.
            services.AddExceptionFilter<ViewResultCreator, ViewResultHandler, SharedResources, AppExceptionCodesDecider>(null, CreateExceptionSettings());
			...
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
			...
            // Uses Pavalisoft.ExceptionHandling Exception Filer in Request Pipeline
            app.UseExceptionHandlingFilter();            
			...
        }
		...
        private ExceptionSettings CreateExceptionSettings()
        {
            return new ExceptionSettings {
                EnableLocalization = true,
                EnableLogging = true,
                DefaultErrorDetail = "E6001",
                DefaulExceptiontHandler = "BaseHandler",
                ErrorDetails = new List<ErrorDetail> {
                    new ErrorDetail
                    {
                        LogLevel = LogLevel.Error,
                        ErrorCode = "6001",
                        StatusCode = System.Net.HttpStatusCode.OK,
                        Message = "Unhandled Exception occurred.",
                        WrapMessage = "Unhandled Exception occurred.",
                        HandlerName = "BaseHandler",
                        EventId = new EventId
                        {
                            Id = 1,
                            Name = "General"
                        },
                        ViewName = "Error"
                    }
                },
                ExceptionHandlers = new List<ExceptionHandlerDefinition>
                {
                    new ExceptionHandlerDefinition
                    {
                        Name = "BaseHandler",
                        Behaviour = HandlingBehaviour.Supress,
                        Config = string.Empty
                    }
                }
            };
        }
    }
    /// <summary>
    /// Application Specific Exception Codes provider implementation
    /// </summary>
    public class AppExceptionCodesDecider : ExceptionCodesDecider
    {
        public override ExceptionCodeDetails DecideExceptionCode(Exception ex)
        {
            if(ex is System.ArgumentOutOfRangeException)
            {
                return new ExceptionCodeDetails("E6004", new object[] { "test1" });
            }
            return base.DecideExceptionCode(ex);
        }
    }
}

Note: Use ObjectResultCreator and ObjectResultHandler in WebAPI applications instead of ViewResultCreator and ViewResultHandler

  1. Use ExceptionManager and/or ExceptionRaiser methods handle and raise exceptions.
// Import Pavalisoft.ExceptionHandling interfaces
using Pavalisoft.ExceptionHandling.Interfaces;

namespace Pavalisoft.ExceptionHandling.FilterSample.Controllers
{
    public class TestController : Controller
    {
        private readonly IExceptionManager _exceptionManager;
        private readonly IExceptionRaiser _exceptionRaiser;

        public TestController(IExceptionManager exceptionManager, IExceptionRaiser exceptionRaiser)
        {
            _exceptionManager = exceptionManager;
            _exceptionRaiser = exceptionRaiser;
        }

        public IActionResult Index()
        {
            // This exception will be caught at ExceptionFilter and gets handled automatically.
            throw new System.ArgumentOutOfRangeException("test");
        }

        public IActionResult RaiseException()
        {
            // Raises an exception with the error code which will be handled by 
            // ExceptionManager at ExceptionFilter level using the ErrorDetail having LogLevel
            // as Error and ExceptionCode as 6002. 
            _exceptionRaiser.RaiseException("E6002", new System.ArgumentNullException(), "test");
            return View();
        }

        public IActionResult ManageException()
        {
            // Handles the an exception with the error code which will be caught at ExceptionFilter level 
            // using the ErrorDetail having LogLevel as Error and ExceptionCode as 6002. 
            _exceptionManager.ManageException("E6002", new System.ArgumentNullException(), "test");
            return View();
        }
    }
}
  1. Create ErrorResponse.cshtml to View/Shared folder with the below html
@{
    ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

<p>
    <strong>Exception Code:</strong><code>@ViewData["ExceptionCode"]</code>
</p>
<p>
    <strong>Message:</strong><code>@ViewData["Message"]</code>
</p>
<p>
    <strong>Event Id:</strong><code>@ViewData["EventId"]</code>
</p>
<p>
    <strong>Event Name:</strong><code>@ViewData["EventName"]</code>
</p>

Exception Manager Usage with ExceptionHandlingMiddleware

  1. Define the Error Details and Exception Handlers in Exceptions configuration section in appSettings.json.
{
  "Exceptions": {
    "EnableLocalization": "true",
    "EnableLogging": "true",
    "DefaultErrorDetail": "E6000",
    "DefaulExceptiontHandler": "SupressHandler",
    "ErrorDetails": [
      {
        "LogLevel": "Error",
        "ErrorCode": "6000",
        "StatusCode": "500",
        "Message": "{0}",
        "HandlerName": "SupressHandler",
        "EventId": {
          "Id": "1",
          "Name": "General"
        },
        "ViewName": "ErrorResponse"
      },
      {
        "LogLevel": "Error",
        "ErrorCode": "6001",
        "StatusCode": "500",
        "Message": "Unhandled Exception occured.",
        "HandlerName": "SupressHandler",
        "EventId": {
          "Id": "1",
          "Name": "General"
        },
        "ViewName": "ErrorResponse"
      },
      {
        "LogLevel": "Error",
        "ErrorCode": "6002",
        "StatusCode": "200",
        "Message": "Argument {0} is null",
        "HandlerName": "PropagateHandler",
        "EventId": {
          "Id": "1",
          "Name": "General"
        },
        "ViewName": "ErrorResponse"
      },
      {
        "LogLevel": "Error",
        "ErrorCode": "6003",
        "StatusCode": "202",
        "Message": "Unbale to connect to {0} server",
        "WrapMessage": "Username or password is invalid",
        "HandlerName": "WrapHandler",
        "EventId": {
          "Id": "1",
          "Name": "Secured"
        },
        "ViewName": "ErrorResponse"
      },
      {
        "LogLevel": "Error",
        "ErrorCode": "6004",
        "StatusCode": "404",
        "Message": "Argument out of index at {0}",
        "HandlerName": "SupressHandler",
        "EventId": {
          "Id": "1",
          "Name": "General"
        },
        "ViewName": "ErrorResponse"
      }
    ],
    "ExceptionHandlers": [
      {
        "Name": "SupressHandler",
        "Behaviour": "Supress"
      },
      {
        "Name": "WrapHandler",
        "Behaviour": "Wrap"
      },
      {
        "Name": "PropagateHandler",
        "Behaviour": "Propagate"
      }
    ]
  }
}
  1. Add the resx (e.g SharedResource.resx) file with the exception localization test to the Resources folder and a class with the same name as resx file(e.g. SharedResource.cs to support localization.
Pavalisoft.ExceptionHandling.MiddlewareSample
|- Resources
|----- SharedResource.resx
|----- SharedResource.te-in.resx
|- SharedResource.cs

using Microsoft.Extensions.Localization;

namespace Pavalisoft.ExceptionHandling.MiddlewareSample
{
    public interface ISharedResource
    {
    }
    public class SharedResource : ISharedResource
    {
        private readonly IStringLocalizer _localizer;

        public SharedResource(IStringLocalizer<SharedResource> localizer)
        {
            _localizer = localizer;
        }

        public string this[string index]
        {
            get
            {
                return _localizer[index];
            }
        }
    }
}
  1. Add Pavalisoft.ExceptionHandling package to project then add `ExceptionFilter' to MVC services and request pipeline with logging and localization.
...
// Imports Pavalisoft.ExceptionHandling
using Pavalisoft.ExceptionHandling.ActionResultCreators;
using Pavalisoft.ExceptionHandling.ActionResultHandlers;
...

namespace Pavalisoft.ExceptionHandling.MiddlewareSample
{
    public class Startup
    {
		...
        public void ConfigureServices(IServiceCollection services)
        {
            ...
            // Add Logging and Localization Middleware to services
            services.AddLogging();
            services.AddLocalization(options => options.ResourcesPath = "Resources");

            // Adds Pavalisoft.ExceptionHandling Middleware to MVC Middleware services with Application Specific Exception Codes decider.
            services.AddExceptionHandling<ViewResultCreator, ViewResultHandler,SharedResource, AppExceptionCodesDecider>();
			...
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
			...
            // Uses Pavalisoft.ExceptionHandling Middleware in Request Pipeline
            app.UseExceptionHandlingMiddleware();
			...
        }
		...
    }
    /// <summary>
    /// Application Specific Exception Codes provider implementation
    /// </summary>
    public class AppExceptionCodesDecider : ExceptionCodesDecider
    {
        public override ExceptionCodeDetails DecideExceptionCode(Exception ex)
        {
            if(ex is System.ArgumentOutOfRangeException)
            {
                return new ExceptionCodeDetails("E6004", new object[] { "test1" });
            }
            return base.DecideExceptionCode(ex);
        }
    }
}
  1. Use the other services.AddExceptionHandling<ViewResultCreator, ViewResultHandler, SharedResources, AppExceptionCodesDecider>() extension method to pass the ExceptionSettings instead of json in appsettings.json file in services.AddExceptionFilter<ViewResultCreator, ViewResultHandler, SharedResources, AppExceptionCodesDecider>(); method.
...
// Imports Pavalisoft.ExceptionHandling
using Pavalisoft.ExceptionHandling.ActionResultCreators;
using Pavalisoft.ExceptionHandling.ActionResultHandlers;
...

namespace Pavalisoft.ExceptionHandling.MiddlewareSample
{
    public class Startup
    {
		...
        public void ConfigureServices(IServiceCollection services)
        {
            ...
            // Add Logging and Localization Middleware to services
            services.AddLogging();
            services.AddLocalization(options => options.ResourcesPath = "Resources");

            // Adds Pavalisoft.ExceptionHandling Middleware to MVC Middleware services with Application Specific Exception Codes decider.
            services.AddExceptionHandling<ViewResultCreator, ViewResultHandler,SharedResource, AppExceptionCodesDecider>(null, CreateExceptionSettings());
			...
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
			...
            // Uses Pavalisoft.ExceptionHandling Middleware in Request Pipeline
            app.UseExceptionHandlingMiddleware();
			...
        }
		...
        private ExceptionSettings CreateExceptionSettings()
        {
            return new ExceptionSettings {
                EnableLocalization = true,
                EnableLogging = true,
                DefaultErrorDetail = "E6001",
                DefaulExceptiontHandler = "BaseHandler",
                ErrorDetails = new List<ErrorDetail> {
                    new ErrorDetail
                    {
                        LogLevel = LogLevel.Error,
                        ErrorCode = "6001",
                        StatusCode = System.Net.HttpStatusCode.OK,
                        Message = "Unhandled Exception occurred.",
                        WrapMessage = "Unhandled Exception occurred.",
                        HandlerName = "BaseHandler",
                        EventId = new EventId
                        {
                            Id = 1,
                            Name = "General"
                        },
                        ViewName = "Error"
                    }
                },
                ExceptionHandlers = new List<ExceptionHandlerDefinition>
                {
                    new ExceptionHandlerDefinition
                    {
                        Name = "BaseHandler",
                        Behaviour = HandlingBehaviour.Supress,
                        Config = string.Empty
                    }
                }
            };
        }
    }
    /// <summary>
    /// Application Specific Exception Codes provider implementation
    /// </summary>
    public class AppExceptionCodesDecider : ExceptionCodesDecider
    {
        public override ExceptionCodeDetails DecideExceptionCode(Exception ex)
        {
            if(ex is System.ArgumentOutOfRangeException)
            {
                return new ExceptionCodeDetails("E6004", new object[] { "test1" });
            }
            return base.DecideExceptionCode(ex);
        }
    }
}

Note: Use ObjectResultCreator and ObjectResultHandler in WebAPI applications instead of ViewResultCreator and ViewResultHandler

  1. Use ExceptionManager and/or ExceptionRaiser methods handle and raise exceptions.
// Import Pavalisoft.ExceptionHandling interfaces
using Pavalisoft.ExceptionHandling.Interfaces;

namespace Pavalisoft.ExceptionHandling.MiddlewareSample.Controllers
{
    public class TestController : Controller
    {
        private readonly IExceptionManager _exceptionManager;
        private readonly IExceptionRaiser _exceptionRaiser;

        public TestController(IExceptionManager exceptionManager, IExceptionRaiser exceptionRaiser)
        {
            _exceptionManager = exceptionManager;
            _exceptionRaiser = exceptionRaiser;
        }

        public IActionResult Index()
        {
            // This exception will be caught at ExceptionHandlingMiddleware and gets handled automatically.
            throw new System.ArgumentOutOfRangeException("test");
        }

        public IActionResult RaiseException()
        {
            // Raises an exception with the error code which will be handled by 
            // ExceptionManager at ExceptionHandlingMiddleware level using the ErrorDetail having LogLevel
            // as Error and ExceptionCode as 6002. 
            _exceptionRaiser.RaiseException("E6002", new System.ArgumentNullException(), "test");
            return View();
        }

        public IActionResult ManageException()
        {
            // Handles the an exception with the error code which will be caught at ExceptionHandlingMiddleware level 
            // using the ErrorDetail having LogLevel as Error and ExceptionCode as 6002. 
            _exceptionManager.ManageException("E6002", new System.ArgumentNullException(), "test");
            return View();
        }
    }
}
  1. Create ErrorResponse.cshtml to View/Shared folder with the below html
@{
    ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

<p>
    <strong>Exception Code:</strong><code>@ViewData["ExceptionCode"]</code>
</p>
<p>
    <strong>Message:</strong><code>@ViewData["Message"]</code>
</p>
<p>
    <strong>Event Id:</strong><code>@ViewData["EventId"]</code>
</p>
<p>
    <strong>Event Name:</strong><code>@ViewData["EventName"]</code>
</p>

Exception Handlers

The below are the inbuilt exception handlers provided.

Action Result Creators

The below are the inbuilt action result creators provided.

  • ObjectResultCreator : Provides implementation to create ObjectResult using ErrorDetail.
  • ViewResultCreator : Provides implementation to create ViewResult with the ViewName in the ErrorDetail.

Action Result Handlers

The below are the inbuilt action result handlers provided.

  • ObjectResultHandler : Provides features to handle REST API Application additional exception handling conditions.
  • ViewResultHandler : Provides features to handle Web Application additional exception handling conditions.

Builds

Get latest builds from nuget

Package Version
Pavalisoft.ExceptionHandling 1.0.0

Contributing

Getting started with Git and GitHub

Once you're familiar with Git and GitHub, clone the repository and start contributing.