【问题标题】:Override ValidateAsync in UserValidator.cs for .NET Core Identity 2.1为 .NET Core Identity 2.1 覆盖 UserValidator.cs 中的 ValidateAsync
【发布时间】:2018-06-10 17:40:49
【问题描述】:

我正在自定义用户名验证以允许相同的用户名(非唯一)。这是一个附加字段“已删除”,作为对身份用户的软删除。因此自定义涉及更改当前验证以检查用户名是否已存在并且已删除为 false 以仅触发 DuplicateUserName 错误。

我所做的是创建一个 CustomUserValidator 类,并覆盖 UserValidator.cs 中的 ValidateAsync 方法以及 ValidateUserName 方法。下面是代码:

CustomUserValidator.cs

public class CustomUserValidator<TUser> : UserValidator<TUser>
    where TUser : ApplicationUser
{
    public override async Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
    {
        if (manager == null)
        {
            throw new ArgumentNullException(nameof(manager));
        }
        if (user == null)
        {
            throw new ArgumentNullException(nameof(user));
        }
        var errors = new List<IdentityError>();
        await ValidateUserName(manager, user, errors);
        if (manager.Options.User.RequireUniqueEmail)
        {
            await ValidateEmail(manager, user, errors);
        }
        return errors.Count > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
    }

    private async Task ValidateUserName(UserManager<TUser> manager, TUser user, ICollection<IdentityError> errors)
    {
        var userName = await manager.GetUserNameAsync(user);
        if (string.IsNullOrWhiteSpace(userName))
        {
            errors.Add(Describer.InvalidUserName(userName));
        }
        else if (!string.IsNullOrEmpty(manager.Options.User.AllowedUserNameCharacters) &&
            userName.Any(c => !manager.Options.User.AllowedUserNameCharacters.Contains(c)))
        {
            errors.Add(Describer.InvalidUserName(userName));
        }
        else
        {
            //var owner = await manager.FindByNameAsync(userName);
            var owner = manager.Users.Where(x => !x.Deleted &&
                x.UserName.ToUpper() == userName.ToUpper())
                .FirstOrDefault();
            if (owner != null &&
                !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
            {
                errors.Add(Describer.DuplicateUserName(userName));
            }
        }
    }
}

在 Startup.cs 中

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IUserValidator<ApplicationUser>, CustomUserValidator<ApplicationUser>>();
}

CustomUserValidator 中的 ValidateAsync 方法中的代码工作正常,但似乎原始 ValidateAsync 也在运行。我之所以这么说是因为:

  1. 调试时,DuplicateUserName() 没有被调用,但仍然收到重复用户名错误。
  2. 通过放置特殊字符测试其他用户名验证。验证失败,特殊字符不允许出现两次错误!

我在这里做错了什么或错过了什么? 提前致谢。

【问题讨论】:

    标签: validation asp.net-core-mvc asp.net-identity


    【解决方案1】:

    首先让我解释一下问题

    原因是Identity库注入了使用默认库UserValidator的验证用户类。

    解决方案只是注入一个 CustomUserValidator。发生的情况是,如果将其注入到正常的实现中,它所做的就是添加 2 个 UserValidator,第一个是身份库的默认值,第二个是您实现 CustomUserIdentity 的那个。

    然后要仅注入 CustomUserIdentity,您必须创建一个新的 CustomUserManager 才能注入新的 ICustomUserValidator,这样它就不会采用默认的 IUserValidator。

    这是我的解决方案:

    这是接口 ICustomUserValidator

        public interface ICustomUserValidator<TUser> : IUserValidator<TUser> where TUser : ApplicationUser
    {
    }
    

    以及类的实现

    public class CustomUserValidator<TUser> : UserValidator<TUser>, ICustomUserValidator<TUser>
        where TUser : ApplicationUser
    {
    
        public async Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
        {
            //Some Code
        }
    
        private async Task ValidateUserName(UserManager<TUser> manager, TUser user, ICollection<IdentityError> errors)
        {
            //Some Code
        }
    }
    

    还有这个用于 CustomUserManager

        public class CustomUserManager<TUser> : UserManager<TUser>
                where TUser : ApplicationUser
    {
        public CustomUserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor,
            IPasswordHasher<TUser> passwordHasher, IEnumerable<ICustomUserValidator<TUser>> userValidators,
            IEnumerable<IPasswordValidator<TUser>> passwordValidators, ILookupNormalizer keyNormalizer,
            IdentityErrorDescriber errors, IServiceProvider tokenProviders,
            ILogger<UserManager<TUser>> logger)
            : base(
                store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors,
                tokenProviders, logger)
        {
        }
    }
    

    请注意,我使用 ICustomUserValidator

    代替了 IUserValidator

    在 Startup 类中你必须注入新类:

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddUserManager<CustomUserManager<ApplicationUser>>()
    

    最后注入这个类

                services.AddTransient<ICustomUserValidator<ApplicationUser>, CustomUserValidator<ApplicationUser>>();
    

    我希望这个实现对你有所帮助。

    【讨论】:

    • 就我而言,您提到的代码没有被触发。因为我是使用软删除的用户名创建的,该用户名也可以正常工作。所以注释掉代码与否,对我来说不会有什么不同。但是,正如我的第 1 项所述,DuplicateUserName() 仍然被触发,尽管在我调试时并非如此。
    • 那太好了@Christopher,期待您的解决方案。
    • 它确实有效。我也在尝试这些方面的东西,但无法让它发挥作用。非常感谢您的帮助。
    • 快乐是我的
    • 感谢您的解释和正确的方法。
    【解决方案2】:

    Core 3.1 中的语法略有不同:

    services.AddTransient<IUserValidator<ApplicationUser>, CustomUserValidator>();
    

    CustomUserValidator 从标准用户验证器(in the source here)插入。如果您要删除验证,这是必要的。请注意,某些验证(例如用户名的唯一性)由数据库约束支持,因此您可能还需要调整它们。

    如果您想简单地添加更多验证,那么您可以将自己的验证器作为附加服务添加到 startup.cs 中的 Identity。

    【讨论】:

    • 这仍然不会替换现有的用户验证器,而是添加一个自定义的。旧验证器仍会按照已接受答案中的说明进行验证。此外语法没有改变一点,您需要以相同的方式注册。这个答案具有误导性。
    • 问题是“覆盖”,我确信我不需要解释“继承”是什么意思。语法对我有用。
    • 是覆盖行为而不是方法。从技术上讲,您覆盖了一个方法,但实际上您的解决方案只实现了它。您可以使用基类来实现,但您不会覆盖现有行为的实际实现,因此您的解决方案会误导您所说的任何内容
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-11
    相关资源
    最近更新 更多