【问题标题】:Conditional Validation with Attributes in ASP.NET MVC在 ASP.NET MVC 中使用属性进行条件验证
【发布时间】:2014-07-31 16:32:55
【问题描述】:

我正在尝试创建在 ASP.NET 中有条件地验证模型的能力。我让它用于服务器端验证,但我不知道如何让它用于客户端验证。

例如,

public class Student
{
    [Required]
    public string Name { get; set; }
    public bool RequiresAddress { get; set; }
    [RequiredIf("RequiresAddress", true)]
    public string Address { get; set; }
}

这是我的RequiredIf 属性(创建以下this post):

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class RequiredIfAttribute : ValidationAttribute
{
    public string DependentName;
    public object DependentValue;

    public RequiredIfAttribute(string PropertyName, object PropertyValue, string ErrorMessage)
    {
        this.DependentName = PropertyName;
        this.DependentValue = PropertyValue;
        this.ErrorMessage = ErrorMessage;
    }

    public override bool IsValid(object value)
    {
        if (value == null) { return false; }

        string ValueString = value as string;
        if (ValueString != null) { return (ValueString.Trim().Length != 0); }
        return true;
    }
}

然后我有了这个验证器(我使用 DataAnnotationsModelValidatorProvider 在 Global.asax.cs 中注册):

public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
{
    public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
        : base(metadata, context, attribute)
    {
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        var Rule = new ModelClientValidationRule
        {
            ErrorMessage = ErrorMessage,
            ValidationType = "requiredif",
        };

        var viewContext = (ControllerContext as ViewContext);
        string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(Attribute.DependentName);
        Rule.ValidationParameters.Add("dependentname", depProp);
        Rule.ValidationParameters.Add("dependentvalue", Attribute.DependentValue);

        yield return Rule;
    }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        var Field = Metadata.ContainerType.GetProperty(Attribute.DependentName);
        if (Field != null)
        {
            var Value = Field.GetValue(container, null);
            if ((Value == null && Attribute.DependentValue == null) || (Value != null && Value.Equals(Attribute.DependentValue)))
            {
                if (!Attribute.IsValid(Metadata.Model))
                {
                    yield return new ModelValidationResult { Message = ErrorMessage };
                }
            }
        }
    }
}

最后,在我看来,我尝试用 jQuery 注册客户端验证器:

if (typeof (jQuery) !== "undefined" && typeof (jQuery.validator) !== "undefined") {
    (function ($) {
        $.validator.addMethod('requiredif', function (value, element, parameters) {
            var id = '#' + parameters['dependentname'];
            var dependentvalue = parameters['dependentvalue'];
            dependentvalue = (dependentvalue == null ? '' : dependentvalue).toString();
            var actualvalue = $(id).val();
            if (dependentvalue === actualvalue) {
                return $.validator.methods.required.call(this, value, element, parameters);
            }
            else {
                return true;
            }
        });

    })(jQuery);
}

就像我说的,服务器端验证完美无缺,但客户端似乎根本没有工作。我对 JavaScript 有点不了解,所以我不确定如何解决这个问题 - 我在这里错过了什么?

更新

我摆脱了 Validator 类,并在自定义属性上实现了 IClientValidatable。这是我的新RequiredIfAttribute。同样,服务器端的一切似乎都按预期工作,但客户端却不工作。

从我看来,我正在使用 DevExpress 构建表单。

附言The zipped solution can be found here.

public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
    public string DependentName { get; set; }
    public object DependentValue { get; set; }

    public RequiredIfAttribute(string DependentName, object DependentValue)
    {
        this.DependentName  = DependentName;
        this.DependentValue = DependentValue;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata Metadata, ControllerContext Context)
    {   
        var rule = new ModelClientValidationRule();
        rule.ErrorMessage = FormatErrorMessage(Metadata.GetDisplayName());
        rule.ValidationParameters.Add("dependentname", DependentName);
        rule.ValidationParameters.Add("dependentvalue", DependentValue);
        rule.ValidationType = "requiredif";
        yield return rule;
    }

    public override bool IsValid(object value)
    {
        if (value == null) { return false; }

        string ValueString = value as string;
        if (ValueString != null) { return (ValueString.Trim().Length != 0); }
        return true;
    }

    protected override ValidationResult IsValid(object Value, ValidationContext Context)
    {
        var ContainerType = Context.ObjectInstance.GetType();
        var Field = ContainerType.GetProperty(this.DependentName);

        if (Field != null)
        {
            var DependentValue = Field.GetValue(Context.ObjectInstance, null);
            if ((DependentValue == null && DependentValue == null) ||
                (DependentValue != null && DependentValue.Equals(this.DependentValue)))
            {
                if (!IsValid(Value))
                {
                    //return new ValidationResult(ErrorMessage, new[] { Context.MemberName });
                    return new ValidationResult(FormatErrorMessage(Context.DisplayName));
                }
            }
        }
        return ValidationResult.Success;
    }

    public string GetPropertyID(ModelMetadata Metadata, ViewContext Context)
    {
        string DependentID = Context.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentName);
        var Fie1ldID = Metadata.PropertyName + "_";
        
        return (DependentID.StartsWith(FieldID))
            ? DependentID.Substring(FieldID.Length)
            : DependentID;
    }
}

然后这是我的带有相关 JavaScript 的 View 类:

<script type="text/javascript">
function OnValueChanged(s, e) {
    var Result = ASPxClientCheckBox.Cast(s).GetValue();
    StudentForm.GetItemByName("Address").SetVisible(!Result);
}
</script>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(false)
    @Html.DevExpress().FormLayout(Form =>
    {
        Form.Name = "StudentForm";
        Form.Items.Add(x => x.Name, Item =>
        {
            (Item.NestedExtensionSettings as TextBoxSettings).ShowModelErrors = true;
            (Item.NestedExtensionSettings as TextBoxSettings).Properties.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithTooltip;
            (Item.NestedExtensionSettings as TextBoxSettings).Properties.ValidationSettings.Display = Display.Dynamic;
        });

        Form.Items.Add(x => x.Address, Item =>
        {
            Item.Name = "Address";
            (Item.NestedExtensionSettings as TextBoxSettings).ShowModelErrors = true;
            (Item.NestedExtensionSettings as TextBoxSettings).Properties.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithTooltip;
            (Item.NestedExtensionSettings as TextBoxSettings).Properties.ValidationSettings.Display = Display.Dynamic;
        });

        Form.Items.Add(x => x.AddressHidden, Item =>
        {
            Item.Name = "AddressHidden";
            (Item.NestedExtensionSettings as CheckBoxSettings).Properties.ClientSideEvents.Init = "OnValueChanged";
            (Item.NestedExtensionSettings as CheckBoxSettings).Properties.ClientSideEvents.ValueChanged = "OnValueChanged";
        });

        Form.Items.Add(x => x.AddressRequired);

        Form.Items.Add(x =>
        {
            x.NestedExtensionType = FormLayoutNestedExtensionItemType.Button;
            ButtonSettings Settings = (ButtonSettings)x.NestedExtensionSettings;
            Settings.UseSubmitBehavior = true;
            Settings.Text = "Submit";
            Settings.Name = "SubmitButton";
        });
    }).Bind(Model).GetHtml()
}

<script type="text/javascript">
    jQuery.validator.unobtrusive.adapters.add('requiredif', ['dependentname', 'dependentvalue'], function (options) {
    options.rules["requiredif"] = true;
    options.messages["requiredif"] = options.message;
    });

    $.validator.addMethod('requiredif', function (value, element, params) {
        var id = '#' + $(element).attr("data-val-requiredif-dependentname");
        var dependentvalue = $(element).attr("data-val-requiredif-dependentvalue");
        var actualvalue = $(id).is(":checked");
        if (dependentvalue == "True" && actualvalue && value.length <= 0) {
            return false;
        }
        return true;
    });
</script>

【问题讨论】:

标签: c# jquery asp.net-mvc jquery-validate unobtrusive-validation


【解决方案1】:

如果你想传递额外的属性来查看你的RequiredIfAttribute必须实现IClientValidatable接口。可以通过添加这个方法来完成:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules( ModelMetadata metadata, ControllerContext context)
{
    var rule = new ModelClientValidationRule();
    rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
    rule.ValidationParameters.Add("dependentname", DependentName);
    rule.ValidationParameters.Add("dependentvalue", DependentValue);
    rule.ValidationType = "requiredif";
    yield return rule;
}

您的 js 代码需要一些更改。我不是不引人注目的大师,但它对我有用:

if ($.validator && $.validator.unobtrusive) {
    $.validator.unobtrusive.adapters.add('requiredif', ['dependentname', 'dependentvalue'], function(options) {
        options.rules["requiredif"] = options.params;
        options.messages["requiredif"] = options.message;
    });

    $.validator.addMethod('requiredif', function(value, element, params) {
        var dependentElement = $("input[name=" + params["dependentname"] + "]");
        var dependentvalue = params["dependentvalue"];
        var actualvalue = dependentElement.parents(".dxWeb_edtCheckBoxChecked_DevEx:first").length > 0; //is checkbox is checked
        if (dependentvalue == "True" && actualvalue && (value == null || value.length <= 0)) {
            return false;
        }
        return true;
    });
}

此代码仅适用于本示例。它假定RequiresAddress 属性是一个布尔值,它被渲染为 chckbox。它需要更多的编码来处理每种类型。我认为为另一种类型编写另一个验证器会更好。

这对你来说是个好解决方案吗?

顺便说一句。 RequiredIfAttribute 构造函数接受三个参数,在你的例子中 Student 类有两个传递;)

【讨论】:

  • 嗯,它似乎不起作用。我之前没有提到这一点——但我正在使用 DevExpress 来构建我的表单。您能帮我准确理解您编写的 jQuery 脚本中发生了什么吗?为什么在第一个函数中使用 'jQuery' 而不是 '$'? “data-val-requiredif-dependentname”指的是什么?
  • 我尝试实施您的更新,但仍然无法正常工作。我在上面发布了更新的代码,并在此处上传了解决方案 (1drv.ms/1kpvbm7)。非常感谢您的所有帮助!
  • 我有编辑代码。现在它应该适用于 DevExpress 表单。但它不适用于标准 mvc。我对 DevExpress 不熟悉,所以有一些 hacky 代码可以检查 RequiresAddress 是否被选中。现在好吗? jQuery$ 是等价的。您可以在示例中同时使用它们。在标准 mvc inpust 中具有 data-val-requiredif-dependentname 等属性,但现在 options.params 在将适配器添加到 $.validator.unobtrusive 时传递,因此不需要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多