簡體   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