Search code examples
asp.net-corecustom-attributesasp.net-core-localization

ASP.NET Core custom validation error message not localized


I want to implement localization in a custom attribute to check if the property is a valid IP address or hostname. The validation is working fine so far, but my problem is that I only get the default english error message though my local is switched to german. I am working with resource files. I do not want to implement a client side validation for this. I know there is way to implement an Adapter but correct me if I'm wrong, this is only for client side validation.

My custom validation class:

public class IPAddressOrHostnameAttribute : ValidationAttribute
{
     public IPAddressOrHostnameAttribute(string propertyName, object desiredvalue, string errorMessage)
    {
        PropertyName = propertyName;
        DesiredValue = desiredvalue;
        ErrorMessage = errorMessage;
    }

    private string PropertyName { get; }
    private object DesiredValue { get; }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        var instance = context.ObjectInstance;
        var type = instance.GetType();
        var propertyValue = type.GetProperty(PropertyName).GetValue(instance, null);
        if (propertyValue.ToString() == DesiredValue.ToString() && value != null)
        {
            if (Regex.IsMatch(value.ToString(), AckConstants.VALIDIPADDRESSREGEX)
                || Regex.IsMatch(value.ToString(), AckConstants.VALIDHOSTNAMEREGEX))
            {
                return ValidationResult.Success;
            }

            return new ValidationResult(ErrorMessage);
        }

        return ValidationResult.Success;
    }
}

My model class:

  [Required(ErrorMessage = "The field {0} is required")]
        [RegularExpression(@"^\S*$", ErrorMessage = "No white spaces allowed.")]
        [IPAddressOrHostname(nameof(IsFileAdapter), true, "Please enter a valid IP address or hostname")]
        [IPAddress(nameof(IsFileAdapter), false, "Please enter a valid IP address")]
        [Display(Name = "Destination")]
        public string Destination { get; set; }

My startup class to configure the DataAnnotationLocalizerProvider:

services
                .AddMvc()
                .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,
                    opts => { opts.ResourcesPath = "Resources"; })
                .AddDataAnnotationsLocalization(options =>
                {
                    options.DataAnnotationLocalizerProvider = (type, factory) =>
                        factory.Create(typeof(SharedResource)); // SharedResource is the class where the DataAnnotations (translations) will be stored.
                })

The localization is working for the default attributes like e.g. Required etc. but not for my custom validation attribute. I have no idea what's wrong in my code. I have read the stackoverflow post ASP.NET Core custom validation attribute localization, but I do not understand why my server side validation with localization is not working. Hopefully someone can help me or give me an example how to get it work, because this problem is driving me crazy.


Solution

  • Creating an adapter could be a solution, but it is too expensive! you have to create the adapter, then create adapter provider then you need to register it in startup! it is too much work.

    A shorter solution is to get localization service in the custom validation attribute via ValidationContext.GetService:

    If you are using the built-in localization service it will be IStringLocalizer, if you are using a custom localization service e.g. (MyLocalizer) you can access it by parsing it to the GetService(typeof(MyLocalizer)) method. see below sample:

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var _localizationService = (IStringLocalizer)validationContext.GetService(typeof(IStringLocalizer));
    
        var localizedError = _localizationService[ErrorMessage];
    
        //
        // do your custom validation
        //
        // if validation result is wrong
            return new ValidationResult(localizedError);
    
        // if validation result is correct
        return ValidationResult.Success;
    }
    

    btw, ValidationAttribute already has ErrorMessage property, so you don't have to define it in your custom attribute.

    public IPAddressOrHostnameAttribute(string propertyName, object desiredvalue /*, string errorMessage*/)
        {
            PropertyName = propertyName;
            DesiredValue = desiredvalue;
            // ErrorMessage = errorMessage;
        }
    
    

    Use it just like other attributes:

    [IPAddressOrHostname(nameof(IsFileAdapter), true, ErrorMessage = "Please enter a valid IP address or hostname")]