128

On my MVC3 project, I store score prediction for football/soccer/hockey/... sport game. So one of properties of my prediction class looks like this:

[Range(0, 15, ErrorMessage = "Can only be between 0 .. 15")]
[StringLength(2, ErrorMessage = "Max 2 digits")]
[Remote("PredictionOK", "Predict", ErrorMessage = "Prediction can only be a number in range 0 .. 15")]
public int? HomeTeamPrediction { get; set; }

Now, I need also change error message for a data type, int in my case. There is some default one used - "The field HomeTeamPrediction must be a number.". Need to find a way how to change this error message. This validation message also seem to take prediction for Remote validation one.

I've tried [DataType] attribute but this does not seem to be plain number in system.componentmodel.dataannotations.datatype enumeration.

Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
Antonin Jelinek
  • 2,277
  • 2
  • 20
  • 25

9 Answers9

251

For any number validation you have to use different different range validation as per your requirements :

For Integer

[Range(0, int.MaxValue, ErrorMessage = "Please enter valid integer Number")]

for float

[Range(0, float.MaxValue, ErrorMessage = "Please enter valid float Number")]

for double

[Range(0, double.MaxValue, ErrorMessage = "Please enter valid doubleNumber")]
Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
Dilip Langhanoja
  • 4,455
  • 4
  • 28
  • 37
  • 6
    This didn't work for me in my context. If the user enters "asdf", [Range(typeof(decimal), "0", "9999.99", ErrorMessage = "Value for {0} must be between {1} and {2}")] throw an exception. However, if I do [Range(typeof(decimal), "0.1", "9999.99", ErrorMessage = "Value for {0} must be between {1} and {2}")] , the error message will work correctly. 0 vs 0.1 , makes no sense. bug maybe? – Northstrider Jul 31 '14 at 16:29
  • 3
    This "integer" validation treats non-integer values as valid (e.g. 0.3) – kevinpo May 24 '17 at 04:13
91

Try one of these regular expressions:

// for numbers that need to start with a zero
[RegularExpression("([0-9]+)")] 


// for numbers that begin from 1
[RegularExpression("([1-9][0-9]*)")] 

hope it helps :D

Letfar
  • 3,253
  • 6
  • 25
  • 35
Goran Žuri
  • 1,623
  • 14
  • 24
  • 15
    Is there not a simpler way? I would hope for something like: [Numeric(ErrorMessage = "This field must be a number")] – Banford Apr 18 '11 at 14:03
  • 3
    Unfortunately no. You can always write your own validation attribute. – Goran Žuri May 24 '12 at 10:07
  • 2
    This is the better solution, since this covers strings. `int.MaxValue` only covers until `2.147.483.647` – Christian Gollhardt Aug 19 '15 at 17:51
  • 4
    This regex will only work if the property is of type `int`, if you're using a property of type `string` the regex it will also accept `a123` or any other string that has at least a number somewhere. to validate a number from beginning to end, use `^[0-9]+$` for integers or `^[0-9]*\.?[0-9]+$` for floats – Leo Bottaro Mar 25 '21 at 15:35
25

Use regex in data annotation

[RegularExpression("([0-9]+)", ErrorMessage = "Please enter valid Number")]
public int MaxJsonLength { get; set; }
Sathish
  • 2,029
  • 15
  • 13
  • 3
    This seems to work fine in the context of the question, provided that the property is not int, but string. – Paul Feb 24 '17 at 16:11
  • 1
    Why the parenthesis around the regular expression? Could it be just `[0-9]+`? – polkduran Mar 08 '18 at 16:37
  • 3
    This regex will only work if the property is of type `int`, if you're using a property of type `string` the regex it will also accept `a123` or any other string that has at least a number somewhere. to validate a number from beginning to end, use `^[0-9]+$` for integers or `^[0-9]*\.?[0-9]+$` for floats – Leo Bottaro Mar 25 '21 at 15:34
8
public class IsNumericAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value != null)
        {
            decimal val;
            var isNumeric = decimal.TryParse(value.ToString(), out val);

            if (!isNumeric)
            {                   
                return new ValidationResult("Must be numeric");                    
            }
        }

        return ValidationResult.Success;
    }
}
Stuart Dobson
  • 3,001
  • 4
  • 35
  • 37
6

Try this attribute :

public class NumericAttribute : ValidationAttribute, IClientValidatable {

    public override bool IsValid(object value) {
        return value.ToString().All(c => (c >= '0' && c <= '9') || c == '-' || c == ' ');
    }


    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = FormatErrorMessage(metadata.DisplayName),
            ValidationType = "numeric"
        };
        yield return rule;
    }
}

And also you must register the attribute in the validator plugin:

if($.validator){
     $.validator.unobtrusive.adapters.add(
        'numeric', [], function (options) {
            options.rules['numeric'] = options.params;
            options.messages['numeric'] = options.message;
        }
    );
}
Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
Stefan Turcanu
  • 904
  • 9
  • 13
4

I was able to bypass all the framework messages by making the property a string in my view model.

[Range(0, 15, ErrorMessage = "Can only be between 0 .. 15")]
[StringLength(2, ErrorMessage = "Max 2 digits")]
[Remote("PredictionOK", "Predict", ErrorMessage = "Prediction can only be a number in range 0 .. 15")]
public string HomeTeamPrediction { get; set; }

Then I need to do some conversion in my get method:

viewModel.HomeTeamPrediction = databaseModel.HomeTeamPrediction.ToString();

and post method:

databaseModel.HomeTeamPrediction = int.Parse(viewModel.HomeTeamPrediction);

This works best when using the range attribute, otherwise some additional validation would be needed to make sure the value is a number.

You can also specify the type of number by changing the numbers in the range to the correct type:

[Range(0, 10000000F, ErrorMessageResourceType = typeof(GauErrorMessages), ErrorMessageResourceName = nameof(GauErrorMessages.MoneyRange))]
michaela112358
  • 444
  • 4
  • 9
3

You can write a custom validation attribute:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class Numeric : ValidationAttribute
{
    public Numeric(string errorMessage) : base(errorMessage)
    {
    }

    /// <summary>
    /// Check if given value is numeric
    /// </summary>
    /// <param name="value">The input value</param>
    /// <returns>True if value is numeric</returns>
    public override bool IsValid(object value)
    {
        return decimal.TryParse(value?.ToString(), out _);
    }
}

On your property you can then use the following annotation:

[Numeric("Please fill in a valid number.")]
public int NumberOfBooks { get; set; }
Hendrik Clercx
  • 129
  • 1
  • 9
1

almost a decade passed but the issue still valid with Asp.Net Core 2.2 as well.

I managed it by adding data-val-number to the input field the use localization on the message:

<input asp-for="Age" data-val-number="@_localize["Please enter a valid number."]"/>
LazZiya
  • 5,286
  • 2
  • 24
  • 37
1

ASP.NET Core 3.1

This is my implementation of the feature, it works on server side as well as with jquery validation unobtrusive with a custom error message just like any other attribute:

The attribute:

  [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public class MustBeIntegerAttribute : ValidationAttribute, IClientModelValidator
    {
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            var errorMsg = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
            MergeAttribute(context.Attributes, "data-val-mustbeinteger", errorMsg);
        }

        public override bool IsValid(object value)
        {
            return int.TryParse(value?.ToString() ?? "", out int newVal);
        }

        private bool MergeAttribute(
              IDictionary<string, string> attributes,
              string key,
              string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
            attributes.Add(key, value);
            return true;
        }
    }

Client side logic:

$.validator.addMethod("mustbeinteger",
    function (value, element, parameters) {
        return !isNaN(parseInt(value)) && isFinite(value);
    });

$.validator.unobtrusive.adapters.add("mustbeinteger", [], function (options) {
    options.rules.mustbeinteger = {};
    options.messages["mustbeinteger"] = options.message;
});

And finally the Usage:

 [MustBeInteger(ErrorMessage = "You must provide a valid number")]
 public int SomeNumber { get; set; }
HMZ
  • 2,949
  • 1
  • 19
  • 30