Estoy tratando de implementar la localización en un atributo de validación personalizado en asp.net core 1.0. Este es mi modelo de vista simplificado:
public class EditPasswordViewModel { [Required(ErrorMessage = "OldPasswordRequired")] [DataType(DataType.Password)] [CheckOldPassword(ErrorMessage = "OldPasswordWrong")] public string OldPassword { get; set; } }La localización de "OldPasswordRequired" funciona correctamente. Sin embargo, la localización de mi atributo personalizado no funciona y siempre devuelve el mensaje "OldPasswordWrong". Este es el código:
public class CheckOldPasswordAttribute : ValidationAttribute { protected override ValidationResult IsValid(object classInstance, ValidationContext validationContext) { if (oldPasswordSaved == oldPasswordTyped) //simplified { return ValidationResult.Success; } else { string errorMessage = FormatErrorMessage(ErrorMessageString); return new ValidationResult(errorMessage); } } }ErrorMessageString siempre es "OldPasswordWrong" y FormatErrorMessage devuelve siempre "OldPasswordWrong". ¿Qué estoy haciendo mal? Estoy usando las nuevas localizaciones de anotaciones de datos principales de asp.net, por lo que no estoy usando los atributos ErrorMessageResourceName y ErrorMessageResourceType (no tengo ViewModel.Designer.cs).
Implemente un adaptador para la localización:
public class RequiredIfAttributeAdapter : AttributeAdapterBase<RequiredIfAttribute> { public RequiredIfAttributeAdapter(RequiredIfAttribute attribute, IStringLocalizer stringLocalizer) : base(attribute, stringLocalizer) {} public override void AddValidation(ClientModelValidationContext context) {} public override string GetErrorMessage(ModelValidationContextBase validationContext) { return GetErrorMessage(validationContext.ModelMetadata, validationContext.ModelMetadata.GetDisplayName()); } }Implemente un proveedor para los adaptadores:
public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider { private readonly IValidationAttributeAdapterProvider _baseProvider = new ValidationAttributeAdapterProvider(); public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer) { if (attribute is RequiredIfAttribute) return new RequiredIfAttributeAdapter(attribute as RequiredIfAttribute, stringLocalizer); else return _baseProvider.GetAttributeAdapter(attribute, stringLocalizer); } }Registre el proveedor en Startup.cs:
services.AddSingleton<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();Créditos a este blog: https://blogs.msdn.microsoft.com/mvpawardprogram/2017/01/03/asp-net-core-mvc/
La respuesta de Ramin es la respuesta correcta. Pero decidí tomar otro camino, por lo que no tengo que escribir adaptadores y proveedores de adaptadores en muchos casos.
La idea es envolver su localizador de cadena específico en una interfaz de servicio y obtenerlo del propio atributo de validación.
public class CPFAttribute: ValidationAttribute { public CPFAttribute() { } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { string cpf; try { cpf = (string)value; } catch (Exception) { return new ValidationResult(GetErrorMessage(validationContext)); } if (string.IsNullOrEmpty(cpf) || cpf.Length != 11 || !StringUtil.IsDigitsOnly(cpf)) { return new ValidationResult(GetErrorMessage(validationContext)); } return ValidationResult.Success; } private string GetErrorMessage(ValidationContext validationContext) { if (string.IsNullOrEmpty(ErrorMessage)) { return "Invalid CPF"; } ErrorMessageTranslationService errorTranslation = validationContext.GetService(typeof(ErrorMessageTranslationService)) as ErrorMessageTranslationService; return errorTranslation.GetLocalizedError(ErrorMessage); } }Entonces el servicio se puede crear como:
public class ErrorMessageTranslationService { private readonly IStringLocalizer<SharedResource> _sharedLocalizer; public ErrorMessageTranslationService(IStringLocalizer<SharedResource> sharedLocalizer) { _sharedLocalizer = sharedLocalizer; } public string GetLocalizedError(string errorKey) { return _sharedLocalizer[errorKey]; } }El servicio se puede registrar como singleton, en la clase Startup.
services.AddSingleton<ErrorMessageTranslationService>();Si estos atributos de validación deben factorizarse en otro ensamblaje, simplemente cree una interfaz para este servicio de traducción a la que puedan hacer referencia todos los atributos de validación que cree.
Es necesario especificar la cultura. ¿Qué hace formaterrormesage? ¿Manejará la cultura?