1

Testing a WebAPI 2.0 POST with:


(1) Body:

{"Description" : 'a', "Priority" : '1', "IsActive":  'x'}

Is supposed to return:

{"errors": [{
   "code": 1020,
   "message": "The isActive field must have a value of either true, false"
}]}

(2) Body:

{"Description" : 'a', "Priority" : '1'}

Is supposed to return:

{"errors": [{
    "Code": 1010
    "Message":"The isActive field is required"
}]}

(3a) Body:

{"Description" : 'a', "Priority" : '1', "IsActive":  true}

Is supposed to return an empty body


(3b) Body:

{"Description" : 'a', "Priority" : '1', "IsActive":  "true"}

Is supposed to return an empty body


(3c) Body:

{"Description" : 'a', "Priority" : '1', "IsActive":  0}

Is supposed to return an empty body


(3d) Body:

{"Description" : 'a', "Priority" : '1', "IsActive":  1}

Is supposed to return an empty body


(3e) Body:

{"Description" : 'a', "Priority" : '1', "IsActive":  "1"}

Is supposed to return an empty body


(3f) Body:

{"Description" : 'a', "Priority" : '1', "IsActive":  "0"}

Is supposed to return an empty body


However, the AC says that when IsActive is missing it should be:

This is the field I'm using which is part of the class which represents the Body.

[Required(ErrorMessage = "The isActive field must have a value of either true, false")]

//When IsActive is missing, message should be: The IsActive field is Required
//when IsActive is not the correct type, message should be: The isActive field must have a value of either true, false"

[JsonConverter(typeof(JsonBoolConverter))] 
public bool? IsActive { get; set; }

Here's the JsonBoolConverter

public class JsonBoolConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(bool?);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            bool @return = true;
            bool? @nullable = null;

            if (reader.Value != null)
            {
                if (TryConvertToBoolean(reader.Value.ToString(), out @return))
                {
                    @nullable = @return;
                }
            }

            return @nullable;
        }

        private static readonly List<string> TrueString = new List<string>(new string[] { "true", "1", });

        private static readonly List<string> FalseString = new List<string>(new string[] { "false", "0", });

        public static bool TryConvertToBoolean(string input, out bool result)
        {
            // Remove whitespace from string and lowercase
            string formattedInput = input.Trim().ToLower();

            if (TrueString.Contains(formattedInput))
            {
                result = true;
                return true;
            }
            else if (FalseString.Contains(formattedInput))
            {
                result = false;
                return true;
            }
            else
            {
                result = false;
                return false;
            }
        }
    }

If I take the ErrorMessage off the Required attribute, I get 1010 for both. If I add the ErrorMessage to the Required attribute, I get 1020 for both.


Can you suggest how I can I get a different message for these 2 scenarios? I have been looking at this for days.

Andrew Roberts
  • 990
  • 2
  • 12
  • 26

1 Answers1

1

If you need this kind of flexibility then you can't use a bool? field. That's because the model validation runs only after the request body is de-serialized into an object. This means that, after the de-serialization, you would lose any information that could not match your property type (in your case, invalid entries for bool? will be mapped to null because of your custom JsonConverter).

A workaround, if you really need to tell the user that he inserted an invalid value inside the JSON field (and you accepts strings as input) is to make the property itself a string, and than validate it using a regex:

[Required(ErrorMessage = "The isActive field is required")]
[RegularExpression("^(?:true|false|0|1)$", ErrorMessage = "The isActive field must have a value of either true, false")]
public string IsActive { get; set; }

You can than convert your string to a bool using any method you like. For instance you could create a new read only property (never serialized) that returns you the bool? value:

[JsonIgnore]
public bool? IsActiveBool {
    get {
        bool? result = null;

        if (IsActive != null)
        {
            if (TryConvertToBoolean(IsActive , out result)) // <-- Your method here
            {
                return result;
            }
        }
        return null;
   }
}
Federico Dipuma
  • 17,655
  • 4
  • 39
  • 56
  • Thanks so much, Federico. Exactly what I needed. And thanks for editing my formatting. Was a bit messy! – Andrew Roberts Apr 17 '16 at 22:50
  • If I pass an actual boolean into the body like: {"Description" : 'a', "Priority" : '1', "IsActive": true} I get a 1020 error. This also needs to be valid. – Andrew Roberts Apr 17 '16 at 23:24
  • I think this is a larger issue actually. If the class which has FromBody or FromUri Attribute on it is Required and a value of the wrong type is supplied, it will be mapped to null. eg if a string is passed to int, byte, Enum, bool. I would expect an Invalid error, rather than a Required Error. More thoughts? – Andrew Roberts Apr 18 '16 at 07:44
  • In your situation I belive you'll have to write a JsonConverter that maps int and bool to their strings equivalents. You may use this answer as base http://stackoverflow.com/a/14428145/3670737 – Federico Dipuma Apr 18 '16 at 07:57