Modelo MVC requer verdadeiro


86

Existe uma maneira de as anotações de dados exigirem que uma propriedade booleana seja definida como true?

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

Qual é exatamente o caso de uso disso? Você não poderia simplesmente deixar a propriedade ser somente leitura e retornar true o tempo todo?
Jan Thomä

1
É muito para dizer ... ei amigo, você se esqueceu de verificar o Eu concordo ... o que deve invalidar o modelo.
Marty Trenouth

Acho que você gostaria de lidar com isso do lado do cliente.
PsychoCoder

15
@PsychoCoder: Deve ser tratado em ambos os lados ... não apenas no lado do cliente. Eu só estava tentando ver se isso poderia ser tratado com a adição de uma anotação de dados simples.
Marty Trenouth

Respostas:


49

Você pode criar seu próprio validador:

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
}

Eu consideraria melhorar isso com uma implementação do lado do cliente - em vez de usar a validação remota mencionada em outras respostas, use o discreto soletrado aqui: jacopretorius.net/2011/01/client-side-validation-in-mvc-3 .html
SamStephens

Esta é uma solução rápida boa (e testada) para nós. Podemos dispensar a validação do lado do cliente na solução de @dazbradbury (também uma boa) porque só precisamos disso em uma caixa de seleção solitária na página anterior de uma pesquisa.
Seth

return (bool) value == true;esta é uma comparação redundante
T-moty

130

Gostaria de criar um validador para o lado do servidor E do cliente. Usando MVC e validação de formulário discreto, isso pode ser alcançado simplesmente fazendo o seguinte:

Em primeiro lugar, crie uma classe em seu projeto para realizar a validação do lado do servidor assim:

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

Em seguida, anote a propriedade apropriada em seu modelo:

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

E, finalmente, habilite a validação do lado do cliente adicionando o seguinte script à sua Visualização:

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

Nota: Já criamos um método GetClientValidationRules que empurra nossa anotação para a vista de nosso modelo.

Se estiver usando arquivos de recursos para fornecer a mensagem de erro para internacionalização, remova a FormatErrorMessagechamada (ou apenas chame a base) e ajuste o GetClientValidationRulesmétodo da seguinte maneira:

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
Obrigado por isso - funciona muito bem! Funciona melhor com o método de substituição FormatErrorMessage removido - dessa forma, a localização de mensagens de erro dos arquivos de recurso funciona. Meu uso: [EnforceTrue (ErrorMessageResourceType = typeof (ValidationMessages), ErrorMessageResourceName = "TermsAndConditionsRequired")]
Matt Frear

2
Não consigo fazer a validação do lado do cliente funcionar e não consigo perceber o que estou fazendo de errado. Onde exatamente devo colocar o javacsript? Na tag head? Ao lado do controlador?
vsdev

Eu concordo, esta deve ser a resposta
Simua

1
Ótima solução mostrando o poder dos atributos de validação personalizados! Embora eu recomende colocar o script em um arquivo js referenciado globalmente, não as visualizações, para reutilização. Além disso, é melhor lidar com todas as maneiras pelas quais as strings de mensagem podem ser adicionadas: padrão se nenhuma for fornecida, ou a string de mensagem ou de um arquivo de recurso.
jeepwran

1
Ótima solução, obrigado por postar. Para aqueles que não conseguem fazer a validação do lado do cliente funcionar: Você tem que estender a validação do jQuery antes que os controles que ele vai validar tenham sido carregados, então coloque o script na cabeça, e não na janela .onload / $ (document ) .ready () evento.
Evert

93

Eu sei que este é um post mais antigo, mas gostaria de compartilhar uma maneira simples do lado do servidor para fazer isso. Você cria uma propriedade pública definida como true e compara seu bool com essa propriedade. Se o seu bool não estiver marcado (por padrão, falso), o formulário não será validado.

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

Código de navalha

@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 para simplicidade. Para sua informação: Eu tive que tornar pública a propriedade 'isTrue' para que isso funcionasse.
Tod Birdsall

Compare não está lá para mim em MVC4
Michael Rudner Evanchik

Super solução ótima solução
Sreerejith SS

9
E se você adicionar um oculto para a propriedade "isTrue", você obterá validação do lado do cliente
billoreid de

2
Irritar essa solução de grande aparência não funcionou para mim. Testado em Mvc 5.2.3.
harvzor

22

Tentei várias soluções, mas nenhuma delas funcionou completamente para eu conseguir a validação do lado do cliente e do servidor. Então, o que fiz em meu aplicativo MVC 5 para fazê-lo funcionar:

Em seu ViewModel (para validação do lado do servidor):

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

Em sua página do Razor (para validação do lado do cliente):

<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
Solução encantadora!
Tobias,

3
Tome cuidado com o valor do campo oculto ("verdadeiro")!
Tobias,

10

Eu gostaria apenas de direcionar as pessoas para o seguinte Fiddle: https://dotnetfiddle.net/JbPh0X

