'FluentValidator custom Password is stopping at the first rule
I am using FluentValidator in an MVC project to validate view model passwords so this is my class:
namespace example123.Models
{
public class RegisterModel
{
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
}
public class RegisterValidator : AbstractValidator<RegisterModel>
{
public RegisterValidator()
{
RuleFor(x => x.Email)
.NotNull()
.NotEmpty()
.WithMessage("Email is required")
.EmailAddress()
.WithMessage("Invalid email format");
RuleFor(a => a.Password).Password();
}
}
}
So I created a custom rule and extended the rule with:
public static class RuleBuilderExtensions
{
public static IRuleBuilder<T, string> Password<T>(this IRuleBuilder<T, string> ruleBuilder, int minimumLength = 8)
{
var options = ruleBuilder
.NotEmpty().WithMessage("Required")
.MinimumLength(minimumLength).WithMessage("Length")
.Matches("[A-Z]").WithMessage("Uppercase msg")
.Matches("[a-z]").WithMessage("Lowercase msg")
.Matches("[0-9]").WithMessage("Digit msg")
.Matches("[^a-zA-Z0-9]").WithMessage("Sepcial char msg");
return options;
}
}
Rendered Html code:
<input type="password" class="form-control input-validation-error validate-equalTo-blur" autocomplete="current-password" aria-required="true" data-val="true" data-val-minlength="Password must be at least 6 characters long" data-val-minlength-min="6" data-val-regex="Uppercase" data-val-regex-pattern="[A-Z]" data-val-required="Password is required" id="Password" name="Password" aria-describedby="Password-error" aria-invalid="true">
The view Register.cshtml is using default jQuery-validate implementation that comes in default MVC project. Email is working fine but the password is stopping at the "Uppercase" rule [A-Z] and is not going away, other custom messages are not displayed. Any idea why?
Solution 1:[1]
I copied your code and wrote the following little xUnit test:
[Theory]
[InlineData("hello", false)]
[InlineData("helloworld", false)]
[InlineData("HelloWorld", false)]
[InlineData("HelloWorld1", false)]
[InlineData("HelloWorld1!", true)]
public void TestName(string password, bool expectedValidity)
{
// Arrange
var testee = new RegisterValidator();
// Act
var result = testee.Validate(new RegisterModel{Email = "[email protected]", Password = password});
// Assert
result.IsValid.Should().Be(expectedValidity);
}
All the tests are passing on my machine. So could you please check whether one of the test cases fails on your machine? If not, there must be a problem with the client logic (e. g. missing refresh).
Solution 2:[2]
So I am included a bunch of different options that might meet your needs.
Are you opposed to using the Must method of FluentValidation? If so it can look something like this:
Create a validation method:
private bool ValidatePassword(string pw)
{
var lowercase = new Regex("[a-z]+");
var uppercase = new Regex("[A-Z]+");
var digit = new Regex("(\\d)+");
var symbol = new Regex("(\\W)+");
return (lowercase.IsMatch(pw) && uppercase.IsMatch(pw) && digit.IsMatch(pw) && symbol.IsMatch(pw));
}
Then create the rule where you did your email one.
RuleFor(x => x.Password)
.Length(5, 15)
.Must(HasValidPassword);
}
And if you have a confirm password field you can do this too:
RuleFor(x => x.ConfirmPassword)
.Equal(x => x.Password)
.WithMessage("Passwords must match");
I believe the main issue is that you are doing this in an extensions which fluentvalidation can consider custom and not built-in, so it might run into some problems.
Only other thing I noticed is you could try changing:
.Matches("[A-Z]").WithMessage("Uppercase msg")
to
.Matches(@"[A-Z]+").WithMessage("Your password must contain at least one uppercase letter.")
.Matches(@"[a-z]+").WithMessage("Your password must contain at least one lowercase letter.")
.Matches(@"[0-9]+").WithMessage("Your password must contain at least one number.")
.Matches(@"[\!\?\*\.]+").WithMessage("Your password must contain at least one (!? *.).");
I added the @ and + for regex regular expression. That might help you too.
Not sure if fluentvalidation cares about the plus sign, but try taking it out of the extension method and doing a normal rule like you did for email and see if that solves it.
If you are trying to do client-side validation then I am pretty sure this won't work. The way you have it currently I believe is for server-side validation and not client. You could just use jQuery directly in the client too and that might meet your need for clien-side. Here is a quick example:
jQuery.validator.addMethod("passwordCheck",
function(value, element, param) {
if (this.optional(element)) {
return true;
} else if (!/[A-Z]/.test(value)) {
return false;
} else if (!/[a-z]/.test(value)) {
return false;
} else if (!/[0-9]/.test(value)) {
return false;
}
return true;
},
"error msg here");
and then attach it to attribute on view:
<input type="password" id="RegisterModel_Password"
name="RegisterModel.Password"
class="form-control"
required="required" minlength="8"
passwordCheck="passwordCheck"/>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | mu88 |
| Solution 2 | mathis1337 |
