Модель MVC требует истины


86

Есть ли способ через аннотации данных потребовать, чтобы для логического свойства было установлено значение true?

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

Каков именно вариант использования этого? Разве нельзя позволить свойству быть доступным только для чтения и постоянно возвращать значение true?
Ян Тома

1
Это довольно много, чтобы сказать ... эй, приятель, ты забыл проверить, согласен ... что должно сделать модель недействительной.
Марти Тренут

Я думаю, это то, что вам нужно сделать на стороне клиента.
PsychoCoder

15
@PsychoCoder: это нужно обрабатывать с обеих сторон ... не только на стороне клиента. Я просто хотел посмотреть, можно ли с этим справиться, добавив простую аннотацию к данным.
Марти Тренут

Ответы:


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

Это хорошее (и проверенное) быстрое решение для нас. Мы можем обойтись без проверки на стороне клиента в решении @ dazbradbury (тоже хорошее), потому что нам это нужно только для одного флажка на последней странице опроса.
Сет

return (bool) value == true;это избыточное сравнение
T-moty

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")]
Мэтт Фрир

2
Я не могу заставить работать проверку на стороне клиента и, кажется, не могу сказать, что я делаю не так. Куда именно я должен поместить javacsript? В заголовке тега? Рядом с контроллером?
vsdev

Я согласен, это должен быть ответ
Симуа

1
Отличное решение, показывающее силу настраиваемых атрибутов проверки! Хотя я рекомендую поместить сценарий в файл js с глобальной ссылкой, а не в представления, для повторного использования. Кроме того, лучше всего обрабатывать все способы добавления строк сообщения: по умолчанию, если они не указаны, или строку сообщения, или из файла ресурсов.
jeepwran

1
Отличное решение, спасибо за публикацию. Для тех, кто не может заставить работать валидацию на стороне клиента: вам необходимо расширить валидацию jQuery до того, как элементы управления, которые он будет проверять, будут загружены, поэтому поместите скрипт в заголовок, а не в window.onload / $ (document ) .ready () событие.
Evert

93

Я знаю, что это более старый пост, но хотел поделиться простым способом сделать это на стороне сервера. Вы создаете общедоступное свойство, для которого установлено значение true, и сравниваете свой bool с этим свойством. Если ваш bool не отмечен (по умолчанию 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 общедоступным, чтобы это работало.
Тод Бердсолл,

Для меня нет сравнения в MVC4
Майкл Руднер Эванчик

Супер решение отличное решение
Sreerejith SS 02

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
Очаровательное решение!
Тобиас

3
Позаботьтесь о значении скрытого поля («истина»)!
Тобиас

10

Я просто хотел бы направить людей на следующую скрипку: https://dotnetfiddle.net/JbPh0X

Пользователь добавил [Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")]к своему логическому свойству, которое заставляет работать проверку на стороне сервера.

Чтобы также работала проверка на стороне клиента, они добавили следующий скрипт:

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

3
Подтверждено, что это работает на стороне сервера, но не на стороне клиента
Мэтт Фрир

Я думал, что проверка на стороне сервера может иметь исключение несоответствия типа, пытающееся сравнить логическое значение со строкой.
Jerad Rose

RegularExpressionAttributeвнутренне использует Convert.ToStringдля получения строкового представления значения свойства (которое доставляется ему как object).
ta.speot.is

Я думаю, что этот ответ проще, чем у меня @ fields-cage +1
Аарон Худон

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

5

[Required]Атрибут означает требование любого значения - оно может быть истинным или ложным. Для этого вам нужно будет использовать другую проверку.


3

Для 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; }

При использовании этого решения учитывайте это . Поместите код javascript вне любого "jquery $document.ready()/ $(function() { });".
Игорь

Еще один совет: НЕ помещайте requiredатрибуты во ввод HTML, например:<input asp-for="..." class="..." id="..." type="checkbox" required/>
Игорь

3

Продолжая сообщение ta.speot.is и комментарий Джерада Роуза:

Данный пост не будет работать на стороне клиента с ненавязчивой проверкой. Это должно работать в обоих лагерях (клиент и сервер):

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

Не знаю, относится ли это к более новой версии, но у меня она не работает с jquery.validate 1.19.2 и jquery.validate.unobtrusive 3.2.11. Проблема, похоже, заключается в том, что regexметод ненавязчивого определения сначала проверяет, является ли флажок необязательным, перед проверкой регулярного выражения, что имеет смысл, за исключением того, что 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 +).


Вроде уже ново, как уже проверять логический флаг .... хотел знать, есть ли для него аннотация данных.
Марти Тренут

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

введите описание изображения здесь

введите описание изображения здесь


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

Я думаю, что лучший способ справиться с этим - просто проверить в контроллере, верно ли значение этого поля, в противном случае просто добавьте ошибку в свою модель и заставьте ее повторно отобразить ваше представление.

Как было сказано ранее, все, что делает [Required], - это убедиться, что есть значение, и в вашем случае, если он не отмечен, вы все равно получите false.


0

Ознакомьтесь с проверкой надежной проверки здесь . Вы можете скачать / установить его через Nuget.

Это отличная маленькая библиотека для такого рода вещей.


Эхххх ... Атрибуты проверки по умолчанию работают довольно хорошо.
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 } };
    }
}

Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится.
Рави Дориа ツ

Это работает Проверьте эту ссылку с указанием
dhandapani harikrishnan

Сегодня работает. Можете ли вы быть уверены, что он продолжит работать через 5, 10 лет? Эта база данных вопросов и ответов создана и для будущих пользователей
Элиягу
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.