Search code examples
asp.net-web-apiserializationfrombodyattribute

WebApi [FromBody] parsing


I have an existing controller where [FromBody] is working as expect in HttpPost methods. When writing tests, I found it necessary to use a customer serializer in order to avoid a circular reference due to the parent object having a child that references the parent. The serializer uses these settings:

JsonSerializerSettings Settings = new JsonSerializerSettings()
        {
            TypeNameHandling = TypeNameHandling.Auto,
            ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
            PreserveReferencesHandling = PreserveReferencesHandling.Objects,
            ObjectCreationHandling = ObjectCreationHandling.Auto
        };

The problem is that [FromBody] is unable to parse the object produced by that serializer (it throws a Newtonsoft.Json.JsonSerializationException). However, if I change [FromBody] to be dynamic, e.g.

public IActionResult Update([FromBody]dynamic json)
{
   var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<MyType>(json);
   ...
}

then I'm able to parse the object without a problem. This is confusing me, and I am wondering if I can override what WebApi does for [FromBody] so that I can get the correct object without having to make every method accept a dynamic parameter?


Solution

  • Here is something I did in my WebAPI. I had a Team entity which has many Player entities. Each Player entity had a reference to a Team entity. When I retrieve a Player it will have a Team, and Team will have all players and each player again will have have a team in it.

    To Handle this, I had to change the approach of exposing data and using the data. I created Models for each entities and exposed the model objects. Model objects are flat objects. In case of Player model, it has a TeamID and Team Name rather than using a whole Team object.

    I used a Model-Factory to create Models out of Entities and Entities out of Models. In WebAPI controller, used something like below

            [ModelValidator]
            public IHttpActionResult Post([FromBody] DoctorModel doctorModel)
            {
                try
                {
                    var doctorEntity = ModelFactory.Create(doctorModel);
                    doctorEntity.UserId = Userid;
                    var doctor = UnitOfWork.Doctors.Add(doctorEntity);
                    var doctorModelNew = ModelFactory.Create(doctor);
                    return Ok(doctorModelNew);
                }
                catch (Exception ex)
                {
                    //Logging    
    #if DEBUG
                    return InternalServerError(ex);
    #endif
                    return InternalServerError();
                }
    
            }