--- title: ".NET - Proper API error handling" date: 2024-06-20T20:00:06-03:00 draft: false summary: "Because returning stack traces isn't secure." --- The main idea behind having centralized error handling is that we can process any unhandled exception to: * Return formatted responses without revealing any internal functionality * Process issues so that they are properly logged on logs or other monitoring systems (like Sentry) * Make sure all errors have the same external behavior For that, we will use a new middleware class: ```csharp public class ErrorResponse { public string Message { get; set; } } public class ErrorHandlerMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; private readonly IHostEnvironment _env; public ErrorHandlerMiddleware(RequestDelegate next, ILogger logger, IHostEnvironment env){ _next = next; _logger = logger; _env = env; } public async Task Invoke(HttpContext httpContext) { // Attempts to execute the next action on the http chain // If it fails, we log the exception and trigger the HandleErrorAsync method try { await _next(httpContext); } catch(Exception ex) { _logger.LogError(ex, "An unhandled exception has occurred: {Message}", ex.Message); await HandleErrorAsync(httpContext, ex); } } private async Task HandleErrorAsync(HttpContext context, Exception exception) { context.Response.ContentType = "application/json"; context.Response.StatusCode = (int) HttpStatusCode.InternalServerError; ErrorResponse errorResponse; if (_env.IsDevelopment()) { // In development, we want to see the full details for easier debugging. errorResponse = new ErrorResponse { Message = exception.ToString() }; } else { // In production, we return a generic message to avoid leaking details. errorResponse = new ErrorResponse { Message = "An internal server error occurred. Please try again later." }; } // We use the modern System.Text.Json for serialization via WriteAsJsonAsync await context.Response.WriteAsJsonAsync(errorResponse); } } ``` We will also define a new Extension class to register this middleware: ```csharp public static class ErrorHandlerExtensions { public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder appBuilder) { return appBuilder.UseMiddleware(); } } ``` And then we just configure it on the `Configure()` method at the startup: ```csharp public void Configure(IApplicationBuilder app) { app.UseErrorHandler(); } ``` Now, when there's an issue on the API execution, the API will return something like this: ```json { "Message": "Internal Server Error" } ``` Sources: * [https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-8.0](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-8.0)