using FluentValidation; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Mirea.Api.DataAccess.Application.Common.Exceptions; using Mirea.Api.Endpoint.Common.Exceptions; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Security; using System.Text.Json; using System.Threading.Tasks; namespace Mirea.Api.Endpoint.Configuration.Core.Middleware; public class CustomExceptionHandlerMiddleware(RequestDelegate next, ILogger logger) { public async Task InvokeAsync(HttpContext context) { try { await next(context); } catch (Exception exception) { await HandleExceptionAsync(context, exception); } } private Task HandleExceptionAsync(HttpContext context, Exception exception) { var traceId = Activity.Current?.Id ?? context.TraceIdentifier; var problemDetails = new ProblemDetails { Type = "https://tools.ietf.org/html/rfc9110#section-15.6", Title = "An unexpected error occurred.", Status = StatusCodes.Status500InternalServerError, Detail = exception.Message, Extensions = new Dictionary() { { "traceId", traceId } } }; switch (exception) { case ValidationException validationException: problemDetails.Status = StatusCodes.Status400BadRequest; problemDetails.Type = "https://tools.ietf.org/html/rfc9110#section-15.5.1"; problemDetails.Title = "Validation errors occurred."; problemDetails.Extensions = new Dictionary { { "errors", validationException.Errors.Select(e => e.ErrorMessage).ToArray() }, { "traceId", traceId } }; break; case NotFoundException: problemDetails.Status = StatusCodes.Status404NotFound; problemDetails.Type = "https://tools.ietf.org/html/rfc9110#section-15.5.4"; problemDetails.Title = "Resource not found."; break; case ControllerArgumentException: problemDetails.Status = StatusCodes.Status400BadRequest; problemDetails.Type = "https://tools.ietf.org/html/rfc9110#section-15.5.1"; problemDetails.Title = "Invalid arguments provided."; break; case SecurityException: problemDetails.Status = StatusCodes.Status401Unauthorized; problemDetails.Type = "https://tools.ietf.org/html/rfc9110#section-15.5.2"; problemDetails.Title = "Unauthorized access."; break; case ServerUnavailableException unavailableException: problemDetails.Status = StatusCodes.Status503ServiceUnavailable; problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.4"; problemDetails.Title = "Server unavailable."; problemDetails.Detail = unavailableException.Message; if (unavailableException.AddRetryAfter) context.Response.Headers.RetryAfter = "600"; break; } if (problemDetails.Status == StatusCodes.Status500InternalServerError) logger.LogError(exception, "Internal server error when processing the request"); context.Response.ContentType = "application/json"; context.Response.StatusCode = problemDetails.Status.Value; return context.Response.WriteAsync(JsonSerializer.Serialize(problemDetails)); } }