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.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user