如何将对象传递给 HttpClient.PostAsync 并序列化为 JSON 主体?

我在使用 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?谢谢。

336217 次浏览

对于您的问题,直截了当的回答是: 不。 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属性可能会有一些 副作用很严重,比如死锁,所以要小心使用。

您需要将请求体中的数据作为原始字符串而不是 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);

一个简单的解决方案是使用 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);

.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<T>将响应内容反序列化为您期望的类型:

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

新的.NET 5 + 解决方案:

在.NET5中,引入了一个名为 JsonContent的新类,它派生自 HttpContent.请参阅 Microsoft 文档

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

用法:

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


JsonContent content = JsonContent.Create(myObject);


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

更好的是 ,您实际上可以使用新的 PostAsJsonAsync扩展方法使其更加简洁ーー参见 这些文件。 用法:

var myObject = new
{
foo = "Hello",
bar = "World",
};
await _httpClient.PostAsJsonAsync("https://...", myObject);

@ arad 很有道理,事实上我刚刚发现了这个扩展方法(. NET 5.0) :

PostAsJsonAsync<TValue>(HttpClient, String, TValue, CancellationToken)

https://learn.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);

你有两个选择,这取决于你的编码框架,你可以只做 JsonContent.Create(yourObject);,如果你在。净值5

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

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;
}
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;
}