Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC4 WebAPI reject invalid enum values

How can I make JSON.NET / MVC 4 WebAPI reject integer values for which the enum has no member? Eg:

If I have this model:

public enum Colour { Red = 1 };

public class Model
{
  public Colour Colour { get; set; }
}

Model Post(Model model)
{
   // model.Colour could be 99, 34234234, 0 etc, etc
}

If I post { Color: 9999 }, I end up with a model where model.Color = 999 and I want to return a Bad Request status code instead.

like image 591
Richard Avatar asked Apr 18 '13 11:04

Richard


2 Answers

It turns out the EnumDataTypeAttribute, which comes with the out-of-the-box ValidationAttributes in the System.ComponentModel.DataAnnotations namespace, does an Enum.Defined check.

Once I applied this attribute to my view model, out-of-range integer values failed validation:

public enum Color {Red = 1, Blue = 2}

public class Car
{
    [EnumDataType(typeof(Color))]
    public Color Color { get; set; }
}

Note: values that can be parsed into integers that are defined on the enum will still pass validation due to the default behavior of enum model binding. This means, for example, true will be parsed as 1, which would be valid for this enum. I assume characters that can be mapped to integers will also work.

If you only want one flavor of enum parsing to work, be it string or integer, consider using that specific type on your view model, and then write a custom ValidationAttribute that takes in the enum type, validating that the string or integer on your view model matches a value in the enum.

like image 119
Technetium Avatar answered Sep 25 '22 12:09

Technetium


One option is to write a validator:

public class ValidEnumValueAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        Type enumType = value.GetType();
        bool valid = Enum.IsDefined(enumType, value);
        if (!valid)
        {
            return new ValidationResult(String.Format("{0} is not a valid value for type {1}", value, enumType.Name));
        }
        return ValidationResult.Success;
    }
}

Use as:

public enum Color {Red = 1, Blue = 2}

public class Car
{
    [ValidEnumValue]
    public Color Color { get; set; }
}

In the controller, ModelState.IsValid would be false.
You can also throw a ValidationException, if you really want to fail the request, but I'm not quite sure that is how they should be used.

like image 45
Kobi Avatar answered Sep 24 '22 12:09

Kobi