using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using Haoliang.Core.Services; using Haoliang.Models.Common; namespace Haoliang.Api.Filters { public class GlobalExceptionFilter : IExceptionFilter { private readonly ILogger _logger; private readonly ILoggingService _loggingService; public GlobalExceptionFilter(ILogger logger, ILoggingService loggingService) { _logger = logger; _loggingService = loggingService; } public void OnException(ExceptionContext context) { // Log the exception _logger.LogError(context.Exception, "An unhandled exception occurred"); await _loggingService.LogErrorAsync($"Unhandled exception: {context.Exception.Message}", context.Exception); // Handle specific exception types if (context.Exception is ValidationException) { context.Result = CreateValidationResult(context.Exception as ValidationException); context.ExceptionHandled = true; return; } if (context.Exception is NotFoundException) { context.Result = new NotFoundObjectResult(CreateErrorResponse("Resource not found", 404)); context.ExceptionHandled = true; return; } if (context.Exception is ForbiddenException) { context.Result = new ObjectResult(CreateErrorResponse("Access forbidden", 403)) { StatusCode = 403 }; context.ExceptionHandled = true; return; } if (context.Exception is BadRequestException) { context.Result = new BadRequestObjectResult(CreateErrorResponse("Bad request", 400)); context.ExceptionHandled = true; return; } // Handle model state validation errors if (!context.ModelState.IsValid) { context.Result = new BadRequestObjectResult(CreateValidationErrorResponse(context.ModelState)); context.ExceptionHandled = true; return; } // Default handling for unhandled exceptions context.Result = new ObjectResult(CreateErrorResponse("An unexpected error occurred", 500)) { StatusCode = 500 }; context.ExceptionHandled = true; } private IActionResult CreateValidationResult(ValidationException validationException) { var response = new ApiResponse> { Success = false, Message = "Validation failed", ErrorCode = 400, Data = validationException?.Errors as Dictionary ?? new Dictionary(), Timestamp = DateTime.Now }; return new BadRequestObjectResult(response); } private object CreateErrorResponse(string message, int errorCode) { return new ApiResponse { Success = false, Message = message, ErrorCode = errorCode, Timestamp = DateTime.Now }; } private object CreateValidationErrorResponse(ModelStateDictionary modelState) { var errors = new Dictionary(); foreach (var keyModelStatePair in modelState) { var key = keyModelStatePair.Key; var errorsArray = keyModelStatePair.Value.Errors.Select(error => error.ErrorMessage).ToArray(); if (errorsArray.Length > 0) { errors[key] = errorsArray; } } return new ApiResponse> { Success = false, Message = "Validation failed", ErrorCode = 400, Data = errors, Timestamp = DateTime.Now }; } } public class ModelStateValidationFilter : IActionFilter { private readonly ILogger _logger; public ModelStateValidationFilter(ILogger logger) { _logger = logger; } public void OnActionExecuting(ActionExecutingContext context) { if (!context.ModelState.IsValid) { var errors = new Dictionary(); foreach (var keyModelStatePair in context.ModelState) { var key = keyModelStatePair.Key; var errorsArray = keyModelStatePair.Value.Errors.Select(error => error.ErrorMessage).ToArray(); if (errorsArray.Length > 0) { errors[key] = errorsArray; } } _logger.LogWarning($"Model validation failed: {JsonSerializer.Serialize(errors)}"); context.Result = new BadRequestObjectResult(new ApiResponse> { Success = false, Message = "Validation failed", ErrorCode = 400, Data = errors, Timestamp = DateTime.Now }); } } public void OnActionExecuted(ActionExecutedContext context) { // No operation needed } } }