Model MVC wymaga true


85

Czy istnieje sposób, aby przez adnotacje danych wymagać, aby właściwość logiczna była ustawiona na wartość true?

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

Jaki dokładnie jest przypadek użycia tego? Nie możesz po prostu pozwolić, aby właściwość była tylko do odczytu i przez cały czas zwracała wartość true?
— Jan Thomä

1
To całkiem dużo do powiedzenia ... hej kolego, zapomniałeś sprawdzić Zgadzam się ... co powinno unieważnić model.
— Marty Trenouth,

Myślę, że jest to coś, czym chciałbyś się zająć po stronie klienta.
— PsychoCoder,

15
@PsychoCoder: Powinien być obsługiwany po obu stronach ... nie tylko po stronie klienta. Chciałem tylko sprawdzić, czy można to zrobić, dodając prostą adnotację danych.
— Marty Trenouth,

Odpowiedzi:


49

Możesz stworzyć swój własny walidator:

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
}

Rozważę ulepszenie tego za pomocą implementacji po stronie klienta - zamiast korzystać ze zdalnej weryfikacji, o której mowa w innych odpowiedziach, użyj dyskretnej pisowni tutaj: jacopretorius.net/2011/01/client-side-validation-in-mvc-3 .html
— SamStephens

To dla nas dobre (i sprawdzone) szybkie rozwiązanie. Możemy obejść się bez walidacji po stronie klienta w rozwiązaniu @ dazbradbury (również dobrym), ponieważ potrzebujemy tego tylko w pojedynczym polu wyboru na poprzedniej stronie ankiety.
— Seth

return (bool) value == true;to jest zbędne porównanie
— T-moty

130

Utworzyłbym walidator po stronie serwera i klienta. Korzystając z MVC i dyskretnego sprawdzania poprawności formularzy, można to osiągnąć, wykonując następujące czynności:

Najpierw utwórz klasę w swoim projekcie, aby przeprowadzić walidację po stronie serwera w następujący sposób:

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

Następnie opisz odpowiednią właściwość w modelu:

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

Na koniec włącz walidację po stronie klienta, dodając następujący skrypt do widoku:

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

Uwaga: stworzyliśmy już metodę, GetClientValidationRulesktóra wypycha naszą adnotację do widoku z naszego modelu.

Jeśli używasz plików zasobów do dostarczania komunikatu o błędzie do internacjonalizacji, usuń FormatErrorMessagepołączenie (lub po prostu zadzwoń do bazy) i dostosuj GetClientValidationRulesmetodę w następujący sposób:

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
Dzięki za to - działa świetnie! Działa lepiej z usuniętą metodą override FormatErrorMessage - w ten sposób działa lokalizacja komunikatów o błędach z plików zasobów. Moje zastosowanie: [EnforceTrue (ErrorMessageResourceType = typeof (ValidationMessages), ErrorMessageResourceName = "TermsAndConditionsRequired")]
— Matt Frear

2
Nie mogę uruchomić walidacji po stronie klienta i nie mogę powiedzieć, co robię źle. Gdzie dokładnie powinienem umieścić javacsript? W tagu głowy? Obok kontrolera?
— vsdev,

Zgadzam się, to powinna być odpowiedź
— Simua

1
Świetne rozwiązanie pokazujące moc niestandardowych atrybutów walidacji! Chociaż zalecam umieszczenie skryptu w pliku js, do którego istnieją odniesienia globalne, a nie widok (y), do ponownego wykorzystania. Najlepiej jest również obsłużyć wszystkie sposoby dodawania ciągów komunikatów: domyślny, jeśli nie podano, lub ciąg wiadomości lub z pliku zasobów.
— jeepwran

1
Świetne rozwiązanie, dzięki za wysłanie. Dla tych, którzy nie mogą uruchomić walidacji po stronie klienta: Musisz rozszerzyć walidację jQuery przed załadowaniem kontrolek, które będzie sprawdzać, więc umieść skrypt w nagłówku, a nie w oknie .onload / $ (dokument ) .ready () zdarzenie.
— Evert

92

Wiem, że to starszy post, ale chciałem udostępnić prosty sposób na wykonanie tego po stronie serwera. Tworzysz właściwość publiczną ustawioną na true i porównujesz swoją wartość bool z tą właściwością. Jeśli wartość bool nie jest zaznaczona (domyślnie false), formularz nie zostanie zweryfikowany.

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

Kod Razor

@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 za prostotę. FYI: Musiałem upublicznić właściwość „isTrue”, aby to zadziałało.
— Tod Birdsall

Porównania nie ma dla mnie w MVC4
— Michael Rudner Evanchik

Super rozwiązanie, świetne rozwiązanie
— Sreerejith SS

9
A jeśli dodasz ukrytą właściwość „isTrue”, otrzymasz walidację po stronie klienta
— billoreid,

2
Irytujące to świetnie wyglądające rozwiązanie nie zadziałało dla mnie. Testowany na Mvc 5.2.3.
— harvzor

22

Wypróbowałem kilka rozwiązań, ale żadne z nich nie działało całkowicie dla mnie, aby uzyskać weryfikację zarówno po stronie klienta, jak i serwera. Co więc zrobiłem w mojej aplikacji MVC 5, aby to zadziałało:

W Twoim ViewModel (do weryfikacji po stronie serwera):

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

Na stronie Razor (do weryfikacji po stronie klienta):

<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
Urocze rozwiązanie!
— Tobias,

3
Zwróć uwagę na wartość ukrytego pola („prawda”)!
— Tobias,

10

Chciałbym tylko skierować ludzi do następującego Fiddle: https://dotnetfiddle.net/JbPh0X

Użytkownik dodał [Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")]do swojej właściwości logicznej, co powoduje, że walidacja po stronie serwera działa.

Aby działała również walidacja po stronie klienta, dodali następujący skrypt:

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

Po prostu sprawdź, czy jego reprezentacja w postaci ciągu jest równa True:

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

@JeradRose Jest sprawdzony poprawnie na serwerze. Czy masz na myśli walidację po stronie klienta?
— ta.speot.is

3
Potwierdzone, działa to po stronie serwera, ale nie po stronie klienta
— Matt Frear

Pomyślałem, że walidacja po stronie serwera może mieć wyjątek niezgodności typu, próbując porównać bool z ciągiem.
— Jerad Rose

RegularExpressionAttributewewnętrznie używa Convert.ToStringdo pobrania ciągu reprezentującego wartość właściwości (która jest dostarczana do niego jako object).
— ta.speot.is

Myślę, że ta odpowiedź jest prostsza niż @ fields-cage +1 ode mnie
— Aaron Hudon

5

Możesz utworzyć własny atrybut lub użyć atrybutu CustomValidationAttribute .

Oto jak można użyć atrybutu CustomValidationAttribute:

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

gdzie BoolValidation jest zdefiniowany jako:

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

5

[Required]atrybut oznacza wymaganie dowolnej wartości - może to być prawda lub fałsz. W tym celu musiałbyś użyć innej walidacji.


3

W związku z postem ta.speot.is i komentarzem Jerada Rose'a:

Podany post nie będzie działał po stronie klienta z dyskretną weryfikacją. Powinno to działać w obu obozach (klient i serwer):

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

Nie wiem, czy jest to problem z nowszą wersją, ale nie działa dla mnie z jquery.validate 1.19.2 i jquery.validate.unobtrusive 3.2.11. Wydaje się, że problem polega na tym, że regexmetoda dyskretna definiuje najpierw sprawdzenie, czy pole wyboru jest opcjonalne przed walidacją wyrażenia regularnego, co ma sens, z wyjątkiem tego, że jquery.validate wydaje się traktować dowolne niezaznaczone pole wyboru jako opcjonalne. tl; dr Uruchamia wyrażenie regularne tylko na zaznaczonych polach wyboru. Możemy dodać podkładkę do regex validatormetody lub po prostu stworzyć własny walidator.
— xr280xr

3

.NET Core MVC - wymagane pole wyboru z adnotacjami danych

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

Nie znam sposobu na adnotacje danych, ale można to łatwo zrobić w kontrolerze.

public ActionResult Add(Domain.Something model)
{

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

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

}

Jedyną inną opcją byłoby zbudowanie niestandardowego walidatora po stronie serwera i zdalnego walidatora po stronie klienta (zdalna walidacja jest dostępna tylko w MVC3 +)


Trochę już nowe, jak już sprawdzić flagę boolowską ... chciałem wiedzieć, czy istnieje dla niej adnotacja danych.
— Marty Trenouth

2

Czy masz odpowiednie elementy w pliku web.config ?

Może to spowodować, że walidacja nie zadziała.

Możesz także spróbować utworzyć niestandardowy atrybut walidacji (ponieważ [Required]dba tylko o to, czy istnieje, a Ty dbasz o wartość):

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

Następnie użycie:

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

Od tutaj .


2

To właśnie zadziałało dla mnie. Nic innego nie zrobiło. Mvc 5:

Model

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

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

Widok

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

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj


2

W przypadku ASP.NET Core MVC jest tutaj weryfikacja klienta i serwera, oparta na rozwiązaniu firmy 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);
    }
}

A potem na kliencie:

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

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

Wtedy użycie to:

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

1

Próbowałem użyć odpowiedzi fields.cage i nie do końca mi się to udało, ale zadziałało coś prostszego i nie wiem dokładnie dlaczego (może inna wersja Razor?), Ale wszystko, co musiałem zrobić, to:

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

A w pliku .cshtml:

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

Dla mnie to nie działa po stronie klienta. Z jakiegoś powodu parametr przekazany do metody reguły jquery.validate jest [NaN, NaN]tam, gdzie powinien[true, true]
— xr280xr

@ xr280xr Nawet jeśli użytkownik zaznaczy pole wyboru?
— Dronz

0

Myślę, że najlepszym sposobem na rozwiązanie tego problemu jest po prostu sprawdzenie w kontrolerze, czy pole jest prawdziwe, w przeciwnym razie po prostu dodaj błąd do modelu i poproś o ponowne wyświetlenie widoku.

Jak już wspomniano, wszystko [Wymagane] to upewnienie się, że istnieje wartość, aw twoim przypadku, jeśli nie jest zaznaczone, nadal otrzymasz fałsz.


0

Sprawdź tutaj niezawodną weryfikację . Możesz go pobrać / zainstalować za pośrednictwem Nuget.

To świetna mała biblioteka do tego typu rzeczy.


Ehhhh ... Domyślne atrybuty walidacji działają jednak całkiem dobrze.
— 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 } };
    }
}

Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
— Ravi Dhoriya ツ

To działa Sprawdź ten link i podaj
— itmeze.com/2010/12/06/...

Dziś to działa. Czy możesz być pewien, że będzie działać za 5, 10 lat później? Ta baza danych pytań i odpowiedzi jest również tworzona dla przyszłych użytkowników
— Eliyahu
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.