Global Exception Handling in ASP .NET Core Web API

Global Exception Handling in ASP .NET Core Web API

Custom Error Handling in C#

An exception is an abnormal condition that occurs during program execution. In other way ,It represents a process for handling run-time errors. Exception in c# allows your program to respond to these issues without crashing and provides a way to transfer control from one part of a program to another.

There are many ways we can describes the error. so, error can generally be classified into two main types.

Compile Time Error:

These types of error generally occur during the compilation of the program, means code has syntax issue or other problem that happen

Syntax issue: basically, it can be mistake in the code that violate the language grammar like missing semicolon unmatched parentheses.

Type Errors: Assigning a value of one type to a variable of another incompatible type.

Name Errors: Using undeclared variables or classes.

Run Time Error:

These type occue generally occure while the program is running ,due to unexpected·  DivideByZeroException: Occurs when attempting to divide by zero.

NullReferenceException: Occurs when trying to access a member of a null object.

IndexOutOfRangeException: These exception  Occurs when trying to access an element outside the bounds of an array or collection.

FileNotFoundException:  These exception  Occurs when trying to access a file that does not exist. Condition

Logical Error:

Logical error is type of error in program that causes   it to produce incorrect outout or behavior even program compile successfully without any error.

There are some way where we can manage exception:

Simple Type

In this type we can manage exception using try and catch block .

Try:

Tyr block we can place actual code that might throw an error inside a try block. This is where you anticipate problems.

try {

    // Code that might throw an exception

    int result = 20 / 0; // This will cause a divide by zero error

}

Catch

We can define catch block . this block catch the exception  through by try block;

catch (DivideByZeroException ex) {

    Console.WriteLine("You can't divide by zero!");

}

Or

catch (Exception ex) {

    Console.WriteLine("You can't divide by zero!");

}

Finally Block (optional): This block runs regardless of whether an exception occurred or not. It’s often used for cleanup tasks.

finally {

    Console.WriteLine("This runs no matter what.");

}

Example:

try {

    int result = 10 / 0; // This will throw an exception

}

catch (DivideByZeroException ex) {

    Console.WriteLine("You can't divide by zero!"); // Handle the error

}

catch (Exception ex) {

    Console.WriteLine("You can't divide by zero!"); // Handle the error

}

finally {

    Console.WriteLine("Cleanup code can go here."); // Always runs

}

Global Exception handling customs way

in this example we are going to use ASP .Net core 8 web api

STEP 1: Create a asp.net core web api

create project for exception handling
Error _handling

Step 2 In this step, create another project named Logger and add a class file.

In this step, create two classes: one interface named ILoggerManager and another class file named LoggerManager. Install the package named NLog

NLog

ILoggerManager:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Logger
{
    public interface ILoggerManager
    {
        void LogInfo(string message);
        void LogWarn(string message);
        void LogDebug(string message);
        void LogError(string message);
    }
}

LoggerManager

using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace Logger
{
    public class LoggerService
    {
        public class LoggerManager : ILoggerManager
        {
            private static ILogger logger = LogManager.GetCurrentClassLogger();

            public LoggerManager()
            {
            }

            public void LogDebug(string message)
            {
                logger.Debug(message);
            }

            public void LogError(string message)
            {
                logger.Error(message);
            }

            public void LogInfo(string message)
            {
                logger.Info(message);
            }

            public void LogWarn(string message)
            {
                logger.Warn(message);
            }
        }
    }
}

STEP 4: In main application create a folder name like any Extension and create a one file name ExceptionMiddlewareExtensions

using Logger;
using Microsoft.AspNetCore.Diagnostics;
using System.Net;

namespace ExceptionHandeling.Extension
{
    public static class ExceptionMiddlewareExtensions
    {
        public static void ConfigureExceptionHandler(this IApplicationBuilder app, ILoggerManager logger)
        {
            app.UseExceptionHandler(appError =>
            {
                appError.Run(async context =>
                {
                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    context.Response.ContentType = "application/json";

                    var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
                    if (contextFeature != null)
                    {
                        string rootLogFolder = "Logs"; // Change this to your desired root log folder path
                        string monthFolderName = DateTime.Now.ToString("yyyy-MM"); // Get the current year and month as "yyyy-MM" format
                        string logFilePath = Path.Combine(rootLogFolder, monthFolderName, "error.log");

                        // Ensure the directory for the log file exists
                        Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));

                        using (StreamWriter writer = new StreamWriter(logFilePath, true))
                        {
                            writer.WriteLine(); // 
                            writer.WriteLine($"Date and Time: {DateTime.Now}");
                            writer.WriteLine($"Error: {contextFeature.Error}");
                            writer.WriteLine($"Path: {contextFeature.Path}");
                            writer.WriteLine($"Endpoint: {contextFeature.Endpoint}");
                            writer.WriteLine("******************************************************************"); // 
                        }

                        await context.Response.WriteAsync(new ErrorDetails()
                        {
                            StatusCode = context.Response.StatusCode,
                            Message = "Internal Server Error",
                        }.ToString());
                    }
                });
            });
        }


        
        public static void ConfigureCustomExceptionMiddleware(this IApplicationBuilder app)
        {
            // app.UseMiddleware<ExceptionMiddleware>();
        }
    }
}

Create a model class name ErrorDetails

using System.Text.Json;

using System.Text.Json;

namespace ExceptionHandeling.Models
{
    public class ErrorDetails
    {
        public int StatusCode { get; set; }
        public string Message { get; set; }


        public override string ToString()
        {
            return JsonSerializer.Serialize(this);
        }
    }
}


Note: This code sets up a global error handler for an ASP.NET Core application. An unhandled exception logs error details to a file organized by year and month. A generic “Internal Server Error” message is sent back to the client. This approach maintains stability and provides information for debugging.

Step 5: in program.cs add these file

builder.Services.AddSingleton();
var logger = app.Services.GetRequiredService();
app.ConfigureExceptionHandler(logger);

Step: 6 Run the program and see the output

Project architecture

Exception Handling in ASP MVC 5

In this example either we can handel exception using inbuild attribute like [handelError]

or we can create a custom exception handling

Create a custom file name LogCustomExceptionFilter

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace elic.CustomFilter
{
    public class LogCustomExceptionFilter : FilterAttribute, IExceptionFilter
    {
        public void OnException(ExceptionContext filterContext)
        {
            if (!filterContext.ExceptionHandled)
            {
                var exceptionMessage = filterContext.Exception.Message;
                var stackTrace = filterContext.Exception.StackTrace;
                var controllerName = filterContext.RouteData.Values["controller"].ToString();
                var actionName = filterContext.RouteData.Values["action"].ToString();

                string Message = "Date :" + DateTime.Now.ToString() + ", Controller: " + controllerName + ", Action:" + actionName +
                                 "Error Message : " + exceptionMessage
                                + Environment.NewLine + "Stack Trace : " + stackTrace;
                //saving the data in a text file called Log.txt
                //You can also save this in a dabase
                File.AppendAllText(HttpContext.Current.Server.MapPath("~/Log/Log.txt"), Message);

                filterContext.ExceptionHandled = true;
                filterContext.Result = new ViewResult()
                {
                    ViewName = "Error"
                };
            }
        }
    }
}

You can call customs filter in either controller level action level or Globally throughout the application .

For Controller you can import simply; [LogCustomExceptionFilter].

External libraries or frameworks that enhance exception handling, like NLog.

for more details: apitpoint

GitHub Repository

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *