DEV Community

Cover image for Centralized Exception Handling in ASP.NET Core - Custom Middleware
DotNet Full Stack Dev
DotNet Full Stack Dev

Posted on

Centralized Exception Handling in ASP.NET Core - Custom Middleware

In modern web applications, managing exceptions in a consistent and efficient manner is crucial for providing a robust and user-friendly experience. ASP.NET Core provides powerful tools to handle exceptions globally, allowing you to manage errors in one place rather than scattering error-handling code throughout your application. This blog post will guide you through the process of implementing centralized exception handling in an ASP.NET Core application.

Why Centralized Exception Handling?

Centralized exception handling offers several benefits.

  1. Consistency: Ensure that all errors are handled uniformly.
  2. Maintainability: It is easier to update and manage error-handling logic in one place.
  3. Security: Avoid exposing sensitive information by controlling what error details are returned to the client.
  4. Logging: Centralize logging of exceptions to ensure that all errors are recorded for analysis and troubleshooting.

Step 1. Create a Custom Exception Middleware

First, we'll create a middleware that will handle exceptions globally.

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

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

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context); // Proceed to the next middleware
        }
        catch (Exception ex)
        {
            _logger.LogError($"Something went wrong: {ex}");
            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 = "Internal Server Error from the custom middleware."
        };

        return context.Response.WriteAsync(JsonConvert.SerializeObject(response));
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2. Register the Middleware in Program.cs

Now that the middleware is created, you need to register it in the application's middleware pipeline.

In ASP.NET Core 6 or later, your Program.cs might look something like this.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddLogging();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

// Add our custom exception middleware
app.UseMiddleware<ExceptionMiddleware>();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthorization();

app.MapControllers();

app.Run();
Enter fullscreen mode Exit fullscreen mode

Step 3. Customize Exception Handling Logic

You can customize the HandleExceptionAsync method to handle different types of exceptions and return more specific error messages or status codes. For example,

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

    // Customize status code based on exception type
    context.Response.StatusCode = exception switch
    {
        ArgumentNullException => (int)HttpStatusCode.BadRequest,
        UnauthorizedAccessException => (int)HttpStatusCode.Unauthorized,
        _ => (int)HttpStatusCode.InternalServerError
    };

    var response = new
    {
        StatusCode = context.Response.StatusCode,
        Message = exception.Message // Return the exception message
    };

    return context.Response.WriteAsync(JsonConvert.SerializeObject(response));
}
Enter fullscreen mode Exit fullscreen mode

Step 4. Implement Error Logging

Logging is crucial for monitoring and troubleshooting. You can enhance the ExceptionMiddleware class to include more detailed logging information.

public async Task InvokeAsync(HttpContext context)
{
    try
    {
        await _next(context);
    }
    catch (Exception ex)
    {
        _logger.LogError($"Exception: {ex.Message}, StackTrace: {ex.StackTrace}");
        await HandleExceptionAsync(context, ex);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 5. Testing the Middleware

To test the middleware, create a controller that throws an exception:

[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
    [HttpGet("error")]
    public IActionResult GetError()
    {
        throw new Exception("This is a test exception.");
    }
}
Enter fullscreen mode Exit fullscreen mode

When you navigate to /api/test/error, the exception will be handled by your custom middleware, and you'll see the custom error message in the response.

Conclusion

Centralized exception handling is an essential feature for modern web applications. By implementing a custom exception middleware, you can ensure that all exceptions are handled consistently across your entire ASP.NET Core application. This approach not only improves maintainability and security but also provides a single point of control for error handling and logging.

With the steps outlined in this blog post, you now have a foundation for building robust and user-friendly .NET applications that gracefully handle errors and provide meaningful feedback to users.

Top comments (0)