【问题标题】:POSTing JsonObject With HttpClient From Web API从 Web API 使用 HttpClient 发布 JsonObject
【发布时间】:2011-09-01 07:20:23
【问题描述】:

我正在尝试使用来自 Web API 的HttpClient 发布JsonObject。我不太确定如何解决这个问题,并且在示例代码中找不到太多。

这是我目前所拥有的:

var myObject = (dynamic)new JsonObject();
myObject.Data = "some data";
myObject.Data2 = "some more data";

HttpClient httpClient = new HttpClient("myurl");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

HttpResponseMessage response = httpClient.Post("", ???);

我想我需要将我的JsonObject 转换为StreamContent,但我在这一步上挂了。

【问题讨论】:

标签: c# asp.net wcf asp.net-web-api wcf-web-api


【解决方案1】:

最简单的方法是使用 StringContent,以及 JSON 对象的 JSON 表示。

httpClient.Post(
    "",
    new StringContent(
        myObject.ToString(),
        Encoding.UTF8,
        "application/json"));

【讨论】:

  • 记下内容类型。我忽略了它,让我调试的时间比我想要的要长。
  • 难道你不应该处理 StringContent 实例吗?
【解决方案2】:

根据您的 .NET 版本,您还可以使用 HttpClientExtensions.PostAsJsonAsync 方法。

https://msdn.microsoft.com/en-us/library/system.net.http.httpclientextensions.postasjsonasync.aspx

【讨论】:

  • 现在在 Microsoft.AspNet.Client.WebApi nuget 中找到
  • 我刚刚从 Microsoft.AspNet.WebApi.Client 安装了它
  • 这解决了我的问题。在传递一个包含一些属性的 C# 类时,我搞砸了(长时间),这些属性是使用 client.PostAsync、client.SendAsync 的列表。我得到了非常复杂的结果。如果数组是空的,我的 API 解决方案会选择它,但如果数组有一个项目,则控制器方法无法对 JSON 进行模型绑定。谢谢你。在我看来,PostAsJsonAsync 更可靠地将复杂的 C# 对象转换为 JSON。
  • 这个有 nuget 包吗?当我将项目转移到新机器时,我讨厌它,并且这个引用总是丢失。
  • 正在寻找这个或其他东西? nuget.org/packages/Microsoft.AspNet.WebApi.Client
【解决方案3】:

使用新版本的HttpClient 并且没有WebApi 包,它将是:

var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
var result = client.PostAsync(url, content).Result;

或者如果你想要它async:

var result = await client.PostAsync(url, content);

【讨论】:

  • 重载的 StringContent 构造函数对我有用。
  • 尽管blog.stephencleary.com/2012/07/dont-block-on-async-code.html在异步方法上调用 Result 之前请三思而后行
  • 对于任何想像我一样将其放入using 的人:aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
  • 使用这个答案,我不断收到来自 API 的“400 Bad Request”响应,我将我的 JSON 请求发布到(Visual Studio 2017,.NET 4.6.2)。除了var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json"),我还必须设置content.Headers.ContentType = new MediaTypeHeaderValue("application/json");。有关详细信息,请参阅下面的答案。
  • @maxshuty,将 HttpClient 包装在 using 块中适用于您仅在该块中使用它的情况。如果您想重用它或使用它来调用不同的端点,它将重新实例化 HttpClient。将其设置为静态也适用于您的 HttpClient 仅使用一个 DNS 的情况。根据 .NET 版本,您可能希望将 IHttpClientFactory 用于类型化的客户端,然后将该客户端注册为单例。在这种情况下,单例比静态要好。
【解决方案4】:

如果使用 Newtonsoft.Json:

using Newtonsoft.Json;
using System.Net.Http;
using System.Text;

public static class Extensions
{
    public static StringContent AsJson(this object o)
        => new StringContent(JsonConvert.SerializeObject(o), Encoding.UTF8, "application/json");
}

例子:

var httpClient = new HttpClient();
var url = "https://www.duolingo.com/2016-04-13/login?fields=";
var data = new { identifier = "username", password = "password" };
var result = await httpClient.PostAsync(url, data.AsJson())

【讨论】:

  • 这不是特定于 asp.net 核心的,它实际上是通用的,甚至可以到 4.5.6
  • JsonConvert.SerializeObject 问题使用 DateTimes ISO 8601 种类:本地或 UTC...hackered.co.uk/articles/…
【解决方案5】:

我没有足够的声誉来对 pomber 的答案添加评论,所以我发布了另一个答案。使用 pomber 的方法,我不断收到来自我将 JSON 请求发布到(Visual Studio 2017,.NET 4.6.2)的 API 的“400 Bad Request”响应。最终问题被追溯到由 StringContent() 生成的“Content-Type”标头不正确(请参阅https://github.com/dotnet/corefx/issues/7864)。

tl;博士

使用 pomber 的答案加上额外的一行来正确设置请求的标头:

var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var result = client.PostAsync(url, content).Result;

【讨论】:

  • 谢谢你,anthls。 var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json") 还不够。它需要 content.Headers.ContentType = new MediaTypeHeaderValue("application/json");谢谢你拯救了我的理智。
  • 这很好用。为什么“application/json”需要设置两次,一次在构造函数中,一次通过属性?这是一个错误吗?
  • @FestusMartingale:好问题!根据我对 github 问题的阅读(在答案中链接),"application/json"StringContent 构造函数中的传递可能不是必需的,因为它已在生成的 content.Headers.ContentType 属性上显式设置。但是,我还没有在代码中对此进行测试。
  • 服务器似乎不支持完整的内容类型字符串。当您使用构造函数而不覆盖 ContentType 时,它​​会将值设置为 application/json; charset=utf-8
【解决方案6】:

vbnet 中的代码:

dim FeToSend as new (object--> define class)

Dim client As New HttpClient
Dim content = New StringContent(FeToSend.ToString(), Encoding.UTF8,"application/json")
content.Headers.ContentType = New MediaTypeHeaderValue( "application/json" )
Dim risp = client.PostAsync(Chiamata, content).Result

msgbox(risp.tostring)

希望有帮助

【讨论】:

    【解决方案7】:

    谢谢你,但为了

    var result = client.PostAsync(url, content).Result;
    

    我用过

    var result = await client.PostAsync(url, content);
    

    因为 Result 使应用程序锁定高请求

    【讨论】:

    【解决方案8】:

    在做这项工作时,我想一一回答,作为对所有人和我自己的说明:

    根据 Serez 的回答 HttpContent 派生类列表如下 https://stackoverflow.com/a/42380027/914284

    HttpClient PostAsync 有一些背景,具体取决于您处理的上下文!

    • 您可以按您想要发送到服务器的类型发布数据以防万一 服务器上下文如下所示等待它
        [HttpPost]
        public async Task<IActionResult> Submit(MyModel model)
        [HttpPost]
        public async Task<IActionResult> Submit([FromForm] MyModel model)
        [HttpPost]
        public async Task<IActionResult> Submit([FromBody] MyModel model)
    

    在编写 FromForm 或 Body 时,它具有 FromForm 的作用。 FromBody 需要 json 内容,否则它需要 KeyValuePairs 作为行。它们都有一些实现,如下所示:

    对于FromForm:我使用了扩展程序

    public static class HelperExtensions
        {
            public static FormUrlEncodedContent ToFormData(this object obj)
            {
                var formData = obj.ToKeyValue();
    
                return new FormUrlEncodedContent(formData);
            }
    
            public static IDictionary<string, string> ToKeyValue(this object metaToken)
            {
                if (metaToken == null)
                {
                    return null;
                }
    
                // Added by me: avoid cyclic references
                var serializer = new JsonSerializer { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
                if (metaToken is not JToken token)
                {
                    // Modified by me: use serializer defined above
                    return ToKeyValue(JObject.FromObject(metaToken, serializer));
                }
    
                if (token.HasValues)
                {
                    var contentData = new Dictionary<string, string>();
                    foreach (var child in token.Children().ToList())
                    {
                        var childContent = child.ToKeyValue();
                        if (childContent != null)
                        {
                            contentData = contentData.Concat(childContent)
                                                     .ToDictionary(k => k.Key, v => v.Value);
                        }
                    }
    
                    return contentData;
                }
    
                var jValue = token as JValue;
                if (jValue?.Value == null)
                {
                    return null;
                }
    
                var value = jValue?.Type == JTokenType.Date ?
                                jValue?.ToString("o", CultureInfo.InvariantCulture) :
                                jValue?.ToString(CultureInfo.InvariantCulture);
    
                return new Dictionary<string, string> { { token.Path, value } };
            }
        }
    

    对于 FromBody:使用任何 json 转换器库 Newtonsoft 或 microsoft

    using Newtonsoft.Json;
    
    var jsonString = JsonConvert.SerializeObject(obj);
    

    两者都需要根据需要定义内容类型,例如json(Write to header)

    request.Headers.Accept.Clear();
    request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    

    或其他用法

            using (var content = new StringContent(JsonConvert.SerializeObject(answer), System.Text.Encoding.UTF8, "application/json"))
            {
                var answerResponse = await client.PostAsync(url, content);
                //use await it has moved in some context on .core 6.0
            }
    

    如果您应该在上下文中使用授权,您也可以提供如下授权:

    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-10
      • 2021-05-26
      • 2019-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多