繁体   English   中英

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

[英]Get full HTML field name for client side validation in ASP.NET Core

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

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

这演示了如何在 model class 中使用它:

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 页面):

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

output 变为:

<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 output我需要 data-val-mycustomvalidation-otherpropertyname 是 "MyReport_Value2" 而不是 "Value2" 否则,验证代码将无法找到第二个 HTML 输入(带有 id MyReport_Value2)并执行验证。

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

我猜有一些方法可以通过使用 context 参数来实现。 我见过类似“*.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 绑定到属于验证属性的实例,即模型。 因此定位 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="">

当呈现的字段是 model 的较深子级时,这种方法也有效。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM