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
- Consistency: Ensures a uniform approach to error handling across the application.
- Maintainability: Simplifies maintenance by consolidating error handling logic in one place.
- Logging: Provides a centralized location for logging errors, making it easier to monitor and debug issues.
- 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.
Dream big, because at WireFuture, no vision is too ambitious. Our team is passionate about turning your software dreams into reality, with custom solutions that exceed expectations.
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.