using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Haoliang.Core.Services; using Haoliang.Models.Common; namespace Haoliang.Api.Filters { public class ValidationFilter : IActionFilter { private readonly ILoggingService _loggingService; public ValidationFilter(ILoggingService loggingService) { _loggingService = loggingService; } public void OnActionExecuting(ActionExecutingContext context) { // Check if model state is valid if (!context.ModelState.IsValid) { LogValidationErrors(context.ModelState); context.Result = CreateValidationErrorResponse(context.ModelState); } } public void OnActionExecuted(ActionExecutedContext context) { // No operation needed } private void LogValidationErrors(ModelStateDictionary modelState) { var errors = new Dictionary>(); foreach (var keyModelStatePair in modelState) { var key = keyModelStatePair.Key; var modelStateErrors = keyModelStatePair.Value.Errors; if (modelStateErrors.Count > 0) { errors[key] = modelStateErrors.Select(error => string.IsNullOrEmpty(error.ErrorMessage) ? "Validation error occurred" : error.ErrorMessage ).ToList(); } } _loggingService.LogWarningAsync($"Model validation failed: {JsonSerializer.Serialize(errors)}").Wait(); } private IActionResult CreateValidationErrorResponse(ModelStateDictionary modelState) { var errors = new Dictionary(); foreach (var keyModelStatePair in modelState) { var key = keyModelStatePair.Key; var errorsArray = keyModelStatePair.Value.Errors.Select(error => string.IsNullOrEmpty(error.ErrorMessage) ? "Validation error occurred" : error.ErrorMessage ).ToArray(); if (errorsArray.Length > 0) { errors[key] = errorsArray; } } var response = new ApiResponse> { Success = false, Message = "Validation failed", ErrorCode = 400, Data = errors, Timestamp = DateTime.Now }; return new BadRequestObjectResult(response); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class ValidateModelAttribute : ActionFilterAttribute { public override 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 => string.IsNullOrEmpty(error.ErrorMessage) ? "Validation error occurred" : error.ErrorMessage ).ToArray(); if (errorsArray.Length > 0) { errors[key] = errorsArray; } } var response = new ApiResponse> { Success = false, Message = "Validation failed", ErrorCode = 400, Data = errors, Timestamp = DateTime.Now }; context.Result = new BadRequestObjectResult(response); } base.OnActionExecuting(context); } } public class ValidationHelper { public static (bool IsValid, List Errors) ValidateModel(object model) { var validationContext = new ValidationContext(model); var validationResults = new List(); var isValid = Validator.TryValidateObject(model, validationContext, validationResults, true); var errors = validationResults.Select(vr => vr.ErrorMessage).ToList(); return (isValid, errors); } public static (bool IsValid, List Errors) ValidateDictionary(Dictionary data, Dictionary expectedTypes) { var errors = new List(); var isValid = true; foreach (var kvp in expectedTypes) { if (!data.ContainsKey(kvp.Key)) { errors.Add($"Missing required field: {kvp.Key}"); isValid = false; continue; } var value = data[kvp.Key]; var expectedType = kvp.Value; if (value == null) { if (expectedType == typeof(string)) { errors.Add($"Field {kvp.Key} cannot be empty"); isValid = false; } continue; } if (!expectedType.IsAssignableFrom(value.GetType())) { errors.Add($"Field {kvp.Key} must be of type {expectedType.Name}, got {value.GetType().Name}"); isValid = false; } } return (isValid, errors); } } }