asp.net-identity - net - identity framework c#
¿Cómo localizar los mensajes de error de nombre de usuario y contraseña de identidad de ASP.NET? (14)
A partir de la versión 2 de identidad que se lanzó el 20 de marzo de 2014, ahora puede tener mensajes de error localizados.
Primero instale el paquete localizado de identidad.
Install-Package Microsoft.AspNet.Identity.Core.fr
Entonces se debe establecer la cultura adecuada para obtener mensajes localizados, por ejemplo, una forma de establecer la cultura es en web.config
<system.web>
<globalization culture="fr-FR" uiCulture="fr"/>
</system.web>
¿Es posible proporcionar cadenas localizadas para el "sistema"? Los mensajes de error de ASP.NET Identity v1, como " Nombre XYZ ya está tomado " o " El nombre de usuario XYZ no es válido, solo puede contener letras o dígitos ".
El método de 1AmirJalali funciona perfectamente. Al configurar la cultura en web.config funciona, también se puede configurar dinámicamente si es necesario, así es cómo se puede lograr esto:
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
En el ejemplo de código anterior, cultureName, es el nombre de la cultura que se establecerá. Puede encontrar una lista de culturas en el encabezado "Observaciones" aquí: http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo%28v=vs.80%29.aspx
Espero que esto ayude.
Este problema me estaba molestando desde hace bastante tiempo, así que traté de encontrar algo genérico, porque la solución con la instalación de Identity.Core para una cultura en particular solo funciona si el paquete está disponible :)
Estos son los pasos de cómo logré la localización correcta:
- Miró la implementación de
Microsoft.AspNet.Identity.PasswordValidator
usando dotPeak que se usa por defecto enApplicationUserManager
, - Entonces se me ocurrió mi propia implementación de la
class CustomPasswordValidator : PasswordValidator
y anulé el métodoValidateAsync
para no devolverIdentityResult
con mensajes de error, sino más bien con "códigos" de estos mensajes de error, todos ellos con el prefijo "CustomValidator". - Luego, en
AccountController
cambiéAddErrors
que se llama desde la publicaciónRegister
para proporcionar mensajes de error localizados de mis propios archivos de recursos basados en códigos de error.
Aquí está el CustomPasswordValidator
y la clase de utilidad PasswordValidatorCodes
contienen códigos de error y un método para recuperar los mensajes de error localizados basados en estos códigos (AccountStrings es un archivo de recursos):
public static class PasswordValidatorCodes
{
public const string ErrorCodePrefix = "CustomPassword";
public const string PasswordTooShort = ErrorCodePrefix + "TooShort";
public const string PasswordRequireNonLetterOrDigit = ErrorCodePrefix + "RequireNonLetterOrDigit";
public const string PasswordRequireDigit = ErrorCodePrefix + "RequireDigit";
public const string PasswordRequireLower = ErrorCodePrefix + "RequireLower";
public const string PasswordRequireUpper = ErrorCodePrefix + "RequireUpper";
public static string GetLocalizedMessageForCode(string code)
{
switch (code)
{
case PasswordTooShort:
return string.Format(AccountStrings.ValidationPasswordTooShort, CustomPasswordValidator.RequiredPasswordLength);
case PasswordRequireNonLetterOrDigit:
return AccountStrings.ValidationPasswordRequireNonLetterOrDigit;
case PasswordRequireDigit:
return AccountStrings.ValidationPasswordRequireDigit;
case PasswordRequireLower:
return AccountStrings.ValidationPasswordRequireLower;
case PasswordRequireUpper:
return AccountStrings.ValidationPasswordRequireUpper;
default:
throw new ArgumentException("code");
}
}
}
public class CustomPasswordValidator : PasswordValidator
{
public const int RequiredPasswordLength = 6;
public CustomPasswordValidator()
{
RequiredLength = RequiredPasswordLength;
RequireNonLetterOrDigit = false;
RequireDigit = true;
RequireLowercase = true;
RequireUppercase = false;
}
public override Task<IdentityResult> ValidateAsync(string item)
{
if (item == null) throw new ArgumentNullException("item");
var list = new List<string>();
if (string.IsNullOrWhiteSpace(item) || item.Length < RequiredLength)
{
list.Add(PasswordValidatorCodes.PasswordTooShort);
}
if (RequireNonLetterOrDigit && item.All(IsLetterOrDigit))
{
list.Add(PasswordValidatorCodes.PasswordRequireNonLetterOrDigit);
}
if (RequireDigit && item.All(c => !IsDigit(c)))
{
list.Add(PasswordValidatorCodes.PasswordRequireDigit);
}
if (RequireLowercase && item.All(c => !IsLower(c)))
{
list.Add(PasswordValidatorCodes.PasswordRequireLower);
}
if (RequireUppercase && item.All(c => !IsUpper(c)))
{
list.Add(PasswordValidatorCodes.PasswordRequireUpper);
}
return Task.FromResult(list.Count == 0
? IdentityResult.Success
: new IdentityResult(list));
}
}
Y aquí está el método modificado AccountController.AddErrors
. Como puede ver, agrego un error al modelo para la Password
propiedad porque si la Password
no pasa la validación, entonces quiero que el mensaje de error se muestre en el campo Password
y no en el resumen de validación. Esta es la única razón por la cual CustomPasswordValidator
produce códigos de error en lugar de mensajes de error directamente:
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
if (error.StartsWith(PasswordValidatorCodes.ErrorCodePrefix))
{
ModelState.AddModelError("Password", PasswordValidatorCodes.GetLocalizedMessageForCode(error));
continue;
}
ModelState.AddModelError(string.Empty, error);
}
}
Por último, pero no menos importante, no olvide modificar el archivo IdentityConfig
, es decir:
manager.PasswordValidator = new CustomPasswordValidator();
ACTUALIZAR:
Solo ahora noté que Marselus Chia (ver otras respuestas) ha presentado la solución similar, pero para VB.
Me encontré con el mismo problema y encontré una solución rápida y sucia. Busqué dentro del ensamblado Microsoft.AspNet.Identity.Core
usando DotPeek (supongo que cualquier descompilador de C # lo hará) y encontré dos clases que son responsables de dichos mensajes:
-
Microsoft.AspNet.Identity.UserValidator<TUser>
(UserName-related) -
Microsoft.AspNet.Identity.MinimumLengthValidator
(relacionado con la contraseña)
Estas clases no contienen ninguna referencia externa (aparte de los recursos de cadena que desea reemplazar de todos modos), por lo que volver a implementarlos en su propio código debería ser bastante sencillo.
Después de hacerlo, no olvide usarlos correctamente en su UserManager
:
UserManager.UserValidator = new MyCustomUserValidator<MyUserType>(UserManager);
UserManager.PasswordValidator = new MyCustomMinimumLengthValidator(6);
Mi solución es crear una nueva clase de validación de contraseñas personalizadas heredadas de Microsoft.AspNet.Identity.PasswordValidator, anula ValidateAsync, que es la que envía el mensaje de error. Personalice el mensaje de error aquí y funciona para mí ...
Estas clases suponen que tiene un validador de nombres de archivos de recursos globales. La secuencia de comandos de esta clase y la muestra del archivo de recursos se puede encontrar aquí Identidad de ASP.NET - Código de ejemplo de Codeplex
Aquí está mi código de trabajo de muestra en vb.net
Imports System.Threading.Tasks
Imports Microsoft.AspNet.Identity
Namespace MyNameSpace
Public Class MyPasswordValidator
Inherits PasswordValidator
Public Overrides Function ValidateAsync(item As String) As Task(Of IdentityResult)
Dim errorMessage As String = String.Empty
Dim bolminChar As Boolean = True
Dim bolminDigit As Boolean = True
Dim bolminLcase As Boolean = True
Dim bolminUCase As Boolean = True
Dim bolminNonAlfanum As Boolean = True
If Not String.IsNullOrWhiteSpace(item) AndAlso item.Length >= Me.RequiredLength Then
bolminChar = True
Else
bolminChar = False
errorMessage = String.Format(CultureInfo.CurrentCulture, Resources.validator.PasswordTooShort & "<br/>", RequiredLength)
End If
If Me.RequireDigit Then
Dim regex As New Regex("^(?=.*/d).+$")
Dim match As Match = regex.Match(item)
If match.Success Then
bolminDigit = True
Else
bolminDigit = False
errorMessage &= Resources.validator.PasswordRequireDigit & ".<br/>"
End If
End If
If Me.RequireLowercase Then
Dim LCrex As New Regex("^(?=.*[a-z]).+$")
Dim LCMatch As Match = LCrex.Match(item)
If LCMatch.Success Then
bolminLcase = True
Else
bolminLcase = False
errorMessage &= Resources.validator.PasswordRequireLower & "<br/>"
End If
End If
If Me.RequireNonLetterOrDigit Then
Dim NAFRex As New Regex("^(?=.*[-+_!@#$%=^/[/]/{/}()&*.,?]).+$")
Dim NAFMatch As Match = NAFRex.Match(item)
If NAFMatch.Success Then
bolminNonAlfanum = True
Else
bolminNonAlfanum = False
errorMessage &= Resources.validator.PasswordRequireNonLetterOrDigit & "<br/>"
End If
End If
If Me.RequireUppercase Then
Dim UCrex As New Regex("^(?=.*[A-Z]).+$")
Dim UCMatch As Match = UCrex.Match(item)
If UCMatch.Success Then
bolminUCase = True
Else
bolminUCase = False
errorMessage &= Resources.validator.PasswordRequireUpper & "<br/>"
End If
End If
If bolminChar And bolminDigit And bolminLcase And bolminNonAlfanum And bolminUCase Then
Return Task.FromResult(Of IdentityResult)(IdentityResult.Success)
Else
Return Task.FromResult(Of IdentityResult)(IdentityResult.Failed(New String() {errorMessage}))
End If
''Return MyBase.ValidateAsync(item)
End Function
End Class
Public Class MyUserValidator
Inherits UserValidator(Of applicationUser, int64)
Private _manager As ApplicationUserManager
Public Sub New(manager As ApplicationUserManager)
MyBase.New(manager)
_manager = manager
End Sub
Public Overrides Function ValidateAsync(item As applicationUser) As Task(Of IdentityResult)
If item Is Nothing Then
Throw New ArgumentNullException("item")
End If
Dim errors As New List(Of String)()
Validateusername(item, errors)
If Me.RequireUniqueEmail Then
ValidateEmail(item, errors)
End If
If errors.Count > 0 Then
Return Task.FromResult(Of IdentityResult)(IdentityResult.Failed(errors.ToArray()))
End If
Return Task.FromResult(Of IdentityResult)(IdentityResult.Success)
End Function
Private Sub Validateusername(User As applicationUser, errors As List(Of String))
If String.IsNullOrWhiteSpace(User.UserName) Then
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.validator.PropertyTooShort, "Email")) ''
ElseIf Me.AllowOnlyAlphanumericUserNames AndAlso Not Regex.IsMatch(User.UserName, "^[A-Za-z0-9@_/.]+$") Then
'' If any characters are not letters or digits, its an illegal user name
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.validator.InvalidUserName, User.UserName))
Else
Dim owner = _manager.FindByName(User.UserName)
If owner IsNot Nothing AndAlso Not EqualityComparer(Of applicationUser).ReferenceEquals(owner.Id, User.Id) Then
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.validator.DuplicateName, User.UserName))
End If
End If
End Sub
Private Sub ValidateEmail(User As ApplicationUser, errors As List(Of String))
Dim email As String = String.Empty
If User.Id > 0 Then
email = _manager.GetEmail(User.Id)
End If
If String.IsNullOrWhiteSpace(email) Then
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.validator.PropertyTooShort, "Email"))
Return
End If
Try
Dim m As New MailAddress(email)
Catch ex As Exception
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.validator.InvalidEmail, email))
Return
End Try
Dim owner = _manager.FindByEmail(email)
If owner IsNot Nothing AndAlso Not EqualityComparer(Of ApplicationUser).ReferenceEquals(owner.Id, User.Id) Then
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.validator.DuplicateName, email))
End If
End Sub
End Class
Remember to update the setting in your identityConfig.vb file to used these class.
No quiero anular la lógica (es decir, reinventar la rueda), ni tampoco quiero cambiar a ASP NET Core. Todo lo que quiero hacer es anular los mensajes de error y usar varios archivos de recursos (por idioma) para adaptar la experiencia del usuario a su idioma elegido.
Aquí está mi solución:
Creé un archivo de recursos ''IdentityErrors.resx'' con lo siguiente:
<data name="DefaultError" xml:space="preserve"> <value>An unknown failure has occured.</value> </data> <data name="DuplicateEmail" xml:space="preserve"> <value>Email ''{0}'' is already taken.</value> </data> <data name="DuplicateName" xml:space="preserve"> <value>Name {0} is already taken.</value> </data> <data name="ExternalLoginExists" xml:space="preserve"> <value>A user with that external login already exists.</value> </data> <data name="InvalidEmail" xml:space="preserve"> <value>Email ''{0}'' is invalid.</value> </data> <data name="InvalidToken" xml:space="preserve"> <value>Invalid token.</value> </data> <data name="InvalidUserName" xml:space="preserve"> <value>User name {0} is invalid, can only contain letters or digits.</value> </data> <data name="LockoutNotEnabled" xml:space="preserve"> <value>Lockout is not enabled for this user.</value> </data> <data name="NoTokenProvider" xml:space="preserve"> <value>No IUserTokenProvider is registered.</value> </data> <data name="NoTwoFactorProvider" xml:space="preserve"> <value>No IUserTwoFactorProvider for ''{0}'' is registered.</value> </data> <data name="PasswordMismatch" xml:space="preserve"> <value>Incorrect password.</value> </data> <data name="PasswordRequireDigit" xml:space="preserve"> <value>Passwords must have at least one digit (''0''-''9'').</value> </data> <data name="PasswordRequireLower" xml:space="preserve"> <value>Passwords must have at least one lowercase (''a''-''z'').</value> </data> <data name="PasswordRequireNonLetterOrDigit" xml:space="preserve"> <value>Passwords must have at least one non letter or digit character.</value> </data> <data name="PasswordRequireUpper" xml:space="preserve"> <value>Passwords must have at least one uppercase (''A''-''Z'').</value> </data> <data name="PasswordTooShort" xml:space="preserve"> <value>Passwords must be at least {0} characters.</value> </data> <data name="PropertyTooShort" xml:space="preserve"> <value>{0} cannot be null or empty.</value> </data> <data name="RoleNotFound" xml:space="preserve"> <value>Role {0} does not exist.</value> </data> <data name="StoreNotIQueryableRoleStore" xml:space="preserve"> <value>Store does not implement IQueryableRoleStore&lt;TRole&gt;.</value> </data> <data name="StoreNotIQueryableUserStore" xml:space="preserve"> <value>Store does not implement IQueryableUserStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserClaimStore" xml:space="preserve"> <value>Store does not implement IUserClaimStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserConfirmationStore" xml:space="preserve"> <value>Store does not implement IUserConfirmationStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserEmailStore" xml:space="preserve"> <value>Store does not implement IUserEmailStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserLockoutStore" xml:space="preserve"> <value>Store does not implement IUserLockoutStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserLoginStore" xml:space="preserve"> <value>Store does not implement IUserLoginStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserPasswordStore" xml:space="preserve"> <value>Store does not implement IUserPasswordStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserPhoneNumberStore" xml:space="preserve"> <value>Store does not implement IUserPhoneNumberStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserRoleStore" xml:space="preserve"> <value>Store does not implement IUserRoleStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserSecurityStampStore" xml:space="preserve"> <value>Store does not implement IUserSecurityStampStore&lt;TUser&gt;.</value> </data> <data name="StoreNotIUserTwoFactorStore" xml:space="preserve"> <value>Store does not implement IUserTwoFactorStore&lt;TUser&gt;.</value> </data> <data name="UserAlreadyHasPassword" xml:space="preserve"> <value>User already has a password set.</value> </data> <data name="UserAlreadyInRole" xml:space="preserve"> <value>User already in role.</value> </data> <data name="UserIdNotFound" xml:space="preserve"> <value>UserId not found.</value> </data> <data name="UserNameNotFound" xml:space="preserve"> <value>User {0} does not exist.</value> </data> <data name="UserNotInRole" xml:space="preserve"> <value>User is not in role.</value> </data>
Creé una clase ''IdentityResultErrorMessages.cs'':
public class IdentityResultErrorMessages { public static List<String> GetResourceEquivalent(IEnumerable<string> errors) { List<String> errorList = new List<String>(); if (errors != null) { foreach (String error in errors) { if (error.StartsWith("An unknown failure has occured.")) { errorList.Add(IdentityErrors.DefaultError); } else if (error.StartsWith("Email ''") && error.EndsWith("'' is already taken.")) { errorList.Add(String.Format(IdentityErrors.DuplicateEmail, error.Replace("Email ''", "").Replace("'' is already taken.", ""))); } else if (error.StartsWith("Name ") && error.EndsWith(" is already taken.")) { errorList.Add(String.Format(IdentityErrors.DuplicateName, error.Replace("Name ", "").Replace(" is already taken.", ""))); } else if (error.StartsWith("A user with that external login already exists.")) { errorList.Add(IdentityErrors.ExternalLoginExists); } else if (error.StartsWith("Email ''") && error.EndsWith("'' is invalid.")) { errorList.Add(String.Format(IdentityErrors.InvalidEmail, error.Replace("Email ''", "").Replace("'' is invalid.", ""))); } else if (error.StartsWith("Invalid token.")) { errorList.Add(IdentityErrors.InvalidToken); } else if (error.StartsWith("User name ") && error.EndsWith(" is invalid, can only contain letters or digits.")) { errorList.Add(String.Format(IdentityErrors.InvalidUserName, error.Replace("User name ", "").Replace(" is invalid, can only contain letters or digits.", ""))); } else if (error.StartsWith("Lockout is not enabled for this user.")) { errorList.Add(IdentityErrors.LockoutNotEnabled); } else if (error.StartsWith("No IUserTokenProvider is registered.")) { errorList.Add(IdentityErrors.NoTokenProvider); } else if (error.StartsWith("No IUserTwoFactorProvider for ''") && error.EndsWith("'' is registered.")) { errorList.Add(String.Format(IdentityErrors.NoTwoFactorProvider, error.Replace("No IUserTwoFactorProvider for ''", "").Replace("'' is registered.", ""))); } else if (error.StartsWith("Incorrect password.")) { errorList.Add(IdentityErrors.PasswordMismatch); } else if (error.StartsWith("Passwords must have at least one digit (''0''-''9'').")) { errorList.Add(IdentityErrors.PasswordRequireDigit); } else if (error.StartsWith("Passwords must have at least one lowercase (''a''-''z'').")) { errorList.Add(IdentityErrors.PasswordRequireLower); } else if (error.StartsWith("Passwords must have at least one non letter or digit character.")) { errorList.Add(IdentityErrors.PasswordRequireNonLetterOrDigit); } else if (error.StartsWith("Passwords must have at least one uppercase (''A''-''Z'').")) { errorList.Add(IdentityErrors.PasswordRequireUpper); } else if (error.StartsWith("Passwords must be at least ") && error.EndsWith(" characters.")) { errorList.Add(String.Format(IdentityErrors.PasswordTooShort, error.Replace("Passwords must be at least ", "").Replace(" characters.", ""))); } else if (error.EndsWith(" cannot be null or empty.")) { errorList.Add(String.Format(IdentityErrors.PropertyTooShort, error.Replace(" cannot be null or empty.", ""))); } else if (error.StartsWith("Role ") && error.EndsWith(" does not exist.")) { errorList.Add(String.Format(IdentityErrors.RoleNotFound, error.Replace("Role ", "").Replace(" does not exist.", ""))); } else if (error.StartsWith("Store does not implement IQueryableRoleStore")) { errorList.Add(IdentityErrors.StoreNotIQueryableRoleStore); } else if (error.StartsWith("Store does not implement IQueryableUserStore")) { errorList.Add(IdentityErrors.StoreNotIQueryableUserStore); } else if (error.StartsWith("Store does not implement IUserClaimStore")) { errorList.Add(IdentityErrors.StoreNotIUserClaimStore); } else if (error.StartsWith("Store does not implement IUserConfirmationStore")) { errorList.Add(IdentityErrors.StoreNotIUserConfirmationStore); } else if (error.StartsWith("Store does not implement IUserEmailStore")) { errorList.Add(IdentityErrors.StoreNotIUserEmailStore); } else if (error.StartsWith("Store does not implement IUserLockoutStore")) { errorList.Add(IdentityErrors.StoreNotIUserLockoutStore); } else if (error.StartsWith("Store does not implement IUserLoginStore")) { errorList.Add(IdentityErrors.StoreNotIUserLoginStore); } else if (error.StartsWith("Store does not implement IUserPasswordStore")) { errorList.Add(IdentityErrors.StoreNotIUserPasswordStore); } else if (error.StartsWith("Store does not implement IUserPhoneNumberStore")) { errorList.Add(IdentityErrors.StoreNotIUserPhoneNumberStore); } else if (error.StartsWith("Store does not implement IUserRoleStore")) { errorList.Add(IdentityErrors.StoreNotIUserRoleStore); } else if (error.StartsWith("Store does not implement IUserSecurityStampStore")) { errorList.Add(IdentityErrors.StoreNotIUserSecurityStampStore); } else if (error.StartsWith("Store does not implement IUserTwoFactorStore")) { errorList.Add(IdentityErrors.StoreNotIUserTwoFactorStore); } else if (error.StartsWith("User already has a password set.")) { errorList.Add(IdentityErrors.UserAlreadyHasPassword); } else if (error.StartsWith("User already in role.")) { errorList.Add(IdentityErrors.UserAlreadyInRole); } else if (error.StartsWith("UserId not found.")) { errorList.Add(IdentityErrors.UserIdNotFound); } else if (error.StartsWith("User ") && error.EndsWith(" does not exist.")) { errorList.Add(String.Format(IdentityErrors.UserNameNotFound, error.Replace("User ", "").Replace(" does not exist.", ""))); } else if (error.StartsWith("User is not in role.")) { errorList.Add(IdentityErrors.UserNotInRole); } } } return errorList; } }
Cambié el método AddErrors del Controlador a lo siguiente:
private void AddErrors(IdentityResult result) { foreach (var error in IdentityResultErrorMessages.GetResourceEquivalent(result.Errors)) { ModelState.AddModelError("", error); } }
Ahora, puedo crear archivos de recursos para el otro contenido de idioma que deseo publicar en mi sitio web.
Permítanme compartir con ustedes lo que he hecho para lograr esto en español. ¡Allí puedes personalizar los mensajes fácilmente!
string Errores = result.Errors.FirstOrDefault(); Errores = Errores.Replace("Name " + Email.Text + " is already taken.", "- El mail " + Email.Text + " ya fue registrado. Intente recuperar la clave.<br/>"); Errores = Errores.Replace("Passwords must be at least 6 characters.", "- La clave debe contener por lo menos 6 caracteres.<br/>"); Errores = Errores.Replace("Passwords must have at least one non letter or digit character.", "- La clave debe contener por lo menos un caracter que no sea letra ni dígito. Por ejemplo: un punto.<br/>"); Errores = Errores.Replace("Passwords must have at least one digit (''0''-''9'').", "- La clave debe contener al menos un dígito (entre 0 y 9).<br/>"); Errores = Errores.Replace("Passwords must have at least one uppercase (''A''-''Z'').", "- La clave debe contener al menos una letra (entre A y Z).<br/>"); ErrorMessage.Text = "Errores:<br/><br/>" + Errores;
Todavía no, tenemos un error que rastrea este elemento de trabajo para permitir especificar de dónde provienen los recursos.
Para ASP.NET Core: (Microsoft.AspNetCore.Identity 1.0.0)
Cree una clase que herede IdentityErrorDescriber y anule los mensajes de error deseados.
public class CustomIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError DefaultError() { return new IdentityError { Code = nameof(DefaultError), Description = $"An unknown failure has occurred." }; }
public override IdentityError ConcurrencyFailure() { return new IdentityError { Code = nameof(ConcurrencyFailure), Description = "Optimistic concurrency failure, object has been modified." }; }
public override IdentityError PasswordMismatch() { return new IdentityError { Code = nameof(PasswordMismatch), Description = "Incorrect password." }; }
public override IdentityError InvalidToken() { return new IdentityError { Code = nameof(InvalidToken), Description = "Invalid token." }; }
public override IdentityError LoginAlreadyAssociated() { return new IdentityError { Code = nameof(LoginAlreadyAssociated), Description = "A user with this login already exists." }; }
public override IdentityError InvalidUserName(string userName) { return new IdentityError { Code = nameof(InvalidUserName), Description = $"User name ''{userName}'' is invalid, can only contain letters or digits." }; }
public override IdentityError InvalidEmail(string email) { return new IdentityError { Code = nameof(InvalidEmail), Description = $"Email ''{email}'' is invalid." }; }
public override IdentityError DuplicateUserName(string userName) { return new IdentityError { Code = nameof(DuplicateUserName), Description = $"User Name ''{userName}'' is already taken." }; }
public override IdentityError DuplicateEmail(string email) { return new IdentityError { Code = nameof(DuplicateEmail), Description = $"Email ''{email}'' is already taken." }; }
public override IdentityError InvalidRoleName(string role) { return new IdentityError { Code = nameof(InvalidRoleName), Description = $"Role name ''{role}'' is invalid." }; }
public override IdentityError DuplicateRoleName(string role) { return new IdentityError { Code = nameof(DuplicateRoleName), Description = $"Role name ''{role}'' is already taken." }; }
public override IdentityError UserAlreadyHasPassword() { return new IdentityError { Code = nameof(UserAlreadyHasPassword), Description = "User already has a password set." }; }
public override IdentityError UserLockoutNotEnabled() { return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "Lockout is not enabled for this user." }; }
public override IdentityError UserAlreadyInRole(string role) { return new IdentityError { Code = nameof(UserAlreadyInRole), Description = $"User already in role ''{role}''." }; }
public override IdentityError UserNotInRole(string role) { return new IdentityError { Code = nameof(UserNotInRole), Description = $"User is not in role ''{role}''." }; }
public override IdentityError PasswordTooShort(int length) { return new IdentityError { Code = nameof(PasswordTooShort), Description = $"Passwords must be at least {length} characters." }; }
public override IdentityError PasswordRequiresNonAlphanumeric() { return new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = "Passwords must have at least one non alphanumeric character." }; }
public override IdentityError PasswordRequiresDigit() { return new IdentityError { Code = nameof(PasswordRequiresDigit), Description = "Passwords must have at least one digit (''0''-''9'')." }; }
public override IdentityError PasswordRequiresLower() { return new IdentityError { Code = nameof(PasswordRequiresLower), Description = "Passwords must have at least one lowercase (''a''-''z'')." }; }
public override IdentityError PasswordRequiresUpper() { return new IdentityError { Code = nameof(PasswordRequiresUpper), Description = "Passwords must have at least one uppercase (''A''-''Z'')." }; }
}
En su clase de Inicio, registre el ErrorDescriber personalizado dentro de ConfigureServices:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddErrorDescriber<CustomIdentityErrorDescriber>(); // Add this line
ACTUALIZACIÓN: A continuación puede encontrar versiones ya traducidas para español y portugués. ¡Revíselos!
public class SpanishIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError DefaultError() { return new IdentityError { Code = nameof(DefaultError), Description = $"Ha ocurrido un error." }; }
public override IdentityError ConcurrencyFailure() { return new IdentityError { Code = nameof(ConcurrencyFailure), Description = "Ha ocurrido un error, el objeto ya ha sido modificado (Optimistic concurrency failure)." }; }
public override IdentityError PasswordMismatch() { return new IdentityError { Code = nameof(PasswordMismatch), Description = "Password Incorrecta." }; }
public override IdentityError InvalidToken() { return new IdentityError { Code = nameof(InvalidToken), Description = "Ha ingresado un código Inválido." }; }
public override IdentityError LoginAlreadyAssociated() { return new IdentityError { Code = nameof(LoginAlreadyAssociated), Description = "Un usuario con ese nombre ya existe." }; }
public override IdentityError InvalidUserName(string userName) { return new IdentityError { Code = nameof(InvalidUserName), Description = $"El nombre de usuario ''{userName}'' es inválido. Solo puede contener letras y números." }; }
public override IdentityError InvalidEmail(string email) { return new IdentityError { Code = nameof(InvalidEmail), Description = $"La dirección de email ''{email}'' es incorrecta." }; }
public override IdentityError DuplicateUserName(string userName) { return new IdentityError { Code = nameof(DuplicateUserName), Description = $"El usuario ''{userName}'' ya existe, por favor ingrese un nombre diferente." }; }
public override IdentityError DuplicateEmail(string email) { return new IdentityError { Code = nameof(DuplicateEmail), Description = $"La direccion de email ''{email}'' ya se encuentra registrada. Puede recupar su contraseña para ingresar nuevamente al sistema." }; }
public override IdentityError InvalidRoleName(string role) { return new IdentityError { Code = nameof(InvalidRoleName), Description = $"El nombre de rol ''{role}'' es inválido." }; }
public override IdentityError DuplicateRoleName(string role) { return new IdentityError { Code = nameof(DuplicateRoleName), Description = $"El nombre de rol ''{role}'' ya existe." }; }
public override IdentityError UserAlreadyHasPassword() { return new IdentityError { Code = nameof(UserAlreadyHasPassword), Description = "El usuario ya tiene contraseña." }; }
public override IdentityError UserLockoutNotEnabled() { return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "El bloqueo no esta habilitado para este usuario." }; }
public override IdentityError UserAlreadyInRole(string role) { return new IdentityError { Code = nameof(UserAlreadyInRole), Description = $"El usuario ya es parte del rol ''{role}''." }; }
public override IdentityError UserNotInRole(string role) { return new IdentityError { Code = nameof(UserNotInRole), Description = $"El usuario no es parte del rol ''{role}''." }; }
public override IdentityError PasswordTooShort(int length) { return new IdentityError { Code = nameof(PasswordTooShort), Description = $"La contraseña deben tener un largo mínimo de {length} caracteres." }; }
public override IdentityError PasswordRequiresNonAlphanumeric() { return new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = "La contraseña debe contener al menos un caracter alfanumérico." }; }
public override IdentityError PasswordRequiresDigit() { return new IdentityError { Code = nameof(PasswordRequiresDigit), Description = "La contraseña debe incluir al menos un dígito (''0''-''9'')." }; }
public override IdentityError PasswordRequiresLower() { return new IdentityError { Code = nameof(PasswordRequiresLower), Description = "La contraseña debe incluir al menos una letra minúscula (''a''-''z'')." }; }
public override IdentityError PasswordRequiresUpper() { return new IdentityError { Code = nameof(PasswordRequiresUpper), Description = "La contraseña debe incluir al menos una letra MAYÚSCULA (''A''-''Z'')." }; }
}
Portugués: (muchas gracias a furlanrapha )
public class PortugueseIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError DefaultError() { return new IdentityError { Code = nameof(DefaultError), Description = $"Um erro desconhecido ocorreu." }; }
public override IdentityError ConcurrencyFailure() { return new IdentityError { Code = nameof(ConcurrencyFailure), Description = "Falha de concorrência otimista, o objeto foi modificado." }; }
public override IdentityError PasswordMismatch() { return new IdentityError { Code = nameof(PasswordMismatch), Description = "Senha incorreta." }; }
public override IdentityError InvalidToken() { return new IdentityError { Code = nameof(InvalidToken), Description = "Token inválido." }; }
public override IdentityError LoginAlreadyAssociated() { return new IdentityError { Code = nameof(LoginAlreadyAssociated), Description = "Já existe um usuário com este login." }; }
public override IdentityError InvalidUserName(string userName) { return new IdentityError { Code = nameof(InvalidUserName), Description = $"Login ''{userName}'' é inválido, pode conter apenas letras ou dígitos." }; }
public override IdentityError InvalidEmail(string email) { return new IdentityError { Code = nameof(InvalidEmail), Description = $"Email ''{email}'' é inválido." }; }
public override IdentityError DuplicateUserName(string userName) { return new IdentityError { Code = nameof(DuplicateUserName), Description = $"Login ''{userName}'' já está sendo utilizado." }; }
public override IdentityError DuplicateEmail(string email) { return new IdentityError { Code = nameof(DuplicateEmail), Description = $"Email ''{email}'' já está sendo utilizado." }; }
public override IdentityError InvalidRoleName(string role) { return new IdentityError { Code = nameof(InvalidRoleName), Description = $"A permissão ''{role}'' é inválida." }; }
public override IdentityError DuplicateRoleName(string role) { return new IdentityError { Code = nameof(DuplicateRoleName), Description = $"A permissão ''{role}'' já está sendo utilizada." }; }
public override IdentityError UserAlreadyHasPassword() { return new IdentityError { Code = nameof(UserAlreadyHasPassword), Description = "Usuário já possui uma senha definida." }; }
public override IdentityError UserLockoutNotEnabled() { return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "Lockout não está habilitado para este usuário." }; }
public override IdentityError UserAlreadyInRole(string role) { return new IdentityError { Code = nameof(UserAlreadyInRole), Description = $"Usuário já possui a permissão ''{role}''." }; }
public override IdentityError UserNotInRole(string role) { return new IdentityError { Code = nameof(UserNotInRole), Description = $"Usuário não tem a permissão ''{role}''." }; }
public override IdentityError PasswordTooShort(int length) { return new IdentityError { Code = nameof(PasswordTooShort), Description = $"Senhas devem conter ao menos {length} caracteres." }; }
public override IdentityError PasswordRequiresNonAlphanumeric() { return new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = "Senhas devem conter ao menos um caracter não alfanumérico." }; }
public override IdentityError PasswordRequiresDigit() { return new IdentityError { Code = nameof(PasswordRequiresDigit), Description = "Senhas devem conter ao menos um digito (''0''-''9'')." }; }
public override IdentityError PasswordRequiresLower() { return new IdentityError { Code = nameof(PasswordRequiresLower), Description = "Senhas devem conter ao menos um caracter em caixa baixa (''a''-''z'')." }; }
public override IdentityError PasswordRequiresUpper() { return new IdentityError { Code = nameof(PasswordRequiresUpper), Description = "Senhas devem conter ao menos um caracter em caixa alta (''A''-''Z'')." }; }
}
Solución temporal de ASP NET (25.11.2016)
Puede inyectar IStringLocalizer en IdentityErrorDescriber personalizado
1) Cree un IdentityErrorDescriber personalizado (vea la respuesta de Gerardo Grignoli)
public class MultilanguageIdentityErrorDescriber : IdentityErrorDescriber
{
private readonly IStringLocalizer<SharedResource> _localizer;
public MultilanguageIdentityErrorDescriber(IStringLocalizer<SharedResource> localizer)
{
_localizer = localizer;
}
public override IdentityError DuplicateEmail(string email)
{
return new IdentityError()
{
Code = nameof(DuplicateEmail),
Description = string.Format(_localizer["Email {0} is already taken."], email)
};
}
// DuplicateUserName, InvalidEmail, DuplicateUserName etc
}
2) Registre MultilanguageIdentityErrorDescriber en Startup.cs
services.AddIdentity<IdentityUser, IdentityRole>()
.AddErrorDescriber<MultilanguageIdentityErrorDescriber>()
.AddDefaultTokenProviders();
3) Agregue mensajes de error a SharedResource.language.resx.
Having come across the same problem one can provide your own resource files to achieve this. Of course it requires a bit of reflection hacking! First you will need to be running with full security, ie set in your web.config:
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
</securityPolicy>
...
Next in your app start up code:
var ass = Assembly.Load("Microsoft.AspNet.Identity.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
Type hack = ass.GetType("Microsoft.AspNet.Identity.Resources");
var field = hack.GetField("resourceMan",
BindingFlags.Static |
BindingFlags.NonPublic);
// NOTE: this is where you set you own resource manager!
field.SetValue(null, new global::System.Resources.ResourceManager("WebApplication1.Resources", typeof(WebApplication1.Resources).Assembly));
Job done!
Here is Gerardo Grignoli''s answer with French translation
/// <inheritdoc />
/// <summary>
/// Service to enable localization (french) for application facing identity errors.
/// </summary>
public class FrenchIdentityErrorDescriber : IdentityErrorDescriber
{
/// <inheritdoc />
public override IdentityError DefaultError() => new IdentityError { Code = nameof(DefaultError), Description = "Une erreur inconnue est survenue." };
/// <inheritdoc />
public override IdentityError ConcurrencyFailure() => new IdentityError { Code = nameof(ConcurrencyFailure), Description = "Erreur de concurrence simultanée optimiste, l''objet a été modifié." };
/// <inheritdoc />
public override IdentityError PasswordMismatch() => new IdentityError { Code = nameof(PasswordMismatch), Description = "Mot de passe incorrect." };
/// <inheritdoc />
public override IdentityError InvalidToken() => new IdentityError { Code = nameof(InvalidToken), Description = "Jeton invalide." };
/// <inheritdoc />
public override IdentityError LoginAlreadyAssociated() => new IdentityError { Code = nameof(LoginAlreadyAssociated), Description = "Un utilisateur avec ce nom de compte existe déjà." };
/// <inheritdoc />
public override IdentityError InvalidUserName(string userName) => new IdentityError { Code = nameof(InvalidUserName), Description = $"Le nom de compte ''{userName}'' est invalide. Seuls les lettres et chiffres sont autorisés." };
/// <inheritdoc />
public override IdentityError InvalidEmail(string email) => new IdentityError { Code = nameof(InvalidEmail), Description = $"L''email ''{email}'' est invalide." };
/// <inheritdoc />
public override IdentityError DuplicateUserName(string userName) => new IdentityError { Code = nameof(DuplicateUserName), Description = $"Le nom de compte ''{userName}'' est déjà utilisé." };
/// <inheritdoc />
public override IdentityError DuplicateEmail(string email) => new IdentityError { Code = nameof(DuplicateEmail), Description = $"L''email ''{email} est déjà utilisée." };
/// <inheritdoc />
public override IdentityError InvalidRoleName(string role) => new IdentityError { Code = nameof(InvalidRoleName), Description = $"Le nom du rôle ''{role}'' est invalide." };
/// <inheritdoc />
public override IdentityError DuplicateRoleName(string role) => new IdentityError { Code = nameof(DuplicateRoleName), Description = $"Le nom du rôle ''{role}'' est déjà utilisé." };
/// <inheritdoc />
public override IdentityError UserAlreadyHasPassword() => new IdentityError { Code = nameof(UserAlreadyHasPassword), Description = "L''utilisateur a déjà un mot de passe." };
/// <inheritdoc />
public override IdentityError UserLockoutNotEnabled() => new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "Le verouillage n''est pas activé pour cet utilisateur." };
/// <inheritdoc />
public override IdentityError UserAlreadyInRole(string role) => new IdentityError { Code = nameof(UserAlreadyInRole), Description = $"L''utilisateur a déjà le rôle ''{role}''." };
/// <inheritdoc />
public override IdentityError UserNotInRole(string role) => new IdentityError { Code = nameof(UserNotInRole), Description = $"L''utilisateur n''a pas le rôle ''{role}''." };
/// <inheritdoc />
public override IdentityError PasswordTooShort(int length) => new IdentityError { Code = nameof(PasswordTooShort), Description = $"Le mot de passe doit contenir au moins {length} caractères." };
/// <inheritdoc />
public override IdentityError PasswordRequiresNonAlphanumeric() => new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = "Le mot de passe doit contenir au moins un caractère non alpha-numérique." };
/// <inheritdoc />
public override IdentityError PasswordRequiresDigit() => new IdentityError { Code = nameof(PasswordRequiresDigit), Description = "Le mot de passe doit contenir au moins un chiffre (''0''-''9'')." };
/// <inheritdoc />
public override IdentityError PasswordRequiresLower() => new IdentityError { Code = nameof(PasswordRequiresLower), Description = "Le mot de passe doit contenir au moins un charactère minuscule (''a''-''z'')." };
/// <inheritdoc />
public override IdentityError PasswordRequiresUpper() => new IdentityError { Code = nameof(PasswordRequiresUpper), Description = "Le mot de passe doit contenir au moins un charactère majuscule (''A''-''Z'')." };
}
I managed to find a solution to create localized version of AspNet.Identity for cultures that no ready-to-use package is available for. Steps are:
- Download the source code from GitHub and open the src/Microsoft.AspNet.Identity.Core project in visual studio.
- Add a new item (Resource file) to the project. Name it
Resources.aa-BB.resx
whichaa-BB
is the culture you need to localize the assembly for. eg.:Resources.fa-IR.resx
. - Open the new created file and copy resource items from
Resources.resx
to it. - Localize the texts as you want.
- Build the project.
- Go to the
bin
folder and under theDebug
orRelease
directory you will see a folder namedaa-BB
(the culture you entered before). Copy the folder to thebin
directory of your project. - Make the changes that @1AmirJalali said ( this post ).
- Well done! Localization is complete. Try to share your localized package :)
My solution was a little bit dirty but It works as expected. The idea is to assign all the errors that the IdentityResult object result brings when we register a new user:
var result = await UserManager.CreateAsync(user, model.Password);
To a new one that we will create. First, we need to create a List that will contain our custom errors:
var erroresEspanol = new List<string>();
Then, we need to iterate trough the errors inside result and add a new custom error for every one of them depending on which was the cause of it (Duplicate user, email, etc.):
foreach (var error in result.Errors)
{
if (error == ("Name " + model.Usuario + " is already taken."))
{
erroresEspanol.Add("El Usuario " + model.Usuario + " ya existe.");
}
if (error.Substring(0, error.IndexOf(" ")) == "Email")
{
erroresEspanol.Add("El Email " + model.Email + " ya fue ingresado.");
}
if (error.Substring(0, error.IndexOf(" ")) == "Passwords")
{
erroresEspanol.Add("La contraseña debe contener mayúscula, minúscula y al menos 6 caracteres de longitud.");
}
}
So after that we just create a new IdentityResult object:
var resultado = new IdentityResult(erroresEspanol);
And pass it to the AddErrors method at the end of our Register action:
AddErrors(resultado);