使用IEnumerable<Interface>类型参数向Web API动作POST数据

8

我正在尝试从客户端向Web API方法发布数据,代码如下:

// Create list of messages that will be sent
IEnumerable<IMessageApiEntity> messages = new List<IMessageApiEntity>();
// Add messages to the list here. 
// They are all different types that implement the IMessageApiEntity interface.

// Create http client
HttpClient client = new HttpClient {BaseAddress = new Uri(ConfigurationManager.AppSettings["WebApiUrl"])};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

// Post to web api
HttpResponseMessage response = client.PostAsJsonAsync("Communications/Messages", messages).Result;

// Read results
IEnumerable<ApiResponse<IMessageApiEntity>> results = response.Content.ReadAsAsync<IEnumerable<ApiResponse<IMessageApiEntity>>>().Result;

我的Web API控制器操作如下:

public HttpResponseMessage Post([FromBody]IEnumerable<IMessageApiEntity> messages)
{
    // Do stuff
}

我遇到的问题是,在进入Web API控制器操作时,messages总是为空(但不是null)。在发布之前,我已经在调试器中验证了客户端 messages 对象确实有项目。
我怀疑这可能与界面类型未在尝试传递对象时转换为具体类型有关,但我不知道如何使其工作。我该如何实现这一点?
3个回答

8
我找到了一种不需要自定义模型绑定器的方法。为了帮助其他人解决这个问题,我将答案发在下面...

客户端:

// Create list of messages that will be sent
IEnumerable<IMessageApiEntity> messages = new List<IMessageApiEntity>();
// Add messages to the list here. 
// They are all different types that implement the IMessageApiEntity interface.

// Create http client
HttpClient client = new HttpClient {BaseAddress = new Uri(ConfigurationManager.AppSettings["WebApiUrl"])};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

// Post to web api (this is the part that changed)
JsonMediaTypeFormatter json = new JsonMediaTypeFormatter
{
    SerializerSettings =
    {
        TypeNameHandling = TypeNameHandling.All
    }
};
HttpResponseMessage response = client.PostAsync("Communications/Messages", messages, json).Result;

// Read results
IEnumerable<ApiResponse<IMessageApiEntity>> results = response.Content.ReadAsAsync<IEnumerable<ApiResponse<IMessageApiEntity>>>().Result;

在WebApiConfig.cs的Register方法中添加:
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;

关键是将类型作为json的一部分发送,并打开自动类型名称处理,这样Web API就可以确定它是什么类型。


谢谢。运行得很好。帮了我很多忙。 - Ales Potocnik Hahonina
建议不要实例化一个新的SerializerSettings,而是更新默认设置(就像在您的WebApiConfig.cs中访问它一样)。这样,您就不会失去其他默认序列化程序设置。 - Jony Adamit

1
我几个星期前也遇到了一个类似的问题,涉及.NET Core WebAPI。 下面提出的添加以下行的解决方案对我没有起作用:
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;

最终我创建了一个通用对象,可以携带我的IEnumerable,其中T是我想要的类

[Serializable]
public class GenericListContainer<T> where T : class
{
    #region Constructors

    public GenericListContainer()
    {

    }

    public GenericListContainer(IEnumerable<T> list)
    {
        List = list;
    }
    #endregion

    #region Properties

    public IEnumerable<T> List { get; set; }

    #endregion
}

然后我将我的WebAPI方法更改为:

[Route("api/system-users/save-collection-async")]
[HttpPost]
[ProducesResponseType(typeof(string), 200)]       
public async Task<IActionResult> SaveSystemUserCollectionAsync([FromBody] GenericListContainer<SystemUser> dto)
{
    var response = await _systemUserService.SaveSystemUserCollectionAsync(dto.List);
    return Ok(response);
}

该方法返回已保存用户的ID(在我的情况下为Guid)。

希望这能帮助其他人!


我喜欢这个解决方案,它非常具有可扩展性! - undefined

0
为什么在方法中使用接口类型?看起来像是Web API,不知道应该使用什么实例来实现消息参数。似乎你需要为此操作编写自定义模型绑定器。

谢谢您的回复。我使用接口类型,因为我需要能够在一个API调用中发布不同类型的消息列表。我已经说过,我认为问题是接口类型没有转换为具体类型。您描述的示例将非常有帮助。 - mayabelle
首先,您需要发送一些数据来指定请求中IMessageApiEntity的具体类型。然后,您应该实现您的IModelBinder(小例子),最后将[FromBody]属性替换为[ModelBinder(typeof(YourCustomModelBinderTypeName))]以用于您的“messages”方法参数。 - Dmytro Rudenko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接