ASP.NET Identity - setting UserValidator does noth

2019-08-05 10:03发布

I'm trying to set the UserValidator for the default ApplicationUserManager in a new ASP.NET MVC 5 project (using ASP.NET Identity 2). I've created a very simple UserValidator:

public class SimpleUserValidator<TUser, TKey> : IIdentityValidator<TUser> where TUser: class, IUser<TKey> where TKey : IEquatable<TKey> {
    private readonly UserManager<TUser, TKey> _manager;

    public SimpleUserValidator(UserManager<TUser, TKey> manager) {
        _manager = manager;
    }

    public async Task<IdentityResult> ValidateAsync(TUser item) {
        var errors = new List<string>();
        if (string.IsNullOrWhiteSpace(item.UserName))
            errors.Add("Username is required");

        if (_manager != null) {
            var otherAccount = await _manager.FindByNameAsync(item.UserName);
            if (otherAccount != null && !otherAccount.Id.Equals(item.Id))
                errors.Add("Select a different username. An account has already been created with this username.");
        }

        return errors.Any()
            ? IdentityResult.Failed(errors.ToArray())
            : IdentityResult.Success;
    }
}

I set this by calling:

manager.UserValidator = new SimpleUserValidator<ApplicationUser, int>(manager);

within the ApplicationUserManager.Create() method.

The problem is, this doesn't change the behavior. I still get the default The Email field is not a valid e-mail address message. Is this not the correct place to set that validation?

2条回答
地球回转人心会变
2楼-- · 2019-08-05 10:25

I had a similar issue to this question and the answer here is not quite right so I am adding mine here to share.

The issue was caused by setting the UserValidator inside the Create function. Since I was newing an instance of the ApplicationUserManager the default validator was being used. Moving the application validation setting to the constructor solved the problem. The Create function should only have the option specific settings I think.

public class ApplicationUserManager : UserManager<ApplicationUser, string>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="ApplicationUserManager"/> class.
    /// </summary>
    /// <param name="store"></param>
    public ApplicationUserManager(IUserStore<ApplicationUser, string> store)
        : base(store)
    {
        // Configure validation logic for usernames
        UserValidator = new ApplicationUserValidator<ApplicationUser>(this)
        {
            AllowOnlyAlphanumericUserNames = true,
            RequireUniqueEmail = false
        };

        // Configure validation logic for passwords
        PasswordValidator = new PasswordValidator
        {
            RequiredLength = 8,
            RequireNonLetterOrDigit = false,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true
        };
    }

    /// <summary>
    /// Creates the specified options.
    /// </summary>
    /// <param name="options">The options.</param>
    /// <param name="context">The context.</param>
    /// <returns></returns>
    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
    {
        var manager = new ApplicationUserManager(new ApplicationUserStore(context.Get<MyContext>()));
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
        }
        return manager;
    }
}

Alternatively you could remove all instances of new ApplicationUserManager, and call Create instead. But that is dificult to enforce on others using your code without making the constructor private

Making the constructor private would make seeding the user table difficult because the IOwinContext is not available in the seeding function. Also you would not be able to unit test your ApplicationUserManager by passing a mocked store during construction.

查看更多
看我几分像从前
3楼-- · 2019-08-05 10:42

I found it. And it should have been obvious.

The UserValidator is not the only thing you have to change for validation from a new template. You also must change all the relevant view models. Go figure.

查看更多
登录 后发表回答