MVC Modeli doğru gerektirir


86

Veri ek açıklamalarında boole özelliğinin true olarak ayarlanmasını gerektirmenin bir yolu var mı?

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

Bunun kullanım durumu tam olarak nedir? Özelliğin salt okunur olmasına ve her zaman doğru değerine dönmesine izin veremez misin?
Jan Thomä

1
Söylemek çok zor ... hey dostum, katılıyorum kontrol etmeyi unuttun ... bu da modeli geçersiz kılar.
Marty Trenouth

Sanırım bu, müşteri tarafında halletmek isteyeceğiniz bir şey.
PsychoCoder

15
@PsychoCoder: Her iki tarafta da ele alınmalı ... sadece müşteri tarafında değil. Ben sadece basit bir veri ek açıklaması ekleyerek ele alınıp alınamayacağını görmek istedim.
Marty Trenouth

Yanıtlar:


49

Kendi doğrulayıcınızı oluşturabilirsiniz:

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
}

Bunu bir istemci tarafı uygulamayla geliştirmeyi düşünürdüm - diğer yanıtlarda belirtilen uzaktan doğrulamayı kullanmak yerine, burada belirtilen göze batmayan ifadeyi
SamStephens

Bu bizim için iyi (ve test edilmiş) hızlı bir çözümdür. @ Dazbradbury'nin çözümünde (aynı zamanda iyi bir çözüm) müşteri tarafı doğrulaması olmadan yapabiliriz çünkü buna yalnızca anketin son sayfasındaki tek bir onay kutusunda ihtiyacımız var.
Seth

return (bool) value == true;bu gereksiz bir karşılaştırmadır
T-moty

130

Hem Sunucu hem de İstemci tarafı için bir doğrulayıcı oluştururdum. MVC ve göze çarpmayan form doğrulama kullanarak, bu basitçe aşağıdakileri yaparak başarılabilir:

İlk olarak, sunucu tarafı doğrulamasını şu şekilde gerçekleştirmek için projenizde bir sınıf oluşturun:

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"
        };
    }
}

Bunu takiben, modelinizdeki uygun mülke açıklama ekleyin:

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

Son olarak, aşağıdaki komut dosyasını Görünümünüze ekleyerek istemci tarafı doğrulamayı etkinleştirin:

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

Not: GetClientValidationRulesAçıklamamızı modelimizden görünüme iten bir yöntem oluşturduk .

Uluslararasılaştırma için hata mesajını sağlamak için kaynak dosyaları kullanıyorsanız, FormatErrorMessagearamayı kaldırın (veya sadece tabanı arayın) ve aşağıdaki GetClientValidationRulesgibi yöntemi değiştirin :

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
Bunun için teşekkürler - harika çalışıyor! Geçersiz kılma FormatErrorMessage yöntemi kaldırıldığında daha iyi çalışır - bu şekilde Kaynak dosyalarından gelen hata mesajlarının yerelleştirilmesi çalışır. Kullanımım: [EnforceTrue (ErrorMessageResourceType = typeof (ValidationMessages), ErrorMessageResourceName = "TermsAndConditionsRequired")]
Matt Frear

2
İstemci tarafı doğrulamasının çalışmasını sağlayamıyorum ve neyi yanlış yaptığımı söyleyemiyorum. Javacript'i tam olarak nereye koymalıyım? Başlık etiketinde mi? Kumandanın yanında mı?
vsdev

Kabul ediyorum, cevap bu olmalı
Simua

1
Özel doğrulama özelliklerinin gücünü gösteren harika bir çözüm! Betiği yeniden kullanmak için görünüm (ler) yerine genel olarak başvurulan bir js dosyasına koymanızı öneririm. Ayrıca, mesaj dizelerinin eklenebileceği tüm yolları işlemek için en iyisi: yoksa varsayılan, mesaj dizesi veya bir kaynak dosyasından.
jeepwran

1
Harika çözüm, gönderdiğiniz için teşekkürler. İstemci tarafı doğrulamasının çalışmasını sağlayamayanlar için: jQuery doğrulamasını, doğrulayacağı kontroller yüklenmeden önce genişletmelisiniz, bu nedenle komut dosyasını window.onload / $ (document ) .ready () olay.
Evert

