Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I pass an interface based object to an MVC 4 WebApi POST?

I want to have an API as such:

public class RelayController : ApiController
{
    // POST api/values
    public void Post([FromBody]IDataRelayPackage package)
    {
        MessageQueue queue = new MessageQueue(".\\private$\\DataRelay");
        queue.Send(package);
        queue.Close();
    }
}

I'm getting a null value for 'package' so I'm wondering what might be going wrong. My only thoughts are that the default JSON serializer can't handle this, but I'm unclear how to fix it.

like image 485
David Cornelson Avatar asked Jan 02 '13 14:01

David Cornelson


2 Answers

You can do this fairly easily with a custom model binder. Here is what worked for me. (Using Web API 2 and JSON.Net 6)

public class JsonPolyModelBinder : IModelBinder
{
    readonly JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto };

    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var content = actionContext.Request.Content;
        string json = content.ReadAsStringAsync().Result;
        var obj = JsonConvert.DeserializeObject(json, bindingContext.ModelType, settings);
        bindingContext.Model = obj;
        return true;
    }
}

The Web API controller looks like this. (Note: should also work for regular MVC actions -- I've done something like this for them before as well.)

public class TestController : ApiController
{
    // POST api/test
    public void Post([ModelBinder(typeof(JsonPolyModelBinder))]ICommand command)
    {
        ...
    }
}

I should also note that when you serialize the JSON, you should serialize it with the same setting, and serialize it as an interface to make the Auto kick in and include the type hint. Something like this.

    JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto };
    string json = JsonConvert.SerializeObject(command, typeof(ICommand), settings);
like image 127
Kasey Speakman Avatar answered Oct 14 '22 18:10

Kasey Speakman


You are trying to deserialise to an interface. The serialiser won't know what type to instantiate unless it is told.

Take a look at TypeNameHandling option Posting a collection of subclasses

Or look at creating a custom JsonConverter. Take a look at this question How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?

like image 29
Mark Jones Avatar answered Oct 14 '22 19:10

Mark Jones