web api model binding to an interface

流过昼夜 提交于 2019-12-20 10:45:08

问题


I'm trying to bind a controller action to an interface but still maintain the default binding behavior.

public class CoolClass : ISomeInterface
{
    public DoSomething {get;set;} // ISomeInterface
}

public class DosomethingController : ApiController
{
    public HttpResponseMessage Post(ISomeInterface model)
    {
        // do something with model which should be an instance of CoolClass
    }
} 

The consumer of my service knows nothing of CoolClass so having them add "$type" to the Json they are passing would be a hack in my opinion. I'd like to be able to handle it in the service. If I specify CoolClass as the action parameter it works fine.

EDIT: So I found a partial solution to my question here Dependency injection for ASP.NET Web API action method parameters but there is a follow up issue. That solution does not resolve interface properties. See my example below.

IConcreteClass will be resolved, but ISubtype will not.

public class SubConcreteClass : ISubtype
{
    // properties
}

public class ConcreteClass : IConcreteClass
{
    public ISubtype Subtype {get;set;}
}

Once the media formatter sees that is can resolve the type in IConcreteClass, it then reads the entire stream. So I'm guessing there is no chance to resolve interface members.


回答1:


So in case this can help anyone else, I'll go ahead and post the solution I came up with.

As I mentioned above, the interface parameters of the action method can be resolved using DI. But the interface members of that object need to be handled differently.

I created 2 types of Json converters, a single entity type and a collection type, to decorate the interface properties.

Here's a class that needs to be resolved as an action interface parameter.

public class CreateEnvelopeModel : ICreateEnvelopeCommand
{
    [JsonConverter(typeof(EntityModelConverter<CreateEmailModel, ICreateEmailCommand>))]
    public ICreateEmailCommand Email { get; set; }
    [JsonConverter(typeof(CollectionEntityConverter<CreateFormModel, ICreateFormCommand>))]
    public IList<ICreateFormCommand> Forms { get; set; }
}

Here's the controller action method

public HttpResponseMessage PostEnvelope(ICreateEnvelopeCommand model)
{
    // do stuff
}

Here's the 2 json converters

public class EntityModelConverter<T, Tt> : JsonConverter where T : Tt
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Tt));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return serializer.Deserialize<T>(reader);
    }

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

public class CollectionEntityConverter<T, Tt> : JsonConverter where T : Tt
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(IList<Tt>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        IList<Tt> items = serializer.Deserialize<List<T>>(reader).Cast<Tt>().ToList();
        return items;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value, typeof(IList<T>));
    }
}



回答2:


Look at Model Binders section here. I think it is your case.



来源:https://stackoverflow.com/questions/18324070/web-api-model-binding-to-an-interface

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!