MVC模型要求为true


85

有没有一种方法可以通过数据注释将布尔属性设置为true?

public class MyAwesomeObj{
    public bool ThisMustBeTrue{get;set;}
}

确切的用例是什么?您是否只能让该属性为只读并始终返回true?
JanThomä2011年

1
几乎可以说...嘿,伙计,您忘了检查我同意...,这会使模型无效。
Marty Trenouth 2011年

我认为这是您要处理的客户端问题。
PsychoCoder 2011年

15
@PsychoCoder:应该在两面都进行处理……而不仅仅是客户端。我只是想看看是否可以通过添加简单的数据注释来处理它。
Marty Trenouth'1

Answers:


49

您可以创建自己的验证器:

public class IsTrueAttribute : ValidationAttribute
{
    #region Overrides of ValidationAttribute

    /// <summary>
    /// Determines whether the specified value of the object is valid. 
    /// </summary>
    /// <returns>
    /// true if the specified value is valid; otherwise, false. 
    /// </returns>
    /// <param name="value">The value of the specified validation object on which the <see cref="T:System.ComponentModel.DataAnnotations.ValidationAttribute"/> is declared.
    ///                 </param>
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");

        return (bool) value;
    }

    #endregion
}

我会考虑通过客户端实现来增强此功能-与其使用其他答案中提到的远程验证,不如使用此处明确说明的拼写:jacopretorius.net/2011/01/client-side-validation-in-mvc-3 .html
SamStephens 2011年

对于我们来说,这是一个很好的(经过测试的)快速解决方案。我们无需@dazbradbury解决方案(也是一个很好的解决方案)中的客户端验证就可以这样做,因为我们只需要在调查的过去页面上的一个单独的复选框中进行此操作即可。
赛斯,

return (bool) value == true;这是一个多余的比较
T-moty '04

130

我将为服务器端和客户端都创建一个验证器。使用MVC和不打扰的表单验证,只需执行以下操作即可实现:

首先,在您的项目中创建一个类,以执行服务器端验证,如下所示:

public class EnforceTrueAttribute : ValidationAttribute, IClientValidatable
{
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
        return (bool)value == true;
    }

    public override string FormatErrorMessage(string name)
    {
        return "The " + name + " field must be checked in order to continue.";
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new ModelClientValidationRule
        {
            ErrorMessage = String.IsNullOrEmpty(ErrorMessage) ? FormatErrorMessage(metadata.DisplayName) : ErrorMessage,
            ValidationType = "enforcetrue"
        };
    }
}

然后,在模型中注释适当的属性:

[EnforceTrue(ErrorMessage=@"Error Message")]
public bool ThisMustBeTrue{ get; set; }

最后,通过将以下脚本添加到视图中来启用客户端验证:

<script type="text/javascript">
    jQuery.validator.addMethod("enforcetrue", function (value, element, param) {
        return element.checked;
    });
    jQuery.validator.unobtrusive.adapters.addBool("enforcetrue");
</script>

注意:我们已经创建了一种方法GetClientValidationRules,可以将注释从模型推入视图。

如果使用资源文件来提供国际化错误消息,请删除该FormatErrorMessage调用(或仅调用基数)并按如下所示调整GetClientValidationRules方法:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    string errorMessage = String.Empty;
    if(String.IsNullOrWhiteSpace(ErrorMessage))
    {
        // Check if they supplied an error message resource
        if(ErrorMessageResourceType != null && !String.IsNullOrWhiteSpace(ErrorMessageResourceName))
        {
            var resMan = new ResourceManager(ErrorMessageResourceType.FullName, ErrorMessageResourceType.Assembly);
            errorMessage = resMan.GetString(ErrorMessageResourceName);
        }
    }
    else
    {
        errorMessage = ErrorMessage;
    }

    yield return new ModelClientValidationRule
    {
        ErrorMessage = errorMessage,
        ValidationType = "enforcetrue"
    };
}

3
谢谢您-效果很好!在删除了重写FormatErrorMessage方法的情况下,它可以更好地工作-通过这种方式,可以本地化资源文件中的错误消息。我的用法:[EnforceTrue(ErrorMessageResourceType = typeof(ValidationMessages),ErrorMessageResourceName =“ TermsAndConditionsRequired”)]
Matt Frear 2014年

