获取 ASP.NET 核心中客户端验证的完整 HTML 字段名称



我正在实现一个自定义验证属性。此属性不仅查看它所应用到的属性的值,还查看另一个属性的值。另一个属性由其名称指定。

我需要找到一种方法来获取其他属性的输入将在最终 HTML 输出中具有的完整 id。

这是我的验证属性的简化版本:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class MyCustomValidationAttribute : ValidationAttribute, IClientModelValidator
{
private string _otherPropertyName;
public MyCustomValidationAttribute(string otherPropertyName)
{
_otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var otherProperty = context.ObjectInstance.GetType().GetProperty(_otherPropertyName);
var otherPropertyValue = Convert.ToString(otherProperty.GetValue(context.ObjectInstance, null));
// Validation logic...
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val-mycustomvalidation", errorMessage);
// THIS ROW NEEDS TO BE FIXED
MergeAttribute(context.Attributes, "data-val-mycustomvalidation-otherpropertyname", _otherProperyName);
}
private void MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (!attributes.ContainsKey(key))
{
attributes.Add(key, value);
}
}
}

这演示了如何在模型类中使用它:

public class Report
{
[MyCustomValidation("Value2", ErrorMessage = "Error...")]
public string Value1 { get; set; }
public string Value2 { get; set; }
}

这是确保验证也在客户端完成的 JavaScript:

$.validator.addMethod('mycustomvalidation',
function (value, element, parameters) {
var otherPropertyValue = $('#' + parameters.otherpropertyname).val();
// Validation logic...
});
$.validator.unobtrusive.adapters.add('mycustomvalidation', ['otherpropertyname'],
function (options) {
options.rules.mycustomvalidation = options.params;
options.messages['mycustomvalidation'] = options.message;
});

带有表单的页面/视图的视图模型如下所示:

public MyViewModel
{
public Report MyReport { get; set; }
}

请注意,我不会将报表用作视图模型,而是用作视图模型中的属性类型。这很重要,因为这是我问题的根源......

视图中显示 Value1 输入的代码并不奇怪(我使用的是 Razor 页面):

<div>
<label asp-for="MyReport.Value1"></label>
<input asp-for="MyReport.Value1" />
<span asp-validation-for="MyReport.Value1"></span>
</div>

输出变为:

<label for="MyReport_Value1">Value1</label>
<input 
type="text" 
id="MyReport_Value1" 
name="MyReport.Value1"
data-val="true" 
data-val-mycustomvalidation="Error..." 
data-val-mycustomvalidation-otherpropertyname="Value2" 
value=""
>
<span
data-valmsg-for="MyReport.Value1" 
data-valmsg-replace="true"
class="text-danger field-validation-valid"
></span>

所以问题是在HTML输出中,我需要data-val-mycustomvalidation-otherpropertyname是"MyReport_Value2"而不仅仅是"Value2"。否则,验证代码将无法找到第二个 HTML 输入(ID 为 MyReport_Value2)并执行验证。

我认为这必须在属性类中的 AddValidation() 方法中完成,但是如何获取 HTML 输入将收到的全名?

我猜有一些方法可以使用上下文参数来获取它。我见过类似"*"的例子。TemplateInfo.GetFullHtmlFieldId(PropertyName)",但我无法让它工作。

任何帮助不胜感激!

您将Value2传递给MyCustomValidationAttribute并使用Value2设置_otherPropertyName,并使用

MergeAttribute(context.Attributes, "data-val-mycustomvalidation-otherpropertyname", _otherProperyName);

这样 HTML 将是

data-val-mycustomvalidation-otherpropertyname="Value2" 

您只需要将Report_Value2传递给MyCustomValidationAttribute而不是Value2

public class Report
{
[MyCustomValidation("Report_Value2", ErrorMessage = "Error...")]
public string Value1 { get; set; }
public string Value2 { get; set; }
}

这样你就会得到data-val-mycustomvalidation-otherpropertyname="Report_Value2"

ValidationContext绑定到属于验证属性的实例,即Model。因此,查找视图模型的引用看起来很困难。 我可以提供三种不同的解决方案,您可以使用哪种解决方案适合您的要求。

解决方案 1:

使用 ValidationContext,您可以获取属性所属类的名称。仅当视图模型属性名称必须与模型类名称相同时,此解决方案才有效。 例如,如果模型类是学生,则属性名称必须是学生。如果属性名称为 Student1,则它不起作用。 即使类名和属性名不同,解决方案 2 和 3 也将起作用。

public class Student
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Please enter name")]
public string Name { get; set; }

[Required]
[Country("Name")]
public string Country { get; set; }
}

视图模型

public class StudentViewModel
{
public Student Student {get;set;} //Solution 1 wil not work for Student1
}

验证属性

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class CountryAttribute : ValidationAttribute, IClientModelValidator
{
private string _otherPropertyName;
private string _clientPropertyName;
public CountryAttribute(string otherPropertyName)
{
_otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{        
var otherProperty = validationContext.ObjectInstance.GetType().GetProperty(_otherPropertyName);
var otherPropertyValue = Convert.ToString(otherProperty.GetValue(validationContext.ObjectInstance, null));
_clientPropertyName = otherProperty.DeclaringType.Name +"_"+ otherProperty.Name;
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-mycustomvalidation-otherpropertyname", _clientPropertyName);
}
}

解决方案 2:

使用 ClientModelValidationContext,您可以获取从控制器传递到视图的 ViewModel 引用。通过使用反射,我们可以获取属性的名称,即模型。 要使用解决方案,您需要从控制器传递空的 ViewModel 引用。

控制器

public IActionResult New()
{
StudentViewModel studentViewModel = new StudentViewModel();
return View(studentViewModel);
}

验证属性

public void AddValidation(ClientModelValidationContext context)
{
var otherClientPropName = context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this._otherPropertyName)
.GetDisplayName();
var viewContext = context.ActionContext as ViewContext;
if (viewContext?.ViewData.Model is StudentViewModel)
{
var model = (StudentViewModel)viewContext?.ViewData.Model;
var instanceName = model.GetType().GetProperties()[0].Name;
otherClientPropName = instanceName + "_" + otherClientPropName;
}
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-mycustomvalidation-otherpropertyname", otherClientPropName);
}

解决方案 3:

使用上下文。属性["id"] 您可以获取当前属性 id 值作为字符串。通过使用字符串操作,您可以获取前缀,然后可以与其他属性名称合并。 此解决方案不需要来自控制器的空视图模型引用。

控制器

public IActionResult New()
{
return View();
}

验证属性

public void AddValidation(ClientModelValidationContext context)
{
var otherClientPropName = context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this._otherPropertyName)
.GetDisplayName();
var id = context.Attributes["id"];
var idPrefix = id.Split("_");
if (idPrefix.Length > 1)
{
otherClientPropName = idPrefix[0] + "_" + otherClientPropName;
}
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-mycustomvalidation-otherpropertyname", otherClientPropName);

}

网页输出

<input class="form-control" type="text" data-val="true" data-val-required="Please enter name" id="Student_Name" name="Student.Name" value="">
<input class="form-control input-validation-error" type="text" data-val="true" data-val-mycustomvalidation-otherpropertyname="Student_Name" data-val-required="The Country field is required." id="Student_Country" name="Student.Country" value="">

当呈现的字段是模型的更深的子级时,此方法也有效。

//Build the client id of the property name.
var dependentClientId = dependentPropertyName;
var clientId = context.Attributes["id"];
var clientIdArr = clientId.Split("_");
if (clientIdArr.Length > 1)
{
//Replace the last value of the array with the dependent property name.
clientIdArr[clientIdArr.Length - 1] = dependentPropertyName;
dependentClientId = string.Join("_", clientIdArr);
}
MergeAttribute(context.Attributes, "data-val-mycustomvalidation-otherpropertyname", dependentClientId );

相关内容

  • 没有找到相关文章

最新更新