When are validators executed

When is the validation evaluated? It would seem that there would be exception thrown with the validation errors before it started to try and authenticate the current user/password. But it seems this is not the case.

Service Class:

[Authenticate]
        public object Post(UpdatePassword request)
        {

            
            var userAuthRepo = base.TryResolve<IAuthRepository>();
            var currentUserAuth = (UserAuth)userAuthRepo.GetUserAuth(UserSession, null);

            IUserAuth verifyUserAuth;
            if(userAuthRepo.TryAuthenticate(UserSession.Email, request.CurrentPassword, out verifyUserAuth)) {
                userAuthRepo.UpdateUserAuth(currentUserAuth, currentUserAuth, request.NewPassword);
            }
            else
            {
                throw new ArgumentException("Current Password is invalid");
            }


            return new HttpResult(HttpStatusCode.OK);
        }




Validator:

  public class UpdatePasswordValidator: AbstractValidator<UpdatePassword>
    {
        public UpdatePasswordValidator()
        {
            RuleSet(ApplyTo.Post, () => {
                RuleFor(p => p.CurrentPassword).NotEmpty().WithMessage("Current Password is required");
                RuleFor(p => p.NewPassword).Length(8, 50).WithMessage("New Password must be at least 8 characters");
                RuleFor(p => p.NewPassword).Equal(p => p.ConfirmPassword).WithMessage("Passwords do not match");
                RuleFor(p => p.NewPassword).Matches("[a-z]").WithMessage("New Password must contain at least 1 lower case character");
                RuleFor(p => p.NewPassword).Matches("[A-Z]").WithMessage("New Password must contain at least 1 upper case character");
                RuleFor(p => p.NewPassword).Matches("[0-9]").WithMessage("New Password must contain at least 1 number");
            });
            
        }
    }

The validation filter is executed in a Global Request Filter before the Service is executed. The [Authenticate] attribute has a -100 priority so it’s executed before any Global Request Filters, it’s the lowest priority of all built-in Request Filter Attributes so it’s usually executed first.

Make sure the Validation Feature is registered as well as all assemblies containing your validators, e.g:

Plugins.Add(new ValidationFeature());

container.RegisterValidators(typeof(UpdatePasswordValidator).Assembly);

Note: the recommended API to access the IAuthRepository is base.AuthRepository.

So what you are saying is if I have everything wired up correctly it should never make it to this point in code unless the Validation passes:

var userAuthRepo = base.AuthRepository;
var currentUserAuth = (UserAuth)userAuthRepo.GetUserAuth(UserSession, null);

Right, when executed from a HTTP/MQ Request it should invoke the UpdatePasswordValidator.

Can you put a breakpoint on the RuleFor() line to see if it’s being executed?

I found the issue. Turns out FluentValidation will pass when NewPassword is empty.

This passes:

RuleFor(p => p.NewPassword).Length(8, 50);
RuleFor(p => p.NewPassword).Matches("[a-z]");
RuleFor(p => p.NewPassword).Matches("[A-Z]");
RuleFor(p => p.NewPassword).Matches("[0-9]");

I had to add this extra Rule in order for it to fail when NewPassword is empty:

RuleFor(p => p.NewPassword).NotEmpty();
etc,
2 Likes