2
我无法使客户端验证正常工作,并且似乎无法告诉我我做错了什么。我到底应该把javacsript放在哪里?在头牌上?在控制器旁边?
vsdev 2014年

我同意,这应该是答案
Simua 2014年

1
出色的解决方案展示了自定义验证属性的强大功能!尽管我建议将脚本放在全局引用的js文件中,而不是在视图中,以便重新使用。另外,最好以所有方式处理可添加消息字符串的方法:如果未提供消息字符串或消息字符串或来自资源文件,则为默认值。
jeepwran 2014年

1
很好的解决方案,感谢您的发布。对于那些无法让客户端验证起作用的人:您必须在加载要验证的控件之前扩展jQuery验证,因此请将脚本放在头而不是window.onload / $(document ).ready()事件。
Evert

92

我知道这是一篇较旧的文章,但想分享一种简单的服务器端方法来做到这一点。您创建一个设置为true的公共属性,然后将布尔值与该属性进行比较。如果您的布尔值未选中(默认为false),则该表单将无法通过验证。

public bool isTrue
{ get { return true; } }

[Required]
[Display(Name = "I agree to the terms and conditions")]
[Compare("isTrue", ErrorMessage = "Please agree to Terms and Conditions")]
public bool AgreeTerms { get; set; }

剃刀代码

@Html.CheckBoxFor(m => Model.AgreeTerms, new { id = "AgreeTerms", @checked = "checked" })
<label asp-for="AgreeTerms" class="control-label"></label>
<a target="_blank" href="/Terms">Read</a>
<br />
@Html.ValidationMessageFor(model => model.AgreeTerms, "", new { @class = "text-danger" })
@Html.HiddenFor(x => Model.isTrue)

12
为简单起见,+ 1。仅供参考:我必须公开'isTrue'属性,才能使其正常工作。
Tod Birdsall

在MVC4中,我不具备比较功能
Michael Rudner Evanchik 2015年

超级解决方案很棒的解决方案
Sreerejith SS 2015年

9
并且,如果您为“ isTrue”属性添加了隐藏的内容,则会获得客户端验证
billoreid

2
烦人的这个好看的解决方案对我不起作用。在Mvc 5.2.3上测试。
harvzor

22

我尝试了几种解决方案,但没有一种对我完全有效,无法同时获得客户端和服务器端验证。因此,我在MVC 5应用程序中所做的就是使其正常工作:

在您的ViewModel中(用于服务器端验证):

public bool IsTrue => true;

[Required]
[Display(Name = "I agree to the terms and conditions")]
[Compare(nameof(IsTrue), ErrorMessage = "Please agree to Terms and Conditions")]
public bool HasAcceptedTermsAndConditions { get; set; }

在您的Razor页面中(用于客户端验证):

<div class="form-group">
   @Html.CheckBoxFor(m => m.HasAcceptedTermsAndConditions)
   @Html.LabelFor(m => m.HasAcceptedTermsAndConditions)
   @Html.ValidationMessageFor(m => m.HasAcceptedTermsAndConditions)

   @Html.Hidden(nameof(Model.IsTrue), "true")
</div>

1
迷人的解决方案!
Tobias

3
注意隐藏字段的值(“ true”)!
Tobias

10

我只想将人们引向以下小提琴:https : //dotnetfiddle.net/JbPh0X

