Search code examples
c#.netasp.net-corefluentvalidation

Auto API Validation with FluentValidation


I want to auto apply validation for some models on requests execution. E.g. if I have a fluent validator for model A it will apply, if I don't have validator for model B then nothing happens. I wrote code, but maybe somebody can advise a better solution.


Solution

  • Create base class and interface:

    public interface IBaseValidationModel
    {
        public void Validate(object validator, IBaseValidationModel modelObj);
    }
    
    public abstract class BaseValidationModel<T> : IBaseValidationModel
    {
        public void Validate(object validator, IBaseValidationModel modelObj)
        {
            var instance = (IValidator<T>)validator;
            var result = instance.Validate((T)modelObj);
        
            if (!result.IsValid && result.Errors.Any())
            {
                throw new Exception("INVALID");
            }
        }
    }
    

    Create model and validator:

    public class LogInModel : BaseValidationModel<LogInModel>
    {
        public string? Name { get; set; }
        public string? Password { get; set; }
        public string? Domain { get; set; }
    }
    
    public class LogInModelValidator : AbstractValidator<LogInModel>
    {
        public LogInModelValidator()
        {
            RuleLevelCascadeMode = CascadeMode.Stop;
    
            RuleFor(x => x.Domain).NotNull().NotEmpty();
            RuleFor(x => x.Name).NotNull().NotEmpty();
            RuleFor(x => x.Password).NotNull().NotEmpty();
        }
    }
    

    Create action filter attribute to apply it to controllers:

    public class ModelValidatorAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            foreach (var actionArgument in context.ActionArguments)
            {
                //validate that model is having validator and resolve it
                if (actionArgument.Value is IBaseValidationModel model)
                {
                    var modelType = actionArgument.Value.GetType();
                    var genericType = typeof(IValidator<>).MakeGenericType(modelType);
                    var validator = context.HttpContext.RequestServices.GetService(genericType);
    
                    if (validator != null)
                    {
                        // execute validator to validate model
                        model.Validate(validator, model);
                    }
                }
            }
            
            base.OnActionExecuting(context);
        }
    }
    

    Controller:

    [Route("account")]
    [ModelValidator]
    public class AccountController : ControllerBase
    {
       public IActionResult Post([FromBody]LogInModel model)
       {
          return Ok("ok);
       }
    }