How do I pass an object to HttpClient.PostAsync and serialize as a JSON body?

C#JsonDotnet Httpclient

C# Problem Overview


I'm using System.Net.Http, I found several examples on the web. I managed to create this code for make a POST request:

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

all working fine. But suppose that I want pass a third param to the POST method, a param called data. The data param is an object like this:

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

how can I do that without create the KeyValuePair? My php RestAPI wait a json input, so the FormUrlEncodedContent should send the raw json correctly. But how can I do this with Microsoft.Net.Http? Thanks.

C# Solutions


Solution 1 - C#

The straight up answer to your question is: No. The signature for the PostAsync method is as follows:

>public Task PostAsync(Uri requestUri, HttpContent content)

So, while you can pass an object to PostAsync it must be of type HttpContent and your anonymous type does not meet that criteria.

However, there are ways to accomplish what you want to accomplish. First, you will need to serialize your anonymous type to JSON, the most common tool for this is Json.NET. And the code for this is pretty trivial:

var myContent = JsonConvert.SerializeObject(data);

Next, you will need to construct a content object to send this data, I will use a ByteArrayContent object, but you could use or create a different type if you wanted.

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

Next, you want to set the content type to let the API know this is JSON.

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

Then you can send your request very similar to your previous example with the form content:

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

On a side note, calling the .Result property like you're doing here can have some bad side effects such as dead locking, so you want to be careful with this.

Solution 2 - C#

You need to pass your data in the request body as a raw string rather than FormUrlEncodedContent. One way to do so is to serialize it into a JSON string:

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

Now all you need to do is pass the string to the post method.

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

Solution 3 - C#

A simple solution is to use Microsoft ASP.NET Web API 2.2 Client from NuGet.

Then you can simply do this and it'll serialize the object to JSON and set the Content-Type header to 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);

Solution 4 - C#

New .NET 5 Solution:

In .NET 5, a new class has been introduced called JsonContent, which derives from HttpContent. See in Microsoft docs

This class contains a static method called Create(), which takes any arbitrary object as a parameter, and as the name implies returns an instance of JsonContent, which you can then pass as an argument to the PostAsync method.

Usage:

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

JsonContent content = JsonContent.Create(myObject);

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

Solution 5 - C#

There's now a simpler way with .NET Standard or .NET Core:

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

NOTE: In order to use the JsonMediaTypeFormatter class, you will need to install the Microsoft.AspNet.WebApi.Client NuGet package, which can be installed directly, or via another such as Microsoft.AspNetCore.App.

Using this signature of HttpClient.PostAsync, you can pass in any object and the JsonMediaTypeFormatter will automatically take care of serialization etc.

With the response, you can use HttpContent.ReadAsAsync<T> to deserialize the response content to the type that you are expecting:

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

Solution 6 - C#

@arad good point. In fact I just found this extension method (.NET 5.0):

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

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

So one can now:

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

Solution 7 - C#

You have two options depending on which framework are you coding, you could just do JsonContent.Create(yourObject); if you are on .Net 5

or create and extension method and call it on your object:

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

Solution 8 - C#

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

in my other form

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIlDrugoView Question on Stackoverflow
Solution 1 - C#CodingGorillaView Answer on Stackoverflow
Solution 2 - C#elolosView Answer on Stackoverflow
Solution 3 - C#trydisView Answer on Stackoverflow
Solution 4 - C#AradView Answer on Stackoverflow
Solution 5 - C#Ken LyonView Answer on Stackoverflow
Solution 6 - C#Charles ThayerView Answer on Stackoverflow
Solution 7 - C#Gustavo MoraesView Answer on Stackoverflow
Solution 8 - C#John Nico NoveroView Answer on Stackoverflow