用户添加 [Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")]到其boolean属性中,该属性使服务器端验证起作用。

为了也可以进行客户端验证,他们添加了以下脚本:

// extend jquery range validator to work for required checkboxes
var defaultRangeValidator = $.validator.methods.range;
$.validator.methods.range = function(value, element, param) {
    if(element.type === 'checkbox') {
        // if it's a checkbox return true if it is checked
        return element.checked;
    } else {
        // otherwise run the default validation function
        return defaultRangeValidator.call(this, value, element, param);
    }
}

9

只需检查其字符串表示形式是否等于True

[RegularExpression("True")]
public bool TermsAndConditions { get; set; }

@JeradRose在服务器上已通过验证。您是指客户端验证吗?
ta.speot。是2014年

3
已确认,这在服务器端有效,但在客户端
不起作用

我以为服务器端验证可能会尝试将布尔值与字符串进行比较时出现类型不匹配异常。
Jerad Rose 2014年

RegularExpressionAttribute在内部用于Convert.ToString获取属性值的字符串表示形式(以形式提供给它object)。
ta.speot。是2014年

我认为这个答案比我收到的@ fields-cage +1更简单
Aaron Hudon

5

您可以创建自己的属性,也可以使用CustomValidationAttribute

这是使用CustomValidationAttribute的方式:

[CustomValidation(typeof(BoolValidation), "ValidateBool")]

其中BoolValidation定义为:

public class BoolValidation
{
  public static ValidationResult ValidateBool(bool boolToBeTrue)
  {
    if (boolToBeTrue)
    {
      return ValidationResult.Success;
    }
    else
    {
      return new ValidationResult(
          "Bool must be true.");
    }
  }


3

跟随ta.speot.is的帖子以及Jerad Rose的评论:

给定的帖子将不会在客户端进行不打扰的验证。这在两个阵营(客户端和服务器)中都应该起作用:

[RegularExpression("(True|true)")]
public bool TermsAndConditions { get; set; }

不知道这是否是新版本的问题,但不适用于jquery.validate 1.19.2和jquery.validate.unobtrusive 3.2.11。问题似乎是regexunobtrusive定义的方法先验证复选框是否是可选的,然后再验证正则表达式,这是有道理的,只是jquery.validate似乎认为任何未选中的复选框都是可选的。tl; dr它仅在选中的复选框上运行正则表达式。我们可以为该regex validator方法添加填充程序,也可以只创建一个自定义验证器。
xr280xr

3

.NET Core MVC-带有数据注释的必需复选框

public class MyModel
{
    [Display(Name = "Confirmation")]
    [Range(typeof(bool), "true", "true", ErrorMessage = "Please check the Confirmation checkbox.")]
    public bool IsConfirmed { get; set; }   
}

<div class="custom-control custom-checkbox col-10">
    <input type="checkbox" asp-for="IsConfirmed" class="custom-control-input" />
    <label class="custom-control-label" for="IsConfirmed">
        "By clicking 'submit', I confirm."
    </label>
    <span asp-validation-for="IsConfirmed" class="text-danger"></span>
</div>

<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

<script type="text/javascript">
    $(document).ready(function () {
        // extend range validator method to treat checkboxes differently
        var defaultRangeValidator = $.validator.methods.range;
        $.validator.methods.range = function (value, element, param) {
            if (element.type === 'checkbox') {
                // if it's a checkbox return true if it is checked
                return element.checked;
            } else {
                // otherwise run the default validation function
                return defaultRangeValidator.call(this, value, element, param);
            }
        }
    });
</script>


2

我不知道通过DataAnnotations的方法,但是这很容易在您的控制器中完成。

public ActionResult Add(Domain.Something model)
{

    if (!model.MyCheckBox)
        ModelState.AddModelError("MyCheckBox", "You forgot to click accept");

    if (ModelState.IsValid) {
        //'# do your stuff
    }

}

唯一的其他选择是为服务器端构建自定义验证,为客户端构建远程验证器(远程验证仅在MVC3 +中可用)


Kinda已经很新了,如何检查boolean标志。...想知道是否有数据注释。
Marty Trenouth 2011年

2

您是否在web.config中设置了适当的项目

这可能导致验证无效。

您还可以尝试创建自定义验证属性(因为[Required]仅关心该属性是否存在,并且您关心该值):

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class RequiredTrueAttribute : ValidationAttribute
{
    // Internal field to hold the mask value.
    readonly bool accepted;

    public bool Accepted
    {
        get { return accepted; }
    }

    public RequiredTrueAttribute(bool accepted)
    {
        this.accepted = accepted;
    }

    public override bool IsValid(object value)
    {
        bool isAccepted = (bool)value;
        return (isAccepted == true);
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentCulture,
          ErrorMessageString, name, this.Accepted);
    }
}

然后,用法:

[RequiredTrue(ErrorMessage="{0} requires acceptance to continue.")]
public bool Agreement {get; set;}

这里


2

这对我有用。什么也没做。Mvc 5:

模型

public string True
{
  get
  {
    return "true";
  }
}

[Required]
[Compare("True", ErrorMessage = "Please agree to the Acknowlegement")]
public bool Acknowlegement { get; set; }

视图

  @Html.HiddenFor(m => m.True)
  @Html.EditorFor(model => model.Acknowlegement, new { htmlAttributes = Model.Attributes })
  @Html.ValidationMessageFor(model => model.Acknowlegement, "", new { @class = "text-danger" })

在此处输入图片说明

在此处输入图片说明


2

对于ASP.NET Core MVC,这是基于dazbradbury解决方案的客户端和服务器验证

public class EnforceTrueAttribute : ValidationAttribute, IClientModelValidator
{
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
        return (bool)value;
    }

    public void AddValidation(ClientModelValidationContext context)
    {
        MergeAttribute(context.Attributes, "data-val", "true");
        var errorMessage = ErrorMessage ?? 
            $"The value for field {context.ModelMetadata.GetDisplayName()} must be true.";
        MergeAttribute(context.Attributes, "data-val-enforcetrue", errorMessage);
    }

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

然后在客户端上:

$.validator.addMethod("enforcetrue", function (value, element, param) {
    return element.checked;
});

$.validator.unobtrusive.adapters.addBool("enforcetrue");

然后用法是:

[EnforceTrue(ErrorMessage = "Please tick the checkbox")]
public bool IsAccepted { get; set; }

1

我尝试使用fields.cage的答案,但它对我而言并不奏效,但更简单的方法做了,而且我不确定为什么(不同的Razor版本?),但是我要做的就是:

[Required]
[Range(typeof(bool), "true", "true", ErrorMessage = "Agreement required.")]
[Display(Name = "By clicking here, I agree that my firstborn child will etc etc...")]
public bool Agreement1Checked { get; set; }

在.cshtml文件中:

@Html.CheckBoxFor(m => m.Agreement1Checked)
@Html.LabelFor(m => m.Agreement1Checked)
@Html.ValidationMessageFor(m => m.Agreement1Checked)

这对我而言不适用于客户端。由于某种原因,传递给jquery.validate规则方法的参数[NaN, NaN]应该在此处[true, true]
xr280xr

@ xr280xr即使用户已选中复选框?
Dronz

0

我认为处理此问题的最佳方法是,如果该框为true,则只需检查控制器,否则只需向模型添加错误并使其重新显示视图即可。

如前所述,所有[Required]都要做的是确保有一个值,并且在您的情况下,如果未选中,您仍然会得到false。



0
/// <summary> 
///  Summary : -CheckBox for or input type check required validation is not working the root cause and solution as follows
///
///  Problem :
///  The key to this problem lies in interpretation of jQuery validation 'required' rule. I digged a little and find a specific code inside a jquery.validate.unobtrusive.js file:
///  adapters.add("required", function (options) {
///  if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
///    setValidationValues(options, "required", true);
///    }
///   });
///   
///  Fix: (Jquery script fix at page level added in to check box required area)
///  jQuery.validator.unobtrusive.adapters.add("brequired", function (options) {
///   if (options.element.tagName.toUpperCase() == "INPUT" && options.element.type.toUpperCase() == "CHECKBOX") {
///              options.rules["required"] = true;
///   if (options.message) {
///                   options.messages["required"] = options.message;
///                       }
///  Fix : (C# Code for MVC validation)
///  You can see it inherits from common RequiredAttribute. Moreover it implements IClientValidateable. This is to make assure that rule will be propagated to client side (jQuery validation) as well.
///  
///  Annotation example :
///   [BooleanRequired]
///   public bool iAgree { get; set' }
/// </summary>


public class BooleanRequired : RequiredAttribute, IClientValidatable
{

    public BooleanRequired()
    {
    }

    public override bool IsValid(object value)
    {
        return value != null && (bool)value == true;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        return new ModelClientValidationRule[] { new ModelClientValidationRule() { ValidationType = "brequired", ErrorMessage = this.ErrorMessage } };
    }
}

虽然此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接的页面发生更改,仅链接的答案可能会失效。
拉维·多里亚

它的工作原理请与原因此链接失败的原因在validation- itmeze.com/2010/12/06/...
dhandapani哈克里希纳恩

今天,它起作用了。您确定它会在5年,10年后继续正常工作吗?这些问答数据库也是为将来的用户创建的
Eliyahu 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.