O usuário adicionou [Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")] à sua propriedade booleana que faz com que a validação do lado do servidor funcione.

Para que a validação do lado do cliente também funcione, eles adicionaram o seguinte script:

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

Basta verificar se a representação da string é igual a True:

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

@JeradRose É validado perfeitamente no servidor. Você está se referindo à validação do lado do cliente?
ta.speot.is

3
Confirmado, isso funciona no lado do servidor, mas não no lado do cliente
Matt Frear

Achei que a validação do lado do servidor pode ter uma exceção de incompatibilidade de tipo tentando comparar um bool a uma string.
Jerad Rose

RegularExpressionAttributeusa internamente Convert.ToStringpara obter a representação de string do valor da propriedade (que é entregue a ela como um object).
ta.speot.is

Acho que esta resposta é mais simples do que @ fields-cage +1 de mim
Aaron Hudon

5

Você pode criar seu próprio atributo ou usar CustomValidationAttribute .

É assim que você usaria o CustomValidationAttribute:

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

onde BoolValidation é definido como:

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

5

[Required]atributo significa exigir qualquer valor - pode ser verdadeiro ou falso. Você teria que usar outra validação para isso.


3

Para ASP.NET Core MVC aqui está a validação de cliente e servidor, com base na solução da 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);
    }
}

E então no cliente:

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

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

Então o uso é:

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

Ao usar esta solução, considere isso . Coloque o código javascript fora de qualquer "jquery $document.ready()/ $(function() { });".
Igor

Outra dica, NÃO coloque o requiredatributo na entrada HTML, como:<input asp-for="..." class="..." id="..." type="checkbox" required/>
Igor

3

Continuando a postagem de ta.speot.is e o comentário de Jerad Rose:

A postagem fornecida não funcionará do lado do cliente com validação discreta. Isso deve funcionar em ambos os campos (cliente e servidor):

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

Não sei se este é um problema de versão mais recente, mas não está funcionando para mim com jquery.validate 1.19.2 e jquery.validate.unobtrusive 3.2.11. O problema parece ser o regexmétodo não intrusivo que primeiro verifica se a caixa de seleção é opcional antes de validar o regex, o que faz sentido, exceto que jquery.validate parece considerar qualquer caixa de seleção não marcada como opcional. tl; dr Ele só executa o regex nas caixas de seleção marcadas. Podemos adicionar um shim para o regex validatormétodo ou apenas criar um validador personalizado.
xr280xr

3

.NET Core MVC - Caixa de seleção obrigatória com anotações de dados

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

Não sei como usar DataAnnotations, mas isso é facilmente feito no seu controlador.

public ActionResult Add(Domain.Something model)
{

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

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

}

A única outra opção seria construir um validador personalizado para o lado do servidor e um validador remoto para o lado do cliente (a validação remota está disponível apenas em MVC3 +)


Já é meio novo como verificar a flag booleana .... queria saber se havia uma anotação de dados para ela.
Marty Trenouth

2

Você tem os itens apropriados configurados no web.config ?

Isso pode fazer com que a validação não funcione.

Você também pode tentar criar um atributo de validação personalizado (uma vez que [Required]só se importa se ele existe ou não e você se preocupa com o valor):

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

Então, uso:

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

A partir daqui .


2

Isto é o que funcionou para mim. Nada mais fez. Mvc 5:

Modelo

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

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

Visão

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

insira a descrição da imagem aqui

insira a descrição da imagem aqui


1

Eu tentei usar a resposta de fields.cage e não funcionou muito bem para mim, mas algo mais simples funcionou, e não tenho certeza exatamente por que (versão diferente do Razor, talvez?), Mas tudo que tive que fazer foi isso:

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

E no arquivo .cshtml:

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

Isso não funciona do lado do cliente para mim. Por algum motivo, o parâmetro passado para o método de regra jquery.validate está [NaN, NaN]onde deveria estar[true, true]
xr280xr

@ xr280xr Mesmo quando o usuário marcou a caixa de seleção?
Dronz

0

Acho que a melhor maneira de lidar com isso é apenas verificar em seu controlador se a caixa é verdadeira, caso contrário, apenas adicione um erro ao seu modelo e faça com que ele exiba novamente sua visualização.

Como afirmado anteriormente, tudo o que [Requerido] faz é garantir que haja um valor e, no seu caso, se não for verificado, você ainda obterá falso.


0

Confira a validação infalível aqui . Você pode baixar / instalar via Nuget.

É uma ótima pequena biblioteca para esse tipo de coisa.


Ehhhh ... Atributos de validação padrão funcionam muito bem.
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 } };
    }
}

Embora este link possa responder à pergunta, é melhor incluir as partes essenciais da resposta aqui e fornecer o link para referência. As respostas somente com link podem se tornar inválidas se a página vinculada mudar.
Ravi Dhoriya ツ

Funciona Verifique este link com o motivo da falha na validação- itmeze.com/2010/12/06/…
dhandapani harikrishnan

Hoje funciona. Você pode ter certeza que continuará funcionando em 5, 10 anos depois? Este banco de dados de perguntas e respostas também foi criado para futuros usuários
Eliyahu
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.