From 7cf9380556b2766dca5b08ea2621d85ccf40aeae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Henrique?= Date: Fri, 20 Jun 2025 23:11:35 -0300 Subject: [PATCH] error handling --- content/posts/error-handling-dotnet.md | 104 +++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 content/posts/error-handling-dotnet.md diff --git a/content/posts/error-handling-dotnet.md b/content/posts/error-handling-dotnet.md new file mode 100644 index 0000000..feae44e --- /dev/null +++ b/content/posts/error-handling-dotnet.md @@ -0,0 +1,104 @@ +--- +title: "Error Handling Dotnet" +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) \ No newline at end of file