ChatGPT解决这个技术问题 Extra ChatGPT

从 Web API 使用 HttpClient 发布 JsonObject

我正在尝试使用来自 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,但我已经挂断了这一步。


D
David Ferenczy Rogožan

使用新版本的 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
使用这个答案,我不断收到来自我正在将我的 JSON 请求发布到(Visual Studio 2017,.NET 4.6.2)的 API 的“400 Bad Request”响应。除了 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 用于类型化的客户端,然后将该客户端注册为单例。在这种情况下,单例比静态要好。
c
carlosfigueira

最简单的方法是使用带有 JSON 对象的 JSON 表示形式的 StringContent

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

注意内容类型。我把它排除在外,让我调试的时间比我想要的要长得多。
你不应该 Dispose StringContent 实例吗?
M
Matthew Steven Monkan

根据您的 .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
M
Matthew Steven Monkan

如果使用 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/…
a
anthls

我没有足够的声誉来对 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;

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

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)

希望这有帮助


k
kachi_dk

我花了几个小时试图解决这个问题。但是@anthls anwser 救了我的皮肤。

var data = new StringContent(JsonConvert.SerializeObject(new
                    {
                        abc = "jsjs",
                        xyz = "hhhh"
                    }));
data.Headers.ContentType = new MediaTypeHeaderValue("application/json"); // <-
var response = client.PostAsync(url, data).Result;

S
Shojaeddin

谢谢你,但是为了

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

我用了

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

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


那是因为第一个例子是阻塞调用,第二个是继续。 Maybe this explanation helps
H
Hamit YILDIRIM

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

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

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

在服务器上下文等待它的情况下,您可以按要发送到服务器的类型发布数据,如下所示

[HttpPost] public async Task Submit(MyModel model) [HttpPost] public async Task Submit([FromForm] MyModel model) [HttpPost] public async Task 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");

M
Manish Patil

我遇到了同样的问题,即 var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");给了“400 Bad Request” Serilizing jsonObject 并在 StringContent() 中传递字符串为我解决了问题,无需单独设置 Encoding.UTF8。


这没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review