如何在ASP.NET Core(v2.2)中本地化验证属性的标准错误消息?例如, [Required] 属性具有以下错误消息“ xxx字段是必填字段。”; [EmailAddress] 具有“ xxx字段不是有效的电子邮件地址。”; [比较] 具有“ 'xxx'和'yyy'不匹配。”,依此类推。在我们的项目中,我们不使用英语,我想找到一种方法来翻译标准错误消息,而又不直接将它们写在每个数据模型类的每个属性中
答案 0 :(得分:0)
这在docs中有详细说明。您可以执行以下任一操作:
在属性上使用ResourcePath
选项。
[Required(ResourcePath = "Resources")]
然后,您将本地化的消息添加到Resources/Namespace.To.MyClass.[lang].resx
。
对所有类使用一个资源文件:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddDataAnnotationsLocalization(options => {
options.DataAnnotationLocalizerProvider = (type, factory) =>
factory.Create(typeof(SharedResource));
});
}
答案 1 :(得分:0)
您已经知道可以在属性级别使用ErrorMessage来设置自定义错误消息,例如:
[Required(ErrorMessage = "MyRequiredErrorMessage")]
但这似乎不是您要寻找的解决方案。相反,如果可以在全局级别上设置 standard 验证属性的本地化,那就太好了。
我也一直在寻找这个,到这里结束,做了一些研究,最后弄清楚了如何做到这一点。这个想法很简单:找出属性的处理位置(适配器)并在全局级别覆盖ErrorMessage。
在适配器中仅设置ErrorMessage,因为这不是业务规则的地方。但是,有两个例外:
设置了本地自定义消息后,请勿替换该消息。
StringLengthAttribute有两条消息。这取决于最小长度参数的值。
创建适配器:
using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.Extensions.Localization;
using System.ComponentModel.DataAnnotations;
public class ValidationAttributeErrorMessageProvider : ValidationAttributeAdapterProvider, IValidationAttributeAdapterProvider
{
public ValidationAttributeErrorMessageProvider()
{
}
IAttributeAdapter IValidationAttributeAdapterProvider.GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
{
// There are multiple error messages per attribute, but only one is used for validation.
// No need to override the messages that are part of an exception.
// These messages are meant for the developer, not the user.
// Please note that DataTypeAttribute is ignored here. From referencesource:
// This override always returns true. Subclasses should override this to provide the correct result.
// https://referencesource.microsoft.com/#System.ComponentModel.DataAnnotations/DataAnnotations/DataTypeAttribute.cs,89
// https://github.com/microsoft/referencesource/tree/master/System.ComponentModel.DataAnnotations/DataAnnotations/DataTypeAttribute.cs#L89
// A local message (a custom message set on the attribute) should not be overwritten.
if (!string.IsNullOrEmpty(attribute.ErrorMessage))
return GetAttributeAdapter(attribute, stringLocalizer);
// Though only default attributes are listed below, you can also include your custom attributes.
if (attribute is CompareAttribute)
{
// CompareAttribute_MustMatch '{0}' and '{1}' do not match.
// CompareAttribute_UnknownProperty Could not find a property named {0}.
attribute.ErrorMessage = "CompareAttribute_MustMatch";
}
else if (attribute is CreditCardAttribute)
{
// CreditCardAttribute_Invalid The {0} field is not a valid credit card number.
attribute.ErrorMessage = "CreditCardAttribute_Invalid";
}
else if (attribute is CustomValidationAttribute)
{
// CustomValidationAttribute_Method_Must_Return_ValidationResult The CustomValidationAttribute method '{0}' in type '{1}' must return System.ComponentModel.DataAnnotations.ValidationResult. Use System.ComponentModel.DataAnnotations.ValidationResult.Success to represent success.
// CustomValidationAttribute_Method_Not_Found The CustomValidationAttribute method '{0}' does not exist in type '{1}' or is not public and static.
// CustomValidationAttribute_Method_Required The CustomValidationAttribute.Method was not specified.
// CustomValidationAttribute_Method_Signature The CustomValidationAttribute method '{0}' in type '{1}' must match the expected signature: public static ValidationResult {0}(object value, ValidationContext context). The value can be strongly typed. The ValidationContext parameter is optional.
// CustomValidationAttribute_Type_Conversion_Failed Could not convert the value of type '{0}' to '{1}' as expected by method {2}.{3}.
// CustomValidationAttribute_Type_Must_Be_Public The custom validation type '{0}' must be public.
// CustomValidationAttribute_ValidationError {0} is not valid.
// CustomValidationAttribute_ValidatorType_Required The CustomValidationAttribute.ValidatorType was not specified.
attribute.ErrorMessage = "CustomValidationAttribute_ValidationError";
}
else if (attribute is EmailAddressAttribute)
{
// EmailAddressAttribute_Invalid The {0} field is not a valid e-mail address.
attribute.ErrorMessage = "EmailAddressAttribute_Invalid";
}
else if (attribute is EnumDataTypeAttribute)
{
// EnumDataTypeAttribute_TypeCannotBeNull The type provided for EnumDataTypeAttribute cannot be null.
// EnumDataTypeAttribute_TypeNeedsToBeAnEnum The type '{0}' needs to represent an enumeration type.
attribute.ErrorMessage = "EnumDataTypeAttribute_TypeNeedsToBeAnEnum";
}
else if (attribute is FileExtensionsAttribute)
{
// FileExtensionsAttribute_Invalid The {0} field only accepts files with the following extensions: {1}
attribute.ErrorMessage = "FileExtensionsAttribute_Invalid";
}
else if (attribute is MaxLengthAttribute)
{
// MaxLengthAttribute_InvalidMaxLength MaxLengthAttribute must have a Length value that is greater than zero. Use MaxLength() without parameters to indicate that the string or array can have the maximum allowable length.
// MaxLengthAttribute_ValidationError The field {0} must be a string or array type with a maximum length of '{1}'.
attribute.ErrorMessage = "MaxLengthAttribute_ValidationError";
}
else if (attribute is MinLengthAttribute)
{
// MinLengthAttribute_InvalidMinLength MinLengthAttribute must have a Length value that is zero or greater.
// MinLengthAttribute_ValidationError The field {0} must be a string or array type with a minimum length of '{1}'.
attribute.ErrorMessage = "MinLengthAttribute_ValidationError";
}
else if (attribute is PhoneAttribute)
{
// PhoneAttribute_Invalid The {0} field is not a valid phone number.
attribute.ErrorMessage = "PhoneAttribute_Invalid";
}
else if (attribute is RangeAttribute)
{
// RangeAttribute_ArbitraryTypeNotIComparable The type {0} must implement {1}.
// RangeAttribute_MinGreaterThanMax The maximum value '{0}' must be greater than or equal to the minimum value '{1}'.
// RangeAttribute_Must_Set_Min_And_Max The minimum and maximum values must be set.
// RangeAttribute_Must_Set_Operand_Type The OperandType must be set when strings are used for minimum and maximum values.
// RangeAttribute_ValidationError The field {0} must be between {1} and {2}.
attribute.ErrorMessage = "RangeAttribute_ValidationError";
}
else if (attribute is RegularExpressionAttribute)
{
// RegexAttribute_ValidationError The field {0} must match the regular expression '{1}'.
// RegularExpressionAttribute_Empty_Pattern The pattern must be set to a valid regular expression.
attribute.ErrorMessage = "RegexAttribute_ValidationError";
}
else if (attribute is RequiredAttribute)
{
// RequiredAttribute_ValidationError The {0} field is required.
attribute.ErrorMessage = "RequiredAttribute_ValidationError";
}
else if (attribute is StringLengthAttribute attr)
{
// StringLengthAttribute_InvalidMaxLength The maximum length must be a nonnegative integer.
// StringLengthAttribute_ValidationError The field {0} must be a string with a maximum length of {1}.
// StringLengthAttribute_ValidationErrorIncludingMinimum The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.
// One exception to the rule, determine which message to show:
if (attr.MinimumLength == 0)
attribute.ErrorMessage = "StringLengthAttribute_ValidationError";
else
attribute.ErrorMessage = "StringLengthAttribute_ValidationErrorIncludingMinimum";
}
else if (attribute is UrlAttribute)
{
// UrlAttribute_Invalid The {0} field is not a valid fully-qualified http, https, or ftp URL.
attribute.ErrorMessage = "UrlAttribute_Invalid";
}
return GetAttributeAdapter(attribute, stringLocalizer);
}
}
所有现有属性都通过此适配器。如您所见,仅ErrorMessage被更新。这样会将内部标志设置为自定义ErrorMessage(就像在属性本身上进行设置时一样)。
在Startup.ConfigureServices中注册适配器:
services.AddSingleton<IValidationAttributeAdapterProvider, ValidationAttributeErrorMessageProvider>();
有关此消息的一条评论。您可以使用 message 本身,例如"The {0} field is required."
或验证错误的名称,例如"RequiredAttribute_ValidationError"
。
我之所以用这个名字( RequiredAttribute_ValidationError ),是因为我有一个用于多个类的资源字符串(例如 SharedResource )。因此,我可以简单地将行从DataAnnotations资源文件复制到资源文件(例如 SharedResource.fr.resx )。请注意,我必须在所有受支持的语言资源文件中复制条目,因为我不想显示名称。
默认属性的错误消息可以在DataAnnotationsResources.resx中找到。
答案 2 :(得分:0)
如果您只想本地化错误消息而不是构建多语言站点,您可以尝试以下方法:(消息字符串可能是您的语言。)
IValidationMetadataProvider
: public class MyModelMetadataProvider : IValidationMetadataProvider
{
public void CreateValidationMetadata(ValidationMetadataProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException();
}
var validators = context.ValidationMetadata.ValidatorMetadata;
// add [Required] for value-types (int/DateTime etc)
// to set ErrorMessage before asp.net does it
var theType = context.Key.ModelType;
var underlyingType = Nullable.GetUnderlyingType(theType);
if (theType.IsValueType &&
underlyingType == null && // not nullable type
validators.Where(m => m.GetType() == typeof(RequiredAttribute)).Count() == 0)
{
validators.Add(new RequiredAttribute());
}
foreach (var obj in validators)
{
if (!(obj is ValidationAttribute attribute))
{
continue;
}
fillErrorMessage<RequiredAttribute>(attribute,
"You must fill in '{0}'.");
fillErrorMessage<MinLengthAttribute>(attribute,
"Min length of '{0}' is {1}.");
fillErrorMessage<MaxLengthAttribute>(attribute,
"Max length of '{0}' is {1}.");
fillErrorMessage<EmailAddressAttribute>(attribute,
"Invalid email address.", true);
// other attributes like RangeAttribute, CompareAttribute, etc
}
}
private void fillErrorMessage<T>(object attribute, string errorMessage,
bool forceOverriding = false)
where T : ValidationAttribute
{
if (attribute is T validationAttribute)
{
if (forceOverriding ||
(validationAttribute.ErrorMessage == null
&& validationAttribute.ErrorMessageResourceName == null))
{
validationAttribute.ErrorMessage = errorMessage;
}
}
}
}
Startup.cs
中添加一些行: public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
.AddMvcOptions(m => {
m.ModelMetadataDetailsProviders.Add(new MyModelMetadataProvider());
m.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(
fieldName => string.Format("'{0}' must be a valid number.", fieldName));
// you may check the document of `DefaultModelBindingMessageProvider`
// and add more if needed
})
;
}
见the document of DefaultModelBindingMessageProvider
如果您能用日语阅读,请参阅this article了解更多详情。