5

In WebAPI, I've noticed an inconsistency that is messing with our validation practices. If you send a bad body/payload with a POST in xml, deserialization fails and you get an null pointer. If you send a bad body/payload in JSON, you get an empty object instead. It is misleading and I don't like it. Is there a way to force a null pointer with a failed json deserialization??

UPDATE: I'm not having a deserialization problem. I'm having a behavior problem that seems to be a difference between the DataContractSerializer and the Json.net serializer. When xml fails to deserialize, the payload is null. However, when Json fails to deserialize, it seems that it is instantiating a default instance of the expected payload.

Example of a bad xml payload: enter image description here

Example of the same call using a bad json payload (payload is not null. instead it is a default instance of the payload class)

enter image description here

6
  • 1
    could you share the code which is showing this difference? Commented Apr 23, 2013 at 21:02
  • I'm not sure how to show you as it happens at the entry point. Commented Apr 23, 2013 at 21:05
  • 1
    Well, for starters, show your model, show your Web API controller action that is taking this model as argument and show the XML and JSON you are posting to this action explaining the exact differences you have observed in the behavior. This should be enough to initiate a constructive discussion which is pretty difficult at the current stage of this question. Commented Apr 23, 2013 at 21:06
  • 2
    I think this is a bug. Please file an issue over here: aspnetwebstack.codeplex.com/workitem/list/basic Commented Apr 23, 2013 at 22:35
  • 1
    @KiranChalla I would also say that this is bug. After the line e.ErrorContext.Handled = true;: github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/… should be come return GetDefaultValueForType(type); call somewhere... Because of the e.ErrorContext.Handled = true; the jsonSerializer.Deserialize will return an empty object and not null. Commented Apr 23, 2013 at 22:39

2 Answers 2

4

By default Web.API used the MissingMemberHandling.Ignore setting in the JsonMediaTypeFormatter.

You need to set it to MissingMemberHandling.Error with:

GlobalConfiguration.Configuration
   .Formatters.JsonFormatter
   .SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;

and you should get null when sending JSON like:

{
   "somenotexistingprop": ""
}

However if you send a completely empty JSON object: {} then you will still get an object with empty properties and not null. Because JsonConvert.DeserializeObject returns a empty object if it deserializes an empty JSON (see this unit test of github).

Sign up to request clarification or add additional context in comments.

3 Comments

@Sinaesthetic I have undeleted my answer because this is the solution for your problem but currently it seems there is a bug in Wep.API which prevents this to work...
Apart from making the MissingMemberHandling to "Error", also you can do the following check so that its consistent across Xml and Json formatters if (!ModelState.IsValid) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState)); }
@KiranChalla Unfortunately, and this is going to sound stupid, this doesn't work at the moment because some of the payloads need to be prepped on the server side. In this cases, they come in as invalid so modelstate always fails. We are working to make our attributes smarter, but this is a different step in the same goal.
1

After one of my team members helped me understand why this is the case, following are some notes about this scenario:

  1. We by default do not have "MissingMemberHandling.Error" on Json formatter because this can help in scenarios where you a newer version of client sending data with extra members to an older version of service. The older version of service should still be able to accept this request and ignore the extra properties. This behavior is consistent with how Xml formatter also behaves.

  2. @Sinaesthetic: if you are setting "MissingMemberHandling.Error" and do not want to rely on "ModelState.IsValid" check only, then you could alway check for ModelState's Keys property to figure out if your request is indeed invalid because of a missing member or something else.

Example below:

public class Customer
{
    public int Id { get; set; }

    [MaxLength(5)]
    public string Name { get; set; }

    public Address Address { get; set; }
}

public class Address
{
    public string Line1 { get;set;}
}

//action
public void Post([FromBody]Customer customer)
    {
        if (!ModelState.IsValid)
        {
            ModelStateDictionary msd = new ModelStateDictionary();

            foreach (string key in ModelState.Keys)
            {
                if (ModelState[key].Errors.Count > 0)
                {
                    foreach (ModelError error in ModelState[key].Errors)
                    {
                        Exception ex = error.Exception;

                        if (ex != null 
                            && typeof(JsonSerializationException).IsAssignableFrom(ex.GetType()) 
                            && ex.Message.StartsWith("Could not find member"))
                        {
                            msd.AddModelError(key, ex);
                        }
                    }
                }
            }

            if (msd.Count > 0)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, msd));
            }
        }

        //process the supplied customer
    }

2 Comments

Or you could create another JsonSerializer with MissingMemberHandling.Error, put it in try/catch, then if members are indeed missing, JsonSerializationException gets caught which is pretty detailed and you can always rethrow it.
Nesting like that is bad practice.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.