Модель 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.