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")]
我想在自定义属性中实现本地化以检查 属性 是否是有效的 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")]