FluentValidation/FluentValidation

The clientside part of a custom PropertyValidator doesn't run

Closed this issue · 2 comments

FluentValidation version

10.3.3

ASP.NET version

.NET 5

Summary

I have a custom validator with the following signature:

public class FileSizeValidator<T> : PropertyValidator<T, IFormFile>

and I tried to implement the client-side portion of it, so that it has parity with attribute-based validations where the client-side portion already worked. There's precious few leads on how would I ever start doing it, but I found #1314 (comment) which seemed not even all that outdated. Certainly more recent then Codeplex where my search led me.

So I made a simple client-side validator like so:

public class FileSizeClientValidator : ClientValidatorBase
{
    public FileSizeClientValidator(IValidationRule rule, IRuleComponent component) : base(rule, component)
    { }

    public override void AddValidation(ClientModelValidationContext context)
    {
        MergeAttribute(context.Attributes, "test-attribute", "test message");
    }
}

and registered it with

services
    .AddFluentValidation(options =>
    {
        options.ConfigureClientsideValidation(clientside =>
        {
            clientside.ClientValidatorFactories[typeof(FileSizeValidator<>)] = (_, rule, component) =>
                new FileSizeClientValidator(rule, component);
        });
    });

which seems to mostly follow what was contained in the comment linked above. Can't exactly get the generic parameter for my validator to cast it, because there seems to be nowhere to get it from, and some signatures changed since then, so I decided on a simpler test. Just a hardcoded attribute and message.

Alas, nothing shows up in the form. Worse still, neither the constructor nor the AddValidation method get hit at any point, the breakpoints placed there never trigger.

Steps to Reproduce

  1. Copy the code from summary
  2. Try to use it with a custom generic validator inheriting PropertyValidator<T1, T2>

Hi, you're on the right track.

Can't exactly get the generic parameter for my validator to cast it,

You should ensure your validator has a non-generic interface, and use that as the key. For example, our NotNullValidator<T,TProperty> implements a non-generic INotNullValidator marker interface: https://github.com/FluentValidation/FluentValidation/blob/main/src/FluentValidation/Validators/NotNullValidator.cs

This is then used in the client validator factories as the key:

{ typeof(INotNullValidator), (context, rule, component) => new RequiredClientValidator(rule, component) },

Thanks, the non-generic interface was the missing piece!