仅为 EmailAddressAttribute 调用 IValidationAttributeAdapterProvider
IValidationAttributeAdapterProvider is called only for EmailAddressAttribute
我用 ASP.NET MVC 5
做了什么
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MaxLengthAttribute), typeof(MyMaxLengthAttributeAdapter));
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredAttribute), typeof(MyRequiredAttributeAdapter));
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MinLengthAttribute), typeof(MyMinLengthAttribute));
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EmailAddressAttribute), typeof(MyEmailAddressAttributeAdapter));
现在我正在将它迁移到 ASP.NET 核心 6
我们不能再使用 DataAnnotationsModelValidatorProvider
,所以我正在尝试使用 IValidationAttributeAdapterProvider
,这对我来说效果不佳。
我的代码
下面是我的IValidationAttributeAdapterProvider
public class MyValidationAttributeAdapterProvider : ValidationAttributeAdapterProvider, IValidationAttributeAdapterProvider
{
IAttributeAdapter? IValidationAttributeAdapterProvider.GetAttributeAdapter(
ValidationAttribute attribute,
IStringLocalizer? stringLocalizer)
{
return attribute switch
{
EmailAddressAttribute => new MyEmailAddressAttributeAdapter((EmailAddressAttribute)attribute, stringLocalizer),
MaxLengthAttribute => new MyMaxLengthAttributeAdapter((MaxLengthAttribute)attribute, stringLocalizer),
MinLengthAttribute => new MyMinLengthAttribute((MinLengthAttribute)attribute, stringLocalizer),
RequiredAttribute => new MyRequiredAttributeAdapter((RequiredAttribute)attribute, stringLocalizer),
_ => base.GetAttributeAdapter(attribute, stringLocalizer),
};
}
}
下面是我的模型class
public class LogInRequestDTO
{
[Required]
[EmailAddress]
[MaxLength(FieldLengths.Max.User.Mail)]
[Display(Name = "mail")]
public string? Mail { get; set; }
[Required]
[MinLengthAttribute(FieldLengths.Min.User.Password)]
[DataType(DataType.Password)]
[Display(Name = "password")]
public string? Password { get; set; }
}
在我的 Program.cs 中,我喜欢下面的内容。
builder.Services.AddControllersWithViews()
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(Resources));
});
builder.Services.AddSingleton<IValidationAttributeAdapterProvider, MyValidationAttributeAdapterProvider>();
让我开心的事情
我希望为每个属性调用 GetAttributeAdapter
,例如 EmailAddressAttribute
、MaxLengthAttribute
等。
但是它只用 EmailAddressAttribute
调用了一次。
所以,所有其他验证结果都不是我的适配器定制的。
如果我从模型 class 中删除 [EmailAddress]
,则永远不会调用 GetAttributeAdapter
。
我是不是漏掉了什么?
添加于 2022/05/24
我想做什么
- 我想自定义所有验证错误信息。
- 我不想在我使用的地方一一定制
[EmailAddress]
例如
- 我只需要服务器端验证。我不需要客户端验证。
可重现的项目
我创建了可以重现问题的最小示例项目。
https://github.com/KuniyoshiKamimura/IValidationAttributeAdapterProviderSample
- 用Visual Studio2022(17.2.1)打开解决方案。
- 在 MyValidationAttributeAdapterProvider 上设置断点。
- 运行 项目。
- 在浏览器的文本框中输入内容并提交。
- 断点只命中一次 EmailAddressAttribute 属性。
- 浏览器显示电子邮件的自定义消息和所有其他验证的默认消息。
下面是工作demo,大家可以参考一下
在所有 AttributeAdapter
中,按如下所示更改您的代码。
public class MyEmailAddressAttributeAdapter : AttributeAdapterBase<EmailAddressAttribute>
{
// This is called as expected.
public MyEmailAddressAttributeAdapter(EmailAddressAttribute attribute, IStringLocalizer? stringLocalizer)
: base(attribute, stringLocalizer)
{
//attribute.ErrorMessageResourceType = typeof(Resources);
//attribute.ErrorMessageResourceName = "ValidationMessageForEmailAddress";
//attribute.ErrorMessage = null;
}
public override void AddValidation(ClientModelValidationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-must-be-true", GetErrorMessage(context));
}
// This is called as expected.
// And I can see the message "Input the valid mail address.".
public override string GetErrorMessage(ModelValidationContextBase validationContext)
{
return GetErrorMessage(validationContext.ModelMetadata, validationContext.ModelMetadata.GetDisplayName());
}
}
在家庭控制器中:
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index([FromForm][Bind("Test")] SampleDTO dto)
{
return View();
}
索引视图:
@model IV2.Models.SampleDTO
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<h4>SampleDTO</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Index">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Test" class="control-label"></label>
<input asp-for="Test" class="form-control" />
<span asp-validation-for="Test" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
结果 1:
结果2:
我找到了解决方案。
我要用的不是ValidationAttributeAdapterProvider
而是IValidationMetadataProvider
.
This article详细介绍了用法。
请注意,某些属性(包括 EmailAddressAttribute
必须以特殊方式处理,如描述 here 因为它们具有默认值 non-null ErrorMessage
.
我已确认 EmailAddressAttribute
和其他一些属性。
此外,还有相关文章 here。
我用 ASP.NET MVC 5
做了什么DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MaxLengthAttribute), typeof(MyMaxLengthAttributeAdapter));
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredAttribute), typeof(MyRequiredAttributeAdapter));
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MinLengthAttribute), typeof(MyMinLengthAttribute));
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EmailAddressAttribute), typeof(MyEmailAddressAttributeAdapter));
现在我正在将它迁移到 ASP.NET 核心 6
我们不能再使用 DataAnnotationsModelValidatorProvider
,所以我正在尝试使用 IValidationAttributeAdapterProvider
,这对我来说效果不佳。
我的代码
下面是我的IValidationAttributeAdapterProvider
public class MyValidationAttributeAdapterProvider : ValidationAttributeAdapterProvider, IValidationAttributeAdapterProvider
{
IAttributeAdapter? IValidationAttributeAdapterProvider.GetAttributeAdapter(
ValidationAttribute attribute,
IStringLocalizer? stringLocalizer)
{
return attribute switch
{
EmailAddressAttribute => new MyEmailAddressAttributeAdapter((EmailAddressAttribute)attribute, stringLocalizer),
MaxLengthAttribute => new MyMaxLengthAttributeAdapter((MaxLengthAttribute)attribute, stringLocalizer),
MinLengthAttribute => new MyMinLengthAttribute((MinLengthAttribute)attribute, stringLocalizer),
RequiredAttribute => new MyRequiredAttributeAdapter((RequiredAttribute)attribute, stringLocalizer),
_ => base.GetAttributeAdapter(attribute, stringLocalizer),
};
}
}
下面是我的模型class
public class LogInRequestDTO
{
[Required]
[EmailAddress]
[MaxLength(FieldLengths.Max.User.Mail)]
[Display(Name = "mail")]
public string? Mail { get; set; }
[Required]
[MinLengthAttribute(FieldLengths.Min.User.Password)]
[DataType(DataType.Password)]
[Display(Name = "password")]
public string? Password { get; set; }
}
在我的 Program.cs 中,我喜欢下面的内容。
builder.Services.AddControllersWithViews()
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(Resources));
});
builder.Services.AddSingleton<IValidationAttributeAdapterProvider, MyValidationAttributeAdapterProvider>();
让我开心的事情
我希望为每个属性调用 GetAttributeAdapter
,例如 EmailAddressAttribute
、MaxLengthAttribute
等。
但是它只用 EmailAddressAttribute
调用了一次。
所以,所有其他验证结果都不是我的适配器定制的。
如果我从模型 class 中删除 [EmailAddress]
,则永远不会调用 GetAttributeAdapter
。
我是不是漏掉了什么?
添加于 2022/05/24
我想做什么
- 我想自定义所有验证错误信息。
- 我不想在我使用的地方一一定制
[EmailAddress]
例如 - 我只需要服务器端验证。我不需要客户端验证。
可重现的项目
我创建了可以重现问题的最小示例项目。 https://github.com/KuniyoshiKamimura/IValidationAttributeAdapterProviderSample
- 用Visual Studio2022(17.2.1)打开解决方案。
- 在 MyValidationAttributeAdapterProvider 上设置断点。
- 运行 项目。
- 在浏览器的文本框中输入内容并提交。
- 断点只命中一次 EmailAddressAttribute 属性。
- 浏览器显示电子邮件的自定义消息和所有其他验证的默认消息。
下面是工作demo,大家可以参考一下
在所有 AttributeAdapter
中,按如下所示更改您的代码。
public class MyEmailAddressAttributeAdapter : AttributeAdapterBase<EmailAddressAttribute>
{
// This is called as expected.
public MyEmailAddressAttributeAdapter(EmailAddressAttribute attribute, IStringLocalizer? stringLocalizer)
: base(attribute, stringLocalizer)
{
//attribute.ErrorMessageResourceType = typeof(Resources);
//attribute.ErrorMessageResourceName = "ValidationMessageForEmailAddress";
//attribute.ErrorMessage = null;
}
public override void AddValidation(ClientModelValidationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-must-be-true", GetErrorMessage(context));
}
// This is called as expected.
// And I can see the message "Input the valid mail address.".
public override string GetErrorMessage(ModelValidationContextBase validationContext)
{
return GetErrorMessage(validationContext.ModelMetadata, validationContext.ModelMetadata.GetDisplayName());
}
}
在家庭控制器中:
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index([FromForm][Bind("Test")] SampleDTO dto)
{
return View();
}
索引视图:
@model IV2.Models.SampleDTO
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<h4>SampleDTO</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Index">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Test" class="control-label"></label>
<input asp-for="Test" class="form-control" />
<span asp-validation-for="Test" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
结果 1:
结果2:
我找到了解决方案。
我要用的不是ValidationAttributeAdapterProvider
而是IValidationMetadataProvider
.
This article详细介绍了用法。
请注意,某些属性(包括 EmailAddressAttribute
必须以特殊方式处理,如描述 here 因为它们具有默认值 non-null ErrorMessage
.
我已确认 EmailAddressAttribute
和其他一些属性。
此外,还有相关文章 here。