ASP.NET 核心自定义验证错误消息未本地化

ASP.NET Core custom validation error message not localized

我想在自定义属性中实现本地化以检查 属性 是否是有效的 IP 地址或主机名。到目前为止,验证工作正常,但我的问题是,尽管我的本地语言已切换为德语,但我只收到默认的英语错误消息。我正在处理资源文件。我不想为此实施客户端验证。我知道有实现适配器的方法,但如果我错了请纠正我,这仅用于客户端验证。

我的自定义验证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;
    }
}

我的模型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; }

我的启动class配置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.
                })

本地化适用于默认属性,例如Required 等,但不适用于我的自定义验证属性。我不知道我的代码有什么问题。我已经阅读了 Whosebug post ,但我不明白为什么我的本地化服务器端验证不起作用。希望有人能帮助我或给我一个如何让它工作的例子,因为这个问题快把我逼疯了。

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.

事实上,事实并非如此。适配器并不意味着您必须使用客户端验证。请参阅

至于你的问题本身,你可以创建一个 Adapter 和一个 AdapterProvider 来提供适配器:

public class IPAddressOrHostnameAttributeAdapter : AttributeAdapterBase<IPAddressOrHostnameAttribute>
{
    public IPAddressOrHostnameAttributeAdapter(IPAddressOrHostnameAttribute 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());
    }
}

public class  IPAddressOrHostnameAttributeAdapterProvider : IValidationAttributeAdapterProvider
{
    private readonly IValidationAttributeAdapterProvider fallback = new ValidationAttributeAdapterProvider();

    public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
    {
        var attr = attribute as IPAddressOrHostnameAttribute;
        return attr == null?
            this.fallback.GetAttributeAdapter(attribute, stringLocalizer):
            new IPAddressOrHostnameAttributeAdapter(attr, stringLocalizer);
    }
}

此外,确保此服务已在 DI 容器中注册:

services.AddSingleton<IValidationAttributeAdapterProvider, IPAddressOrHostnameAttributeAdapterProvider>();

最后,如果您使用查询字符串作为文化提供者,请不要忘记在表单操作中附加 culture=de

@{ var __culture = Context.Features.Get<IRequestCultureFeature>().RequestCulture.Culture.ToString(); }

<form asp-action="Create" asp-route-culture="@__culture">
    ....
</form>

演示截图

创建一个适配器可能是一个解决方案,但它太昂贵了!您必须创建适配器,然后创建适配器提供程序然后您需要在启动时注册它!工作量太大了。

一个较短的解决方案是通过 ValidationContext.GetService:

在自定义验证属性中获取本地化服务

如果您使用的是内置本地化服务,它将是 IStringLocalizer,如果您使用的是自定义本地化服务,例如(MyLocalizer) 您可以通过将其解析为 GetService(typeof(MyLocalizer)) 方法来访问它。请参阅以下示例:

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;
}

顺便说一句,ValidationAttribute 已经有 ErrorMessage 属性,因此您不必在自定义属性中定义它。

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

像其他属性一样使用它:

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