【问题标题】:How do I pass an object to HttpClient.PostAsync and serialize as a JSON body?如何将对象传递给 HttpClient.PostAsync 并序列化为 JSON 主体?
【发布时间】:2016-04-14 14:15:51
【问题描述】:

我使用的是System.Net.Http,我在网上找到了几个例子。我设法创建此代码以发出POST 请求:

public static string POST(string resource, string token)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(baseUri);
        client.DefaultRequestHeaders.Add("token", token);

        var content = new FormUrlEncodedContent(new[]
        {
             new KeyValuePair<string, string>("", "")
        });

        var result = client.PostAsync("", content).Result;
        string resultContent = result.Content.ReadAsStringAsync().Result;
        return resultContent;
    }
 }

一切正常。但是假设我想将第三个参数传递给POST 方法,一个名为data 的参数。数据参数是这样的对象:

object data = new
{
    name = "Foo",
    category = "article"
};

如果不创建KeyValuePair,我该怎么做?我的 php RestAPI 等待 json 输入,所以 FormUrlEncodedContent 应该正确发送 raw json。但是我怎么能用Microsoft.Net.Http 做到这一点?谢谢。

【问题讨论】:

  • 如果我理解你的问题,你想发送 JSON 内容而不是正确的形式编码内容(并且通过扩展你希望你的匿名类型被序列化为 JSON 到该内容中)?
  • @CodingGorilla yes 是匿名类型。
  • 作为对未来读者的附注,不要将using 用于HttpClientaspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
  • 来自 Microsoft 的说明为什么不应使用 usingHttpClient is intended to be instantiated once and reused throughout the life of an application. The following conditions can result in SocketException errors: Creating a new HttpClient instance per request. Server under heavy load. Creating a new HttpClient instance per request can exhaust the available sockets. docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/…
  • 旁注:.Result 将在任何具有同步上下文的环境中死锁。

标签: c# json dotnet-httpclient


【解决方案1】:

您的问题的直接答案是:不。PostAsync 方法的签名如下:

public Task PostAsync(Uri requestUri, HttpContent content)

因此,虽然您可以将 object 传递给 PostAsync,但它必须是 HttpContent 类型,而您的匿名类型不符合该标准。

但是,有一些方法可以完成您想要完成的事情。首先,您需要将匿名类型序列化为 JSON,最常用的工具是 Json.NET。这方面的代码非常简单:

var myContent = JsonConvert.SerializeObject(data);

接下来,您将需要构造一个内容对象来发送此数据,我将使用 ByteArrayContent 对象,但您可以根据需要使用或创建不同的类型。

var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);

接下来,您要设置内容类型以让 API 知道这是 JSON。

byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

然后您可以使用表单内容发送与您之前的示例非常相似的请求:

var result = client.PostAsync("", byteContent).Result

顺便说一句,像您在这里所做的那样调用.Result 属性可能会出现一些bad side effects,例如死锁,因此您要小心处理。

【讨论】:

  • 好的,很清楚。感谢您的回答。只是一个问题:当执行POST, PUT, DELETE 时,通常API 返回TRUE,我将方法声明为string,但是当我这样做时:return result; 我得到:Can't Convert HttpResponseMessage in string,我应该更改方法声明?我需要字符串响应,因为我需要在其他类方法中反序列化它。
  • 如果您需要反序列化响应的正文,那么以您在问题中的方式返回字符串(使用result.Content.ReadAsStringAsync())可能就可以了。根据您的应用程序结构,如果您需要检查标头以确定内容类型是什么(例如 XML 或 JSON),最好直接返回 Content 对象。但如果您知道它总是会返回 JSON(或其他格式),那么只需将响应正文作为字符串返回就可以了。
  • 不好意思问一下,如果数据是StringContent类型,是否需要这样做?
  • @MyDaftQuestions 我不确定你在问什么,但你可以将StringContent 直接传递给PostAsync,因为它派生自HttpContent
  • @CodingGorilla,这我要问的。谢谢你:)
【解决方案2】:

您需要将请求正文中的数据作为原始字符串而不是FormUrlEncodedContent 传递。一种方法是将其序列化为 JSON 字符串:

var json = JsonConvert.SerializeObject(data); // or JsonSerializer.Serialize if using System.Text.Json

现在您需要做的就是将字符串传递给 post 方法。

var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+

var client = new HttpClient();
var response = await client.PostAsync(uri, stringContent);

【讨论】:

  • 什么是stringContent?在我的情况下,stringContent 的值是"\"\""。这是正确的值吗?
  • 是否可以从你的c#代码中获取vb中的字符串结果?我发现它很相似....
【解决方案3】:

一个简单的解决方案是使用NuGet 中的Microsoft ASP.NET Web API 2.2 Client

然后您可以简单地执行此操作,它会将对象序列化为 JSON,并将 Content-Type 标头设置为 application/json; charset=utf-8

var data = new
{
    name = "Foo",
    category = "article"
};

var client = new HttpClient();
client.BaseAddress = new Uri(baseUri);
client.DefaultRequestHeaders.Add("token", token);
var response = await client.PostAsJsonAsync("", data);

【讨论】:

  • 肯定有 PostAsJsonAsync 可以使用
  • 如果它也有相应的 PatchAsJsonAsync 就好了。
【解决方案4】:

新的 .NET 5 解决方案:

在 .NET 5 中,引入了一个名为 JsonContent 的新类,它派生自 HttpContentSee in Microsoft docs

这个类包含一个名为Create()的静态方法,它接受任意对象作为参数,顾名思义,返回一个JsonContent的实例,然后您可以将其作为参数传递给PostAsync方法.

用法:

var myObject = new
{
    foo = "Hello",
    bar = "World",
};

JsonContent content = JsonContent.Create(myObject);

HttpResponseMessage response = await _httpClient.PostAsync("https://...", content);

【讨论】:

  • 我用了你的例子,在调试时我的代码没有任何错误或响应就退出了......从来没有见过这样的......你猜猜它可能是什么?
【解决方案5】:

现在有了.NET Standard.NET Core 的更简单方法:

var client = new HttpClient();
var response = await client.PostAsync(uri, myRequestObject, new JsonMediaTypeFormatter());

注意:要使用JsonMediaTypeFormatter 类,您需要安装Microsoft.AspNet.WebApi.Client NuGet 包,它可以直接安装,也可以通过Microsoft.AspNetCore.App 等其他方式安装。

使用HttpClient.PostAsync的这个签名,你可以传入任何对象,JsonMediaTypeFormatter会自动处理序列化等。

通过响应,您可以使用HttpContent.ReadAsAsync&lt;T&gt; 将响应内容反序列化为您期望的类型:

var responseObject = await response.Content.ReadAsAsync<MyResponseType>();

【讨论】:

  • 这使用的是什么版本的 .net?我的版本在 System.Net.Http 命名空间中找不到“Formatting”
  • @Programmatic 正如我所提到的,您需要使用.NET Standard.NET Core。也许您正在使用.NET Framework?在我的项目中,从这里加载 JsonMediaTypeFormatter:C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnet.webapi.client\5.2.6\lib\netstandard2.0\System.Net.Http.Formatting。 dll
  • @Programmatic 如果您已经在使用其中一种项目类型,则可能需要添加额外的 NuGet 包。我完全忘记了哪些是自动为我包含的。在我的例子中,它包含在 Microsoft.AspNetCore.App NuGet 包中。
  • 我使用的是 .NET Core,但我认为我的解决方案没有设置为使用最新版本的 c# 语言。我更新了,它奏效了。谢谢
  • @Programmatic 不客气。我很高兴听到你成功了!我在关于 NuGet 包的回答中添加了注释。
【解决方案6】:

@arad 好点。其实我只是发现了这个扩展方法(.NET 5.0):

PostAsJsonAsync&lt;TValue&gt;(HttpClient, String, TValue, CancellationToken)

来自https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpclientjsonextensions.postasjsonasync?view=net-5.0

所以现在可以:

var data = new { foo = "Hello"; bar = 42; };
var response = await _Client.PostAsJsonAsync(_Uri, data, cancellationToken);

【讨论】:

    【解决方案7】:

    您有两种选择,具体取决于您编码的框架,如果您使用的是 .Net 5,则可以使用 JsonContent.Create(yourObject);

    或创建和扩展方法并在您的对象上调用它:

    public static StringContent GetStringContent(this object obj)
    {
        var jsonContent = JsonConvert.SerializeObject(obj);
        var contentString = new StringContent(jsonContent, Encoding.UTF8, "application/json");
        contentString.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    
        return contentString;
    }
    

    【讨论】:

      【解决方案8】:
      public static async Task<string> Post(string param, string code, string subject, string description)
          {
              object mydata = new
              {
                  code = code,
                  subject = subject,
                  description = description
              };
              var myContent = JsonConvert.SerializeObject(mydata);
              var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
              var byteContent = new ByteArrayContent(buffer);
              byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
      
              using (HttpClient client = new HttpClient())
              {
                  using (HttpResponseMessage res = await client.PostAsync(baseURL + param, byteContent))
                  {
                      Console.WriteLine("Nico", res);
                      using (HttpContent content = res.Content)
                      {
                          string data = await content.ReadAsStringAsync();
                          if (data != null) { return data; }
                      }
                  }
              }
              return string.Empty;
          }
      

      我的另一种形式

      private async void button2_Click(object sender, EventArgs e) //POST
              {
                  string param = "subject";
                  string code = txtCode.Text; //NC101
                  string subject = txtSubject.Text;
                  string description = txtDescription.Text;
                  var res = await RESTHelper.Post(param, code, subject, description);
                  txtRes.Text = res;
              }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-12
        • 1970-01-01
        • 2017-06-23
        • 2022-01-17
        • 1970-01-01
        • 2011-04-24
        • 1970-01-01
        • 2015-10-06
        相关资源
        最近更新 更多