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

时间:2019-09-03 18:18:29

标签: asp.net-core custom-attributes asp.net-core-localization

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

我的自定义验证类:

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

我的模型课:

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

本地化适用于默认属性,例如kill(pid, SIGTERM)等,但不适用于我的自定义验证属性。我不知道我的代码有什么问题。我已经阅读了ASP.NET Core custom validation attribute localization的stackoverflow帖子,但是我不明白为什么我的本地化服务器端验证无法正常工作。希望有人能帮助我或给我一个例子,让它正常工作,因为这个问题使我发疯。

2 个答案:

答案 0 :(得分:0)

  

我不想为此执行客户端验证。我知道有一种实现适配器的方法,但是如果我错了,请纠正我,这仅用于客户端验证。

实际上,那不是事实。适配器并不意味着您必须使用客户端验证。请参阅Ramin's answer here

对于您自己的问题,您可以创建一个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>();

最后,如果您使用querystring作为区域性提供程序,请不要忘记在表单操作中附加一个culture=de

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

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

演示的屏幕截图

enter image description here

答案 1 :(得分:0)

创建适配器可能是一种解决方案,但这太昂贵了!您必须先创建适配器,然后创建适配器提供程序,然后在启动时进行注册!这是太多的工作。

一种较短的解决方案是通过ValidationContext.GetService在自定义验证属性中获取本地化服务:

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

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")]