【问题标题】:Get full HTML field name for client side validation in ASP.NET Core获取 ASP.NET Core 中客户端验证的完整 HTML 字段名称
【发布时间】:2021-12-19 04:06:25
【问题描述】:

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

我需要找到一种方法来获取其他属性的输入将在最终 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; }
}

请注意,我没有使用 Report 作为我的视图模型,而是作为视图模型中属性的类型。这很重要,因为这是我问题的根源......

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

<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 输入将收到的全名?

我猜有一些方法可以通过使用 context 参数来获得它。我见过类似“*.TemplateInfo.GetFullHtmlFieldId(PropertyName)”的例子,但我无法让它工作。

感谢任何帮助!

【问题讨论】:

    标签: asp.net-core validation razor-pages


    【解决方案1】:

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

    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"

    【讨论】:

    • 我想这在这种情况下会起作用,但是如果我需要在没有具有名为 MyReport 的属性的视图模型的其他任何地方使用 Report 类,它将不起作用。我希望找到一个更通用的解决方案,其中 Report 类不必知道视图模型类 (MyReport) 中的属性名称。
    【解决方案2】:

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

    解决方案 1:

    使用 ValidationContext 可以获取属性所属类的名称。仅当 ViewModel 属性名称必须与模型类名称相同时,此解决方案才有效。 例如如果模型类是学生,那么属性名称必须是学生。如果属性名称是 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:

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

    控制器

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

    HTML 输出

    <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="">
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-15
      • 2017-04-07
      • 1970-01-01
      • 2011-07-19
      • 2021-09-24
      • 1970-01-01
      相关资源
      最近更新 更多