Atributo para validar Números utilizando Data Annotation em Asp.Net MVC 4 com C# (CSharp)

Autor : Antonio Carlos Ferreira de Azevedo
Postado em : 18/04/2016

Data Annotation Custumizado [NumeroBrasil()]

Nesta postagem você vai aprender a validar um campo Int ou Double usando Data Annotation e jquery.validate onde será feita a validação tanto do lado do cliente como no servidor.
Vamos criar aqui uma classe derivada da ValidationAttribute e criar nossa classe customizada para a validação no servidor.

NumeroBrasilAttribute

ErrorMessage Mensagem padrão de erro.
DecimalRequerido True Se o pondo decimal for requerido False se o ponto decimal não for requerido.
PontoMilharPermitido True se o ponto milhar for permitido False se o ponto milhar não for permitido.

Obs:Não utilizar o ponto milhar quando for tratar diretamente o campo int, double e decimal em sua validação padrão este despreza o valor por não reconhecer o ponto milhar, caso queira utilizar mascaras você pode faze-lo de duas maneiras :

1) Utilize um campo string e converta quando for gravar no banco de dados.
2) Ou veja nossa postagem sobre ModelBinder - Customizando o vinculo do Modelo com o HTML AspNet com C# (CSharp)

Crie uma classe na pasta Models em seu projeto MVC com o nome de NumeroBrasilAttribute.cs copie e cole o código abaixo.

NumeroBrasilAttribute.cs

/* 
 * Atributo NumeroBrasil
 * 
 * Data Annotations para Validar um int ou double.
 * 
 * Visite nossa página http://www.codigoexpresso.com.br
 * 
 * by Antonio Azevedo
 *  
 * Chamada em sua Classe : 
 *   [NumeroBrasil(ErrorMessage="Sua mensagem de erro", DecimalRequerido=true/false, PontoMilharPermitido=true/false)]
 *    
 */
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

public class NumeroBrasilAttribute : ValidationAttribute, IClientValidatable
{
    public Boolean DecimalRequerido { get; set; }
    public Boolean PontoMilharPermitido { get; set; }
    public NumeroBrasilAttribute()
    {
        this.ErrorMessage = "Valor inválido.";
        this.DecimalRequerido = false;
        this.PontoMilharPermitido = false;
    }

    protected override ValidationResult IsValid(
        object value,
        ValidationContext validationContext)
    {

        // Verifica se o Valor é nulo
        if (value == null)
        {
            value = "";
        }

        string newValue = value.ToString();

        // Verifica se Ponto milhar é permitido, se sim retira os pontos para validação
        if (PontoMilharPermitido)
        {
            newValue = newValue.Replace(".", string.Empty);
        }
       
        // Caso o valor informado seja nulo não é requerido retorna sem validar
        if (value.ToString() == "" && DecimalRequerido == false)
        {
            return ValidationResult.Success; ;
        }

        // Verifica se decimal é requerida para fazer a validacao como double ou com integer
        // Se a conversão não for possivel retor com erro padrão
        if (DecimalRequerido==true)
        {
            try
            {
                double x = Convert.ToDouble(newValue);
            }
            catch
            {
                return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
            }
        }
        else
        {
            try
            {
                Int32 x = Convert.ToInt32(newValue);
            }
            catch
            {
                return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
            }
        }

        // Retorna com sucesso caso a converão tenha sido feita
        return ValidationResult.Success;
    }

    // Diretivas para validação do lado do Cliente, implementa com jquery.validate
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
        ModelMetadata metadata,
        ControllerContext context)
    {
        var Rule = new ModelClientValidationRule
        {
            ValidationType = "numerobrasil",
            ErrorMessage = this.FormatErrorMessage(metadata.PropertyName)

        };

        string[] array = new string[] {DecimalRequerido.ToString(),PontoMilharPermitido.ToString()};
      
        Rule.ValidationParameters["params"] = string.Join(",", Array.ConvertAll(array, x => x.ToString()));
    
        yield return Rule;
    }
}

Agora vamos cuidar da validação do lado no cliente, ou seja, no navegado do cliente.
O MVC se encarrega de transportar os parametros para nosso HTML mas mesmo assim temos que fazer toda a validação em JavaScript novamente.

Crie uma arquivo JavaScript na pasta Scripts de seu projeto MVC, de o nome de CustomValidacoes.js copie e cole o código abaixo.

CustomValidacoes.js

// Adicionamos o método ranger para sobrepor o método original e corrigir os valores passados,
// retirando o ponto milhar e trocando a virgula decimal por ponto decimal, 
// Com isto solucionamos os conflitos ao tentar definir um range para um valor quando utilizamos o atributo NumeroBrasil 
$.validator.addMethod('range',
   function (value, element, params) {
    
    // Coleta os parametros passados, valor maximo e minimo
    var min = params[0];
    var max = params[1];

    // Retira todo ponto de milhar formatado no campo valor
    var pos = value.indexOf('.');
    while (pos > -1) {
        value = value.replace('.', '');
        pos = value.indexOf('.');
    }

    // Troca virgula decimal por ponto decimal
    value = value.replace(',', '.');

    // Faz a validacao do valor, comparando com o valor minimo e maximo
    if (value < min)
    {
        return false;
    }

    if (value > max) {
        return false;
    }

    return true;
});


// Adicionamos o metodo number para sobrepor o metodo padrão e atribuindo sempre um retorno verdadeiro para garantir 
// que o JQuery não vai tratar os numeros exibindo mensagens indesejadas. 
$.validator.addMethod('number',
   function (value, element) {
        return true;
});

// Aqui adicionamos os metodos respeitando os nomes gerado em nossa classe atributo
// os nomes devem estar em caixa baixa tanto na integração como aqui no JavaScript (Nome atributo, parametro)
$.validator.unobtrusive.adapters.addSingleVal('numerobrasil', 'params');

//Aqui temos a funcao que Valida do lado do cliente os números digitados utilizando uma expressão regular
$.validator.addMethod('numerobrasil',
function (value, element, params) {

    // Separamos o parametros recebidos
    var parametros = params.split(',');

    // Verifica o Ponto de milhar
    if (parametros[1].toString() == "True") {
        var pos = value.indexOf('.');
        while (pos > -1) {
            value = value.replace('.', '');
            pos = value.indexOf('.');
        }
    }

    // Valida caso o valor não tenha sido preenchido
    if (value.length == 0) {
        return true;
    }

    var expReg = '';

    // atribui a expressao regular com ou sem ponto decimal
    if (parametros[0].toString() == 'False') {
        expReg = /^[-+]?\d*$/;
    }
    else {
        expReg = /^[-+]?\d*\,?\d*$/;
    }

    // Valida a expressão, se for compativel vai retornar validando a campo, caso contrario exibe a mensagem de erro informada no atributo;
    return value.match(expReg);
});

Este arquivo deve ser carregado após o carregamento do jquery.validate, estamos sobrepondo o método number do JQuery.validate para evitar que seja feita a validação padrão.

Exemplo de como utilizar o atributo em sua classe.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using SeuProjeto.Models;

namespace AspNetMVC_Aula.Models
{
    public class ModAluno
    {
       [NumeroBrasil(ErrorMessage = "Número inválido", DecimalRequerido = true, PontoMilharPermitido = false )]
       public double? Valor { get; set; }
    }
}







Comentários

Antonio Azevedo (Administrador) em 23/10/2018 20:39:00
 

Caro Colega Luiz,
Para CPF e CNPJ temos disponível somente os validadores, ou seja, as classe onde fazemos as validações e devem ser carregadas no servidor e não no lado co cliente (Navegador), mas nada impede que as use com Attribute Remote veja como fazer validação remota em Aula #07 - Validação Remota.

Veja como calcular os digitos CPF/CNPJ em nossas postagens abaixo :
Digito verificador CPF
Digito verificador CNPJ

Veja como criar mascaras em j-query:
Mascaras com JQuery-Mask

Obrigado por seu comentário!!!


Luiz Chequini em 23/10/2018 13:00:40
 

Amigo do Código Expresso. Gostaria de saber se você tem validações para CPF CNPJ juntamente com mascara, igual ao do valor monetário que você disponibilizou aqui no site. Referente a campo para Telefone e Celular você tem mascara também ?

Tudo em Asp.Net MVC

Fico no aguardo


Antonio Azevedo (Administrador) em 05/10/2018 23:15:49
 

Olá Luiz, Nem me lembro por que coloquei o Double basicamente os dois são iguais a diferença esta no armazenamento e na precisão, de resto o comportamento será o mesmo, veja a tabela abaixo.

Tipo Precisão Tamanho
Double 15-16 8 bytes
Decimal 28-29 16 bytes

A precisão é a capacidade de armazenamento e o tamanho é o quanto ele vai ocupar de espaço físico.
Obrigado por seu comentário.....


Luiz Chequini em 03/10/2018 02:39:45
 

Amigo parabéns pelo site, só estou com uma dúvida, em seus tutoriais você utiliza double ao invés de decimal, Eu sempre utilizei decimal em aplicações desktop, gostaria de saber de você qual a opção correta ?

Estou utilizando decimal juntamente com a classe deste post e esta funcionando.


Thomaz em 27/05/2016 09:45:23
 

Agora funcionou, valeu.


Antonio Azevedo (Administrador) em 27/05/2016 01:14:58
 

Colega, desculpe ai, realmente fiz alguns testes aqui e o problema era na realidade a formatação, customizei a range do JQuery e agora estamos retirando os pontos de milhar e trocando a virgula decimal por ponto decimal. Testei aqui e agora esta tudo funcionando, verifique no código Java Script e adicione o método
$.validator.addMethod('range'
Que adicionei no código.


Thomaz em 23/05/2016 17:38:17
 

Quando uso seu componente para validar números não estou conseguindo usar o atributo range, só esta validando corretamente o valor minimo, o valor máximo esta dando problema. testei aqui e se não usar seu componente funciona perfeitamente.