MVC.NET Core中的条件验证(RequiredIf)

时间:2018-09-13 20:11:47

标签: c# asp.net-core-mvc

我试图有条件地验证MVC.NET Core中的字段。我有两个单选按钮。如果我选择“是”(作为“所有权”),我想在必填字段(活动下拉列表)下方填写一个字段

但是,无论我怎么努力,要验证的值始终来自“活动”字段,而不是来自“所有权”字段(“ N \ A”而不是“是”)

有人可以告诉我我做错了吗

视图(chtml)

<div class=" form-group">
    <div class="bisformdynamiclabel"></div>
    <br />
    @Html.RadioButtonFor(model => model.BIS232Request.JSONData.OwnershipActivity.Ownership, "Yes", new { id = "OwnershipAnswer_true", onclick = "displayOwnershipFieldsRow(true)" })
    <label for="OwnershipAnswer_true">Yes</label>
    @Html.RadioButtonFor(model => model.BIS232Request.JSONData.OwnershipActivity.Ownership, "No", new { id = "OwnershipAnswer_false", onclick = "displayOwnershipFieldsRow(false)" })
    <label for="OwnershipAnswer_false">No</label>
    <span class="alert-danger">
        @Html.ValidationMessage("OwnershipAnswer")
    </span>
</div>
<div class="row ownershipfieldsrow">
    <div class="col-xs-12 col-md-12">
        <div class=" form-group">
            <div class="bisformdynamiclabel"></div>
            <br />
            <input style="display:none" class="form-control" type="text" asp-for="BIS232Request.JSONData.OwnershipActivity.Activity" />
            <select class="form-control ownershipactivityselect" onchange="$('#BIS232Request_JSONData_OwnershipActivity_Activity').val($(this).val());  ">
                <option value="N/A">Please Select</option>
                <option value="Manufacturer">Manufacturer</option>
                <option value="Distributor">Distributor</option>
                <option value="Exporter">Exporter</option>
                <option value="Importer">Importer</option>
                <option value="Other">Other</option>
            </select>
            <span asp-validation-for="BIS232Request.JSONData.OwnershipActivity.Activity" class="alert-danger"></span>
            <span class="alert-danger">
                @Html.ValidationMessage("OwnershipAnswerActivity")
            </span>
        </div>
    </div>

模型

[Required]
public string Ownership { get; set; }
[RequiredIf("Ownership", "OwnershipAnswer_true", "Activity is required if Ownership is selected")]
public string Activity { get; set; }        
public class RequiredIfAttribute : ValidationAttribute
{
    private String PropertyName { get; set; }
    private String ErrorMessage { get; set; }
    private Object DesiredValue { get; set; }

    public RequiredIfAttribute(String propertyName, Object desiredvalue, String errormessage)
    {
        this.PropertyName = propertyName;
        this.DesiredValue = desiredvalue;
        this.ErrorMessage = errormessage;
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        Object instance = context.ObjectInstance;
        Type type = instance.GetType();
        Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
        if (proprtyvalue.ToString() == DesiredValue.ToString() && value == null)
        {
            return new ValidationResult(ErrorMessage);
        }
        return ValidationResult.Success;
    }
}

4 个答案:

答案 0 :(得分:1)

找到答案

已更改

if (proprtyvalue.ToString() == DesiredValue.ToString() && value == null)

if (proprtyvalue.ToString() == DesiredValue.ToString() && value.ToString() == "N/A")

答案 1 :(得分:1)

基于原始实现,我建议扩展RequiredAttribute而不是ValidationAttribute-然后根据[Required]设置默认的ErrorMessage和其他默认值。无论哪种方式,“ errormessage”属性都是多余的,因为您已经将此属性作为ValidationAttribute的属性,并且原始代码为ErrorMessage属性生成了警告-您也可以将nameof用于属性修饰也可以使代码中的内容更加紧密:

我的实现稍微更具体一些,因此,如果某个属性是布尔型,我可以指出该属性是必需的(如果勾选了复选框):

[AttributeUsage(AttributeTargets.Property)]
public class RequiredIfTrueAttribute : RequiredAttribute
{
    private string PropertyName { get; set; }

    public RequiredIfTrueAttribute(string propertyName)
    {
        PropertyName = propertyName;
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        object instance = context.ObjectInstance;
        Type type = instance.GetType();

        bool.TryParse(type.GetProperty(PropertyName).GetValue(instance)?.ToString(), out bool propertyValue);

        if (propertyValue && string.IsNullOrWhiteSpace(value?.ToString()))
        {
            return new ValidationResult(ErrorMessage);
        }

        return ValidationResult.Success;
    }
}

示例用法:

public bool IsBusinessProfile { get; set; }

[RequiredIfTrue(nameof(IsBusinessProfile), ErrorMessage = "ABN is required for Business Profiles")]
public string Abn { get; set; }

答案 2 :(得分:1)

另一种更清洁,更通用的方法是实现更通用的属性,而不是特定的“ requiredIf”属性,因为您将不得不为碰巧使用的每种验证类型创建多个自定义属性。

幸运的是,自.NET Core 2起,Microsoft提供了IPropertyValidationFilter接口,您可以在自定义属性上实现该接口。此接口定义了一个函数ShouldValidateEntry,该函数可以控制是否应验证当前条目;因此,此操作将在调用任何验证程序之前进行。

框架中已经有一个默认的实现ValidateNeverAttribute,但是实现自己的对另一个值进行条件检查很简单:

using System;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;

namespace Foo {
    // Implementation makes use of the IPropertyValidationFilter interface that allows
    // control over whether the attribute (and its children, if relevant) need to be
    // validated.
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class ConditionalValidationAttribute : Attribute, IPropertyValidationFilter {
        public string OtherProperty { get; set; }
        public object OtherValue { get; set; }

        public ConditionalValidationAttribute(string otherProperty, object otherValue) {
            OtherProperty = otherProperty;
            OtherValue = otherValue;
        }

        public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry) {
            // Default behaviour if no other property is set: continue validation
            if (string.IsNullOrWhiteSpace(OtherProperty)) return true;

            // Get the property specified by the name. Might not properly work with
            // nested properties.
            var prop = parentEntry.Metadata.Properties[OtherProperty]?.PropertyGetter?.Invoke(parentEntry.Model);

            return prop == OtherValue;
        }
    }
}

只需使用此属性注释相关属性,任何验证器,以及您自己实现的自定义验证器,都只会在必要时调用!

实施示例:here

答案 3 :(得分:0)

我基于Rob提供的答案。这是一个通用的验证程序,而不是从Required继承,并且还提供了客户端验证。我正在使用.Net Core 3.0

using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System;
using System.Collections.Generic;
using System.Text;

namespace System.ComponentModel.DataAnnotations
{

    [AttributeUsage(AttributeTargets.Property)]
    public class RequiredIfTrueAttribute : ValidationAttribute, IClientModelValidator
    {
        private string PropertyName { get; set; }

        public RequiredIfTrueAttribute(string propertyName)
        {
            PropertyName = propertyName;
            ErrorMessage = "The {0} field is required."; //used if error message is not set on attribute itself
        }

        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            object instance = context.ObjectInstance;
            Type type = instance.GetType();

            bool.TryParse(type.GetProperty(PropertyName).GetValue(instance)?.ToString(), out bool propertyValue);

            if (propertyValue && (value == null || string.IsNullOrWhiteSpace(value.ToString())))
            {
                return new ValidationResult(ErrorMessage);
            }

            return ValidationResult.Success;
        }

        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
            MergeAttribute(context.Attributes, "data-val-requirediftrue", errorMessage);
        }

        private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
            attributes.Add(key, value);
            return true;
        }
    }
}

客户端Javascript

//Custom validation script for the RequiredIfTrue validator
/*
 * Note that, jQuery validation registers its rules before the DOM is loaded. 
 * If you try to register your adapter after the DOM is loaded, your rules will
 * not be processed. So wrap it in a self-executing function.
 * */
(function ($) {

    var $jQval = $.validator;

   $jQval.addMethod("requirediftrue",
       function (value, element, parameters) {
            return value !== "" && value != null;
        }
    );

    var adapters = $jQval.unobtrusive.adapters;
    adapters.addBool('requirediftrue');

})(jQuery);

用法

    public bool IsSpecialField { get; set; }

    [RequiredIfTrue(nameof(IsSpecialField), ErrorMessage="This is my custom error message")]
    [Display(Name = "Address 1")]
    public string Address1 { get; set; }

    [RequiredIfTrue(nameof(IsSpecialField))]
    public string City { get; set; }