using DigitalData.Core.Exceptions; using FluentValidation; using Microsoft.AspNetCore.Mvc; using ReC.Application.Common.Exceptions; using System.Net; using System.Text.Json; namespace ReC.API.Middleware; //TODO: Fix and use DigitalData.Core.Exceptions.Middleware /// /// Middleware for handling exceptions globally in the application. /// Captures exceptions thrown during the request pipeline execution, /// logs them, and returns an appropriate HTTP response with a JSON error details. /// [Obsolete("Use DigitalData.Core.Exceptions.Middleware")] public class ExceptionHandlingMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The next middleware in the request pipeline. /// The logger instance for logging exceptions. public ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } /// /// Invokes the middleware to handle the HTTP request. /// /// The HTTP context of the current request. /// A task that represents the asynchronous operation. public async Task InvokeAsync(HttpContext context) { try { await _next(context); // Continue down the pipeline } catch (Exception ex) { await HandleExceptionAsync(context, ex, _logger); } } /// /// Handles exceptions by logging them and writing an appropriate JSON response. /// /// The HTTP context of the current request. /// The exception that occurred. /// The logger instance for logging the exception. /// A task that represents the asynchronous operation. private static async Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger) { context.Response.ContentType = "application/json"; ValidationProblemDetails? details = null; switch (exception) { case BadRequestException badRequestEx: context.Response.StatusCode = (int)HttpStatusCode.BadRequest; details = new() { Title = "Bad Procedure", Detail = badRequestEx.Message }; break; case ValidationException validationEx: context.Response.StatusCode = (int)HttpStatusCode.BadRequest; var errors = validationEx.Errors .GroupBy(e => e.PropertyName, e => e.ErrorMessage) .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()); details = new ValidationProblemDetails() { Title = "Validation failed", Errors = validationEx.Errors .GroupBy(e => e.PropertyName, e => e.ErrorMessage) .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()), }; break; case NotFoundException notFoundEx: context.Response.StatusCode = (int)HttpStatusCode.NotFound; details = new() { Title = "Not Found", Detail = notFoundEx.Message }; break; case DataIntegrityException dataIntegrityEx: context.Response.StatusCode = (int)HttpStatusCode.Conflict; details = new() { Title = "Data Integrity Violation", Detail = dataIntegrityEx.Message }; break; case InsertObjectFailedException insertFailedEx: logger.LogError( insertFailedEx, "Insert operation failed during request processing. {procedure}", JsonSerializer.Serialize( insertFailedEx.Procedure, options: new() { WriteIndented = true } )); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; details = new() { Title = "Insert Operation Failed", Detail = insertFailedEx.Message }; break; default: logger.LogError(exception, "Unhandled exception occurred."); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; details = new() { Title = "Internal Server Error", Detail = "An unexpected error occurred. Please try again later." }; break; } if (details is not null) await context.Response.WriteAsJsonAsync(details); } }