【问题标题】:POST to Web API action with IEnumerable<Interface> type parameter使用 IEnumerable<Interface> 类型参数发布到 Web API 操作
【发布时间】:2013-11-20 18:12:47
【问题描述】:

我正在尝试从客户端发布到 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
}

我遇到的问题是 messages 在进入 Web API 控制器操作时始终为空(但不是 null)。我在调试器中验证了客户端的messages 对象在发布之前确实有项目。

我怀疑这可能与尝试传递对象时未将接口类型转换为具体类型有关,但我不知道如何使其工作。我怎样才能做到这一点?

【问题讨论】:

    标签: c# asp.net asp.net-mvc asp.net-web-api dotnet-httpclient


    【解决方案1】:

    我想出了如何在没有自定义模型绑定器的情况下做到这一点。发布答案以防其他人遇到此问题...

    客户:

    // 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 可以确定它是什么类型。

    【讨论】:

    • 谢谢。奇迹般有效。帮了我很多忙。
    • 我建议不要实例化新的SerializerSettings,而是更新默认值(只需按照您在WebApiConfig.cs 中的方式访问它)。这样您就不会丢失其他默认序列化程序设置。
    【解决方案2】:

    为什么在方法中使用接口类型?看起来像 web API,不知道应该使用哪种实例来实现消息参数。看来您必须为此操作编写自定义模型绑定器。

    【讨论】:

    • 感谢您的回复。我正在使用接口类型,因为我需要能够在一个 API 调用中发布不同类型的消息列表。我已经说过,我认为问题在于接口类型没有被转换为具体类型。你所描述的一个例子会很有帮助。
    • 首先,您必须发送一些数据以在您的请求中指定 IMessageApiEntity 的具体类型。那么您应该实现您的 IModelBinder (small example) 并毕竟将 [FromBody] 属性替换为您的“消息”方法参数的 [ModelBinder(typeof(YourCustomModelBinderTypeName))]
    【解决方案3】:

    几周前,我在使用 .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)。

    希望这对其他人有帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-13
      • 2013-02-08
      • 2015-08-06
      • 2015-04-22
      • 1970-01-01
      • 2020-03-15
      • 2017-06-01
      相关资源
      最近更新 更多