Centralized Exception Handling in ASP.NET Core

Tapesh Mehta Tapesh Mehta | Published on: Jul 17, 2024 | Est. reading time: 7 minutes
Centralized Exception Handling in ASP.NET Core

Exception handling is an essential part of developing powerful and dependable programs within ASP.NET Core. Centralized exception handling enables you to handle errors across your application. With middleware, exceptions can be caught at a single point and handled uniformly. Injecting dependencies into the middleware also gives the code flexibility and maintainability.

This article discusses centralized exception handling using middleware in ASP.NET Core. We’ll show you how to implement a custom middleware for exception handling and inject dependencies to handle errors. We’ll also give plenty of code samples to demonstrate these concepts.

For those interested in learning more about .NET development, check out our .NET Development blogs. Stay updated with the latest insights and best practices!

Understanding Middleware in ASP.NET Core

Middleware is an element of the ASP.NET Core request processing process. Each middleware element processes requests and replies in the order they’re put into the pipeline. Middleware components can handle authentication, logging and error handling.

Concept of Centralized Exception Handling Using Middleware

Centralized exception handling captures and manages exceptions at one point in the application. This approach simplifies error handling by reducing redundancy and maintaining consistency. With custom middleware, exceptions thrown during request processing can be intercepted and handled accordingly.

Benefits of Centralized Exception Handling

  1. Consistency: Ensures a uniform approach to error handling across the application.
  2. Maintainability: Simplifies maintenance by consolidating error handling logic in one place.
  3. Logging: Provides a centralized location for logging errors, making it easier to monitor and debug issues.
  4. Flexibility: Allows for custom error responses and error handling strategies.

Creating Custom Middleware for Exception Handling

To implement centralized exception handling, we’ll create a custom middleware that captures exceptions and returns a standardized error response.

Step-by-Step Implementation

1. Create the Middleware Class First, create a new class for the custom middleware:

using Microsoft.AspNetCore.Http;
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;

    public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An unhandled exception occurred.");
            await HandleExceptionAsync(context, ex);
        }
    }

    private Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        var response = new
        {
            StatusCode = context.Response.StatusCode,
            Message = "An internal server error occurred. Please try again later.",
            Detailed = exception.Message // This can be removed in production for security reasons
        };

        return context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response));
    }
}

2. Register the Middleware in the Pipeline

Next, register the middleware in the Startup.cs file:

public class Startup
{
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseMiddleware<ExceptionHandlingMiddleware>();

        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Injecting Dependencies into Middleware

Looking to enhance your team with top talent? Hire ASP.NET developers through WireFuture for expert solutions and exceptional service. Discover more on our website!

Middleware in ASP.NET Core can leverage dependency injection (DI) to access services required for exception handling. For example, we might want to inject a logging service or a custom error handling service.

1. Define a Custom Error Handling Service First, create an interface and a class for the custom error handling service:

public interface ICustomErrorHandler
{
    Task HandleErrorAsync(HttpContext context, Exception exception);
}

public class CustomErrorHandler : ICustomErrorHandler
{
    private readonly ILogger<CustomErrorHandler> _logger;

    public CustomErrorHandler(ILogger<CustomErrorHandler> logger)
    {
        _logger = logger;
    }

    public Task HandleErrorAsync(HttpContext context, Exception exception)
    {
        _logger.LogError(exception, "An error occurred.");

        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        var response = new
        {
            StatusCode = context.Response.StatusCode,
            Message = "An error occurred. Please try again later."
        };

        return context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response));
    }
}

2. Update the Middleware to Use the Custom Error Handling Service

Modify the ExceptionHandlingMiddleware to inject and use the custom error handling service:

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ICustomErrorHandler _customErrorHandler;

    public ExceptionHandlingMiddleware(RequestDelegate next, ICustomErrorHandler customErrorHandler)
    {
        _next = next;
        _customErrorHandler = customErrorHandler;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await _customErrorHandler.HandleErrorAsync(context, ex);
        }
    }
}

3. Register the Custom Error Handling Service in the DI Container

Register the custom error handling service in the Startup.cs file:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ICustomErrorHandler, CustomErrorHandler>();
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseMiddleware<ExceptionHandlingMiddleware>();

        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Example of Dependency Injection with Logging

Suppose we want to inject a logging service into the middleware to log exceptions. ASP.NET Core’s built-in logging framework can be used for this purpose.

1. Inject the Logger into the Middleware Modify the ExceptionHandlingMiddleware to inject the ILogger service:

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;

    public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An unhandled exception occurred.");
            await HandleExceptionAsync(context, ex);
        }
    }

    private Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        var response = new
        {
            StatusCode = context.Response.StatusCode,
            Message = "An internal server error occurred. Please try again later.",
            Detailed = exception.Message // This can be removed in production for security reasons
        };

        return context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response));
    }
}

2. Register the Middleware in the Pipeline

Register the middleware in the Startup.cs file:

public class Startup
{
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseMiddleware<ExceptionHandlingMiddleware>();

        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Example of Custom Error Handling Service with DI

In this example, we’ll demonstrate how to create a custom error handling service and inject it into the middleware for managing exceptions.

1. Define the Custom Error Handling Service Create an interface and a class for the custom error handling service:

public interface ICustomErrorHandler
{
    Task HandleErrorAsync(HttpContext context, Exception exception);
}

public class CustomErrorHandler : ICustomErrorHandler
{
    private readonly ILogger<CustomErrorHandler> _logger;

    public CustomErrorHandler(ILogger<CustomErrorHandler> logger)
    {
        _logger = logger;
    }

    public Task HandleErrorAsync(HttpContext context, Exception exception)
    {
        _logger.LogError(exception, "An error occurred.");

        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        var response = new
        {
            StatusCode = context.Response.StatusCode,
            Message = "An error occurred. Please try again later."
        };

        return context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response));
    }
}

2. Update the Middleware to Use the Custom Error Handling Service

Modify the ExceptionHandlingMiddleware to inject and use the custom error handling service:

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ICustomErrorHandler _customErrorHandler;

    public ExceptionHandlingMiddleware(RequestDelegate next, ICustomErrorHandler customErrorHandler)
    {
        _next = next;
        _customErrorHandler = customErrorHandler;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await _customErrorHandler.HandleErrorAsync(context, ex);
        }
    }
}

3. Register the Custom Error Handling Service in the DI Container

Register the custom error handling service in the Startup.cs file:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ICustomErrorHandler, CustomErrorHandler>();
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseMiddleware<ExceptionHandlingMiddleware>();

        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Conclusion

Centralized exception handling making use of middleware within ASP.NET Core provides a consistent, manageable method of handling errors. Injecting dependencies into the middleware can improve the flexibility and functionality of your error handling logic. Whether it’s logging errors, returning custom error responses, or integrating with external services, dependency injection allows you to create robust ASP.NET development solutions.

We have discussed centralized exception handling using middleware, a implementation guide and dependency injection for error management. Following these practices can help you develop resilient ASP.NET Core applications which are much less difficult to maintain.

Share

clutch profile designrush wirefuture profile goodfirms wirefuture profile
Join the Digital Revolution with WireFuture! 🌟

Step into the future with WireFuture at your side. Our developers harness the latest technologies to deliver solutions that are agile, robust, and ready to make an impact in the digital world.

Hire Now

Categories
.NET Development Angular Development JavaScript Development KnockoutJS Development NodeJS Development PHP Development Python Development React Development Software Development SQL Server Development VueJS Development All
About Author
wirefuture - founder

Tapesh Mehta

verified Verified
Expert in Software Development

Tapesh Mehta is a seasoned tech worker who has been making apps for the web, mobile devices, and desktop for over 13+ years. Tapesh knows a lot of different computer languages and frameworks. For robust web solutions, he is an expert in Asp.Net, PHP, and Python. He is also very good at making hybrid mobile apps, which use Ionic, Xamarin, and Flutter to make cross-platform user experiences that work well together. In addition, Tapesh has a lot of experience making complex desktop apps with WPF, which shows how flexible and creative he is when it comes to making software. His work is marked by a constant desire to learn and change.

Get in Touch
Your Ideas, Our Strategy – Let's Connect.

No commitment required. Whether you’re a charity, business, start-up or you just have an idea – we’re happy to talk through your project.

Embrace a worry-free experience as we proactively update, secure, and optimize your software, enabling you to focus on what matters most – driving innovation and achieving your business goals.

Hire Your A-Team Here to Unlock Potential & Drive Results
You can send an email to contact@wirefuture.com
clutch wirefuture profile designrush wirefuture profile goodfirms wirefuture profile good firms award-4 award-5 award-6