using Fitness_core_BE.Exceptions; using Fitness_core_BE.Models.IOs; namespace Fitness_core_BE.Helpers { // The app has no central exception->HTTP mapping today -- an unhandled NotFoundException/ // BadRequestException/etc. (or the equivalent BCL KeyNotFoundException/ArgumentException // many services throw) 500s with a raw stack trace instead of the ApiResponse shape every // other error path returns. AgentController.cs already hand-rolls this exact mapping // per-action (see its catch blocks); this middleware applies the same mapping once, for // every controller, without having to add a try/catch to each action. public class GlobalExceptionMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public GlobalExceptionMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (Exception ex) { var (statusCode, message) = ex switch { NotFoundException or KeyNotFoundException => (404, ex.Message), BadRequestException or ArgumentException => (400, ex.Message), UnauthorizedException => (401, ex.Message), ForbiddenException => (403, ex.Message), ServiceUnavailableException => (503, ex.Message), _ => (500, "An unexpected error occurred."), }; if (statusCode == 500) _logger.LogError(ex, "Unhandled exception for {Method} {Path}", context.Request.Method, context.Request.Path); if (context.Response.HasStarted) throw; context.Response.Clear(); context.Response.StatusCode = statusCode; context.Response.ContentType = "application/json"; await context.Response.WriteAsJsonAsync(new ApiResponse { StatusCode = statusCode, Message = message, Data = null! }); } } } }