Могу ли я передать объект, основанный на интерфейсе, на MVC 4 WebApi POST?

Я хочу иметь API как таковой:

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

Я получаю нулевое значение для "пакета", поэтому мне интересно, что может быть неправильным. Мои единственные мысли в том, что сериализатор по умолчанию JSON не может справиться с этим, но я не понимаю, как его исправить.

Ответы

Ответ 1

Вы можете сделать это довольно легко с помощью настраиваемого связующего. Вот что сработало для меня. (Использование Web API 2 и 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;
    }
}

Контроллер Web API выглядит следующим образом. (Примечание: также следует работать для регулярных действий MVC - я сделал что-то подобное для них и раньше.)

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

Следует также отметить, что при сериализации JSON вы должны сериализовать его с тем же параметром и сериализовать его как интерфейс, чтобы включить автоматический удар и включить подсказку типа. Что-то вроде этого.

    JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto };
    string json = JsonConvert.SerializeObject(command, typeof(ICommand), settings);

Ответ 2

Вы пытаетесь десериализовать интерфейс. Сериализатор не будет знать, какой тип будет создан, если не будет сказано.

Взгляните на параметр TypeNameHandling Публикация коллекции подклассов

Или посмотрите на создание настраиваемого JsonConverter. Взгляните на этот вопрос Как реализовать пользовательский JsonConverter в JSON.NET для десериализации списка объектов базового класса?

Ответ 3

Json.NET(стандартный сериализатор Json для ASP.NET Web API) может справиться с этой ситуацией. Все, что вам нужно сделать, это изменить настройки сериализатора и установить TypeNameHandling для всех (или объектов). Это добавит свойство "$ type" -json в ваш json, содержащий имя типа вашего экземпляра. С другой стороны, он попытается снова десериализовать этот тип.

Мы использовали это для себя, взяв абстрактный базовый класс как тип.

Ответ 4

Сериализатору нужен тип, который он может построить, один с пустым конструктором (по умолчанию). Поскольку интерфейс не может быть сконструирован, сериализация не выполняется, и вы получаете нулевое значение.

Два варианта: