2

Trying to return a correct error message instead of the WebAPI default one {"Message":"The request is invalid.","ModelState" when Json deserialzation fails.

I implemented my custom ActionFilterAttribute:

internal class ValidateModelAttribute : ActionFilterAttribute {
    public override void OnActionExecuting(HttpActionContext actionContext)     {
        if (!actionContext.ModelState.IsValid) {
            actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
        }
    }
}

}

I decorated my Controller method with this attribute:

   [ValidateModelAttribute]
    public async Task<HttpResponseMessage> Put(string username, string serviceId, [FromBody] Dictionary<string, string> jsonData)
    {
      // code
    }

If I set a breakpoint in the OnActionExecuting it only breaks when the jsonData is parsed succesfully as a json. If the json is invalid it never gets into the filter, and the same error message is returned. So looks like this is done somewhere before but all the posts I found say that this should be the place to handle this.

Any idea what's wrong?

Dan Dinu
  • 32,492
  • 24
  • 78
  • 114

1 Answers1

1

The attribute will never get called since deserialization is failing before the method is ever called, meaning that the attributes decorating the method are not called. You need a custom converter (the technique for handling cultures was borrowed from this answer).

public class Testee {}
public class Tester
{
    [JsonConverter(typeof(CustomMesssageConverter<Testee>), "Custom Error Message")]
    public Testee Testee { get; set; }
}

public class CustomMesssageConverter<T> : JsonConverter where T : new()
{
    private string _customErrorMessage;

    public CustomMesssageConverter(string customErrorMessage)
    {
        _customErrorMessage = customErrorMessage;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
       serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            if (reader.TokenType == JsonToken.Null)
                return null;

            // Load JObject from stream
            JObject jObject = JObject.Load(reader);

            // Create target object based on type
            var target = new T();

            //Create a new reader for this jObject, and set all properties to match the original reader.
            JsonReader jObjectReader = jObject.CreateReader();
            jObjectReader.Culture = reader.Culture;
            jObjectReader.DateParseHandling = reader.DateParseHandling;
            jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
            jObjectReader.FloatParseHandling = reader.FloatParseHandling;

            // Populate the object properties
            serializer.Populate(jObjectReader, target);

            return target;
        }
        catch(Exception ex)
        {
            // log ex here
            throw new Exception(_customErrorMessage);
        }
    }

    public override bool CanConvert(Type objectType)
    {
       return typeof(T).IsAssignableFrom(objectType);
    }
}
Community
  • 1
  • 1
David L
  • 32,885
  • 8
  • 62
  • 93