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.
- Consistency: Ensure that all errors are handled uniformly.
- Maintainability: Easier to update and manage error-handling logic in one place.
- Security: Avoid exposing sensitive information by controlling what error details are returned to the client.
- 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));
}
}
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();
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));
}
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);
}
}
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.");
}
}
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.