From 612d8371d34bc0af4649467735e3f16292e0050f Mon Sep 17 00:00:00 2001 From: OlgunR Date: Tue, 21 Apr 2026 13:18:59 +0200 Subject: [PATCH] Improve exception handling with ProblemDetails responses ExceptionHandlingMiddleware now returns structured ProblemDetails responses for errors, including specific handling for InvalidOperationException (400 Bad Request) and general exceptions (500 Internal Server Error). Responses use "application/problem+json" content type and include trace IDs. ProblemDetails support is also registered in Program.cs. --- .../Middleware/ExceptionHandlingMiddleware.cs | 35 ++++++++++--------- DbFirst.API/Program.cs | 1 + 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/DbFirst.API/Middleware/ExceptionHandlingMiddleware.cs b/DbFirst.API/Middleware/ExceptionHandlingMiddleware.cs index 4957a94..369ed1b 100644 --- a/DbFirst.API/Middleware/ExceptionHandlingMiddleware.cs +++ b/DbFirst.API/Middleware/ExceptionHandlingMiddleware.cs @@ -1,5 +1,4 @@ -using System.Net; -using System.Text.Json; +using Microsoft.AspNetCore.Mvc; namespace DbFirst.API.Middleware; @@ -20,33 +19,35 @@ public class ExceptionHandlingMiddleware { await _next(context); } + catch (InvalidOperationException ex) + { + _logger.LogWarning(ex, "Domain validation error"); + await WriteProblemAsync(context, StatusCodes.Status400BadRequest, "Eingabe ungültig", ex.Message); + } catch (Exception ex) { _logger.LogError(ex, "Unhandled exception"); - await WriteProblemDetailsAsync(context, ex); + await WriteProblemAsync(context, StatusCodes.Status500InternalServerError, "Serverfehler", ex.Message); } } - private static async Task WriteProblemDetailsAsync(HttpContext context, Exception ex) + private static async Task WriteProblemAsync(HttpContext context, int status, string title, string detail) { - if (context.Response.HasStarted) - { - throw ex; - } + if (context.Response.HasStarted) return; context.Response.Clear(); - context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; - context.Response.ContentType = "application/json"; + context.Response.StatusCode = status; + context.Response.ContentType = "application/problem+json"; - var problem = new + var problem = new ProblemDetails { - type = "https://tools.ietf.org/html/rfc9110#section-15.6.1", - title = "Serverfehler", - status = context.Response.StatusCode, - detail = ex.Message, - traceId = context.TraceIdentifier + Type = $"https://tools.ietf.org/html/rfc9110#section-{(status == 400 ? "15.5.1" : "15.6.1")}", + Title = title, + Status = status, + Detail = detail, + Extensions = { ["traceId"] = context.TraceIdentifier } }; - await context.Response.WriteAsync(JsonSerializer.Serialize(problem)); + await context.Response.WriteAsJsonAsync(problem); } } diff --git a/DbFirst.API/Program.cs b/DbFirst.API/Program.cs index 3fd04b7..2ae820a 100644 --- a/DbFirst.API/Program.cs +++ b/DbFirst.API/Program.cs @@ -13,6 +13,7 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddProblemDetails(); // TODO: allow listed origins configured in appsettings.json // In any case, dont let them to free to use without cors. if there is no origin specified, block all.