How can customize Asp.net Identity 2 username already taken validation message?

14,144

Solution 1

Well, I didn't find any simple solution to this issue. And by simple i mean modifying some message in a attribute/model/controller.

One possible solution could be:

After executing

var result = await UserManager.CreateAsync(user, model.Password);

In case that result is not successful you can check it's Errors property for the "Name XYZ is already taken." pattern and replace it with your custom message.

Another solution (this is my preferred way) is to write a custom UserValidation class:

 /// <summary>
    ///     Validates users before they are saved to an IUserStore
    /// </summary>
    /// <typeparam name="TUser"></typeparam>
    public class CustomUserValidator<TUser> : UserValidator<TUser, string>
        where TUser : ApplicationUser
    {
        /// <summary>
        ///     Constructor
        /// </summary>
        /// <param name="manager"></param>
        public CustomUserValidator(UserManager<TUser, string> manager) : base(manager)
        {
            this.Manager = manager;
        }

        private UserManager<TUser, string> Manager { get; set; }

        /// <summary>
        ///     Validates a user before saving
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public override async Task<IdentityResult> ValidateAsync(TUser item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            var errors = new List<string>();
            await ValidateUserName(item, errors);
            if (RequireUniqueEmail)
            {
                await ValidateEmail(item, errors);
            }
            if (errors.Count > 0)
            {
                return IdentityResult.Failed(errors.ToArray());
            }
            return IdentityResult.Success;
        }

        private async Task ValidateUserName(TUser user, List<string> errors)
        {
            if (string.IsNullOrWhiteSpace(user.UserName))
            {
                errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "Name"));
            }
            else if (AllowOnlyAlphanumericUserNames && !Regex.IsMatch(user.UserName, @"^[A-Za-z0-9@_\.]+$"))
            {
                // If any characters are not letters or digits, its an illegal user name
                errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.InvalidUserName, user.UserName));
            }
            else
            {
                var owner = await Manager.FindByNameAsync(user.UserName);
                if (owner != null && !EqualityComparer<string>.Default.Equals(owner.Id, user.Id))
                {
                    errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateName, user.UserName));
                }
            }
        }

        // make sure email is not empty, valid, and unique
        private async Task ValidateEmail(TUser user, List<string> errors)
        {
            if (!user.Email.IsNullOrWhiteSpace())
            {
                if (string.IsNullOrWhiteSpace(user.Email))
                {
                    errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "Email"));
                return;
                }
                try
                {
                    var m = new MailAddress(user.Email);
                }
                catch (FormatException)
                {
                    errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.InvalidEmail, email));
                return;
                }
            }
            var owner = await Manager.FindByEmailAsync(user.Email);
            if (owner != null && !EqualityComparer<string>.Default.Equals(owner.Id, user.Id))
            {
                errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateEmail, email));
            }
        }
    }

You can see that for all the validation error messages Resources being used, So by specifying a custom format in your resources you will be able to customize those messages.

You can register your validator in ApplicationUserManager class, Create method:

public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
{
   manager.UserValidator = new CustomUserValidator<ApplicationUser>(manager)
   {
       AllowOnlyAlphanumericUserNames = false,
       RequireUniqueEmail = true
   };
}

Solution 2

It can be done much easier than the accepted answer.

Add a class and inherit it from IdentityErrorDescriber

public class AppErrorDescriber : IdentityErrorDescriber
    {
        public override IdentityError DuplicateUserName(string userName)
        {
            var error = base.DuplicateUserName(userName);
            error.Description = "This email address has already been registered. Please log in.";
            return error;
        }
    }

Now use the new class in your Startup.cs and that's it.

services.AddDefaultIdentity<AppUser>(options => ... )
                .AddErrorDescriber<AppErrorDescriber>();

Solution 3

Just customize your AddErrors method like this:

private void AddErrors(IdentityResult result)
{
    foreach (var error in result.Errors)
    {
        if (error.StartsWith("Name"))
        {
            var NameToEmail= Regex.Replace(error,"Name","Email");
            ModelState.AddModelError("", NameToEmail);
        }
        else
        {
            ModelState.AddModelError("", error);
        }
    }
}
Share:
14,144

Related videos on Youtube

Nazmul Hossain
Author by

Nazmul Hossain

Hi, I’m a Full Stack .Net Developer with passion for delivering quality software that is easy to use, maintainable, and extensible. Love programming , watch movies , listen music Website : https://nazmulpro.com/ Blog : https://blog.nazmulpro.com/ Youtube : https://www.youtube.com/nazmulhossain

Updated on September 16, 2022

Comments

  • Nazmul Hossain
    Nazmul Hossain over 1 year

    How can i customize Asp.net Identity 2 username already taken validation message(Name XYZ is already taken.)? Thanks

    • Alex Art.
      Alex Art. over 9 years
      @qamar. In this particular case you are wrong because all the messages are embedded into Identity resources
  • Justin Skiles
    Justin Skiles over 8 years
    It is unbelievable that you are required to implement your own validator in order to change a string.
  • mellis481
    mellis481 about 8 years
    And I don't even see how you can replace the error message without creating a new IdentityResult since Errors is a read-only IEnumerable.
  • SandroRiz
    SandroRiz over 3 years
    Great solution!! PS if you have scaffolded the pages, service should be added in IdentityHostingStartup.cs
  • christophe.chapron
    christophe.chapron over 3 years
    Best answer by far