Search code examples
c#asp.net-corevalidationasp.net-core-webapifluentvalidation

FluentValidation does not validate List<string> when input property is List of objects


I have the following model (simplified for brevity)

public class AdvancedFilter
{
    public List<string> Values { get; set; }
}

Then my ApiController as following

    [HttpPost("test")]
    public AdvancedFilter Test(List<AdvancedFilter> filters)
    {
        return filters.First();
    }

Now my Validator looks as following

public class AdvancedFilterValidator : AbstractValidator<AdvancedFilter>
{
    public AdvancedFilterValidator()
    {
        RuleFor(x => x.Values).NotEmpty().ForEach(x => x.MinimumLength(20));
        //RuleForEach(x => x.Values).MinimumLength(20); (tried both approaches, none of them worked for me)
    }
}

When I try to call my endpoint with invalid paylaod such as

[
  {
     "values":["test"]
  }
]

It does not throw Validation exception back at me. Interestingly enough, when I change my controller to accept just a single object like this, the fluent validation suddenly kicks in as expected.

    [HttpPost("test")]
    public AdvancedFilter Test(AdvancedFilter filters)
    {
        return filters;
    }

I am using ASP Net Core v 6 with FluentValidation.AspNetCore version "11.3.0" (latest)

Bonus question: My end goal with this validation is to have ensure that all input values are numbers (even though they are strings, for certain cases I need to make sure they are int). Expanded model would shine more light to it (still simplified)

public class AdvancedFilter
{
    public AdvancedFilterColumn Column { get; set; }
    public List<string> Values { get; set; }
}

public class AdvancedFilterValidator : AbstractValidator<AdvancedFilter>
{
    public AdvancedFilterValidator()
    {
        When(x => x.Column == AdvancedFilterColumn.Jurisdiction, () =>
        {
            RuleForEach(x => x.Values).Must(x => int.TryParse(x, out _));
        });
    }
}

As you can see for specific case, when Column is Jurisdiction I need to make sure that passed in values are all numbers. I believe the validation rule is written correctly, yet I cannot test it (due to abovementioned problem).

Any help in respect to this matter would be highly appreciated.


Solution

  • Think that you need to build a validator for validating the AdvancedFilter collection.

    public class AdvancedFilterCollectionValidator : AbstractValidator<IEnumerable<AdvancedFilter>>
    {
        public AdvancedFilterCollectionValidator()
        {
            RuleForEach(x => x).SetValidator(new AdvancedFilterValidator());
        }
    }
    

    And register the validator service in the ConfigureServices method.

    public void ConfigureServices(IServiceCollection services) 
    {
        services.AddMvc();
    
        // Other configurations
    
        services.AddScoped<IValidator<AdvancedFilter>, AdvancedFilterValidator>();
        services.AddScoped<IValidator<IEnumerable<AdvancedFilter>>, AdvancedFilterCollectionValidator>();
    }
    

    Reference: Validation for a for Collection of a type at Root level?