93

Bunun daha eski bir gönderi olduğunu biliyorum, ancak bunu yapmanın basit bir sunucu tarafı yolunu paylaşmak istedim. True olarak ayarlanmış bir genel mülk oluşturursunuz ve bool'unuzu bu mülkle karşılaştırırsınız. Bool değeriniz kontrol edilmezse (varsayılan olarak yanlış), form doğrulanmayacaktır.

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; }

Jilet kodu

@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
Basitlik için +1. Bilginize: Bunun işe yaraması için 'isTrue' özelliğini halka açık hale getirmem gerekiyordu.
Tod Birdsall

MVC4'te karşılaştırma benim için orada değil
Michael Rudner Evanchik

Süper çözüm harika çözüm
Sreerejith SS

9
Ve "isTrue" özelliği için bir gizli eklerseniz, istemci tarafı doğrulaması elde edersiniz
billoreid

2
Sinir bozucu bu harika görünümlü çözüm benim için işe yaramadı. Mvc 5.2.3'te test edilmiştir.
harvzor

22

Birkaç çözüm denedim, ancak hiçbiri hem istemci hem de sunucu tarafı doğrulamasını almak için tam olarak çalışmadı. MVC 5 uygulamamda onu çalıştırmak için yaptığım şey:

ViewModel'inizde (sunucu tarafı doğrulama için):

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 sayfanızda (istemci tarafı doğrulama için):

<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
Büyüleyici çözüm!
Tobias

3
Gizli alanın değerine dikkat edin ("doğru")!
Tobias

10

İnsanları şu Fiddle'a yönlendirmek istiyorum: https://dotnetfiddle.net/JbPh0X

Kullanıcı [Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")], sunucu tarafı doğrulamasının çalışmasına neden olan boole özelliğine ekledi .

İstemci tarafı doğrulamasının da çalışabilmesi için aşağıdaki komut dosyasını eklediler:

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

Dize temsilinin şuna eşit olup olmadığını kontrol edin True:

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

@JeradRose Sunucuda gayet iyi doğrulandı. İstemci tarafı doğrulamadan mı bahsediyorsunuz?
ta.speot .is

3
Onaylandı, bu sunucu tarafında çalışıyor ancak istemci tarafında
çalışmıyor

Sunucu tarafı doğrulamasının bir bool ile bir dizeyi karşılaştırmaya çalışırken bir tür uyuşmazlığı istisnası olabileceğini düşündüm.
Jerad Rose

RegularExpressionAttributedahili Convert.ToStringolarak mülkün değerinin dize temsilini almak için kullanır (bu, ona bir olarak teslim edilir object).
ta.speot.

Sanırım bu yanıt benden @ Fields-cage +1'den daha basit
Aaron Hudon

5

Kendi özniteliğinizi oluşturabilir veya CustomValidationAttribute'u kullanabilirsiniz .

CustomValidationAttribute şu şekilde kullanılır:

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

BoolValidation şu şekilde tanımlanır:

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

5

[Required]öznitelik, herhangi bir değer gerektirmek anlamına gelir - doğru veya yanlış olabilir. Bunun için başka bir doğrulama kullanmanız gerekir.


3

İçin ASP.NET MVC Çekirdek burada dazbradbury çözümüyle dayalı istemci ve sunucu doğrulama olduğunu

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

Ve sonra müşteride:

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

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

O zaman kullanım:

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

Bu çözümü kullandığınızda, dikkate bu . Javascript kodunu herhangi bir "jquery $document.ready()/ $(function() { });" nin dışına koyun .
Igor

Başka bir ipucu, requiredHTML girdisine öznitelik KOYMAYIN , örneğin:<input asp-for="..." class="..." id="..." type="checkbox" required/>
Igor

3

Ta.speot.is tarafından yazılan gönderi ve Jerad Rose'un yorumu:

Verilen gönderi, göze çarpmayan doğrulama ile istemci tarafında çalışmayacaktır. Bu her iki kampta da çalışmalıdır (istemci ve sunucu):

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

Bunun daha yeni bir sürüm sorunu olup olmadığını bilmiyorum, ancak jquery.validate 1.19.2 ve jquery.validate.unobtrusive 3.2.11 ile benim için çalışmıyor. Sorun, regexgöze batmayan yöntemin, normal ifadeyi doğrulamadan önce onay kutusunun isteğe bağlı olup olmadığını kontrol eden ilk kontrolleri olduğu gibi görünüyor; jquery.validate, işaretlenmemiş herhangi bir onay kutusunu isteğe bağlı olarak kabul ediyor gibi görünmesi dışında mantıklıdır. tl; dr Normal ifadeyi yalnızca işaretli onay kutularında çalıştırır. regex validatorYöntem için bir şim ekleyebilir veya sadece özel bir doğrulayıcı oluşturabiliriz.
xr280xr

3

.NET Core MVC - Veri Açıklamaları ile Gerekli Onay Kutusu

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 yoluyla bir yol bilmiyorum, ancak bu, kontrol cihazınızda kolayca yapılabilir.

public ActionResult Add(Domain.Something model)
{

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

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

}

Diğer tek seçenek, sunucu tarafı için özel bir doğrulayıcı ve istemci tarafı için uzaktan doğrulayıcı oluşturmaktır (uzaktan doğrulama yalnızca MVC3 + 'da mevcuttur)


Boole bayrağının nasıl kontrol edileceği zaten yeni bir tür .... bunun için bir veri açıklaması olup olmadığını öğrenmek istedi.
Marty Trenouth

2

Eğer var mı web.config'de kurmak uygun öğeleri ?

Bu, doğrulamanın çalışmamasına neden olabilir.

Ayrıca özel bir doğrulama özniteliği oluşturmayı da deneyebilirsiniz (çünkü [Required]yalnızca var olup olmadığı ve değeri sizin umursadığınız için):

[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);
    }
}

Ardından, kullanım:

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

Gönderen burada .


2

Bu benim için çalıştı. Başka hiçbir şey yapmadı. Mvc 5:

Modeli

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

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

Görünüm

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

görüntü açıklamasını buraya girin

görüntü açıklamasını buraya girin


1

Fields.cage'in cevabını kullanmayı denedim ve benim için pek işe yaramadı, ancak daha basit bir şey yaptı ve tam olarak neden olduğundan emin değilim (farklı Razor sürümü, belki?), Ama tek yapmam gereken şuydu:

[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; }

Ve .cshtml dosyasında:

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

Bu benim için müşteri tarafında çalışmıyor. Bazı nedenlerden dolayı, jquery.validate kural yöntemine iletilen parametre [NaN, NaN]olması gereken [true, true]
yerdedir

@ xr280xr Kullanıcı onay kutusunu işaretlediğinde bile?
Dronz

0

Sanırım bunu halletmenin en iyi yolu, kutunun doğruysa kontrol cihazınızı kontrol etmektir, aksi takdirde modelinize bir hata ekleyin ve görünümünüzü yeniden göstermesini sağlayın.

Daha önce de belirtildiği gibi, [Gerekli] 'nin yaptığı her şey bir değerin olduğundan emin olmaktır ve sizin durumunuzda kontrol edilmezse yine de yanlış alırsınız.


0

Kusursuz doğrulamaya buradan göz atın . Nuget aracılığıyla indirebilir / kurabilirsiniz.

Bu tür şeyler için harika bir küçük kütüphane.


Ehhhh ... Varsayılan doğrulama öznitelikleri oldukça iyi çalışıyor.
Pangamma

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 } };
    }
}

Bu bağlantı soruyu cevaplayabilirken, cevabın temel kısımlarını buraya eklemek ve referans için bağlantıyı sağlamak daha iyidir. Bağlantılı sayfa değişirse yalnızca bağlantı yanıtları geçersiz hale gelebilir.
Ravi Dhoriya ツ

Çalışıyor Bu bağlantıyı doğrulamanın neden başarısız olduğunu
kontrol edin-

Bugün işe yarıyor. 5-10 yıl sonra da çalışmaya devam edeceğinden emin misiniz? Bu Soru-Cevap DB gelecekteki kullanıcılar için de oluşturuldu
Eliyahu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.