using System.Net; using System.Text.Json; using FluentValidation; using Microsoft.AspNetCore.Mvc; namespace ProposalSystem.Api.Middleware; public class GlobalExceptionHandler : IMiddleware { private readonly ILogger _logger; public GlobalExceptionHandler(ILogger logger) { _logger = logger; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) { try { await next(context); } catch (Exception ex) { await HandleExceptionAsync(context, ex); } } private async Task HandleExceptionAsync(HttpContext context, Exception exception) { var (statusCode, problemDetails) = exception switch { ValidationException validationEx => ( HttpStatusCode.BadRequest, new ProblemDetails { Status = 400, Title = "Validation Error", Detail = string.Join("; ", validationEx.Errors.Select(e => e.ErrorMessage)), } ), KeyNotFoundException => ( HttpStatusCode.NotFound, new ProblemDetails { Status = 404, Title = "Not Found", Detail = exception.Message, } ), UnauthorizedAccessException => ( HttpStatusCode.Unauthorized, new ProblemDetails { Status = 401, Title = "Unauthorized", Detail = "Authentication required", } ), InvalidOperationException => ( HttpStatusCode.BadRequest, new ProblemDetails { Status = 400, Title = "Invalid Operation", Detail = exception.Message, } ), _ => ( HttpStatusCode.InternalServerError, new ProblemDetails { Status = 500, Title = "Internal Server Error", Detail = "An unexpected error occurred", } ), }; if (statusCode == HttpStatusCode.InternalServerError) { _logger.LogError(exception, "Unhandled exception"); } context.Response.StatusCode = (int)statusCode; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsync( JsonSerializer.Serialize(problemDetails, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, })); } }