414

I'm trying to POST a JsonObject using HttpClient from Web API. I'm not quite sure how to go about this and can't find much in the way of sample code.

Here's what I have so far:

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("", ???);

I think I need to cast my JsonObject as a StreamContent but I'm getting hung up on that step.

Mark
  • 21,067
  • 14
  • 53
  • 71
  • Related post - [.NET HttpClient. How to POST string value?](https://stackoverflow.com/q/15176538/465053) – RBT Sep 07 '21 at 04:21

11 Answers11

674

With the new version of HttpClient and without the WebApi package it would be:

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

Or if you want it async:

var result = await client.PostAsync(url, content);
David Ferenczy Rogožan
  • 23,966
  • 9
  • 79
  • 68
pomber
  • 23,132
  • 10
  • 81
  • 94
  • 8
    That overloaded StringContent constructor did the trick for me. – Captain Kenpachi Jan 25 '18 at 13:30
  • 42
    Think twice before calling Result on a Async method though https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – Ruchira Mar 26 '18 at 03:02
  • 8
    For anyone who was tempted to throw this in a `using` like I was: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ – maxshuty Aug 07 '18 at 12:30
  • I put a `using` around the `StringContent` creation though. – bcr Sep 21 '18 at 17:17
  • 12
    Using this answer I kept receiving a "400 Bad Request" response from an API I was POSTing my JSON request to (Visual Studio 2017, .NET 4.6.2). In addition to `var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json")` I had to set `content.Headers.ContentType = new MediaTypeHeaderValue("application/json");`. See answer below for more details. – anthls Jan 09 '19 at 05:27
  • `PostAsJsonAsync` is probably better if available to you - see @user3285954 – Simon_Weaver Feb 03 '19 at 18:24
  • The synchronous approach shown above (i.e. using the Result attribute) can also be used for calls to ReadAsStringAsync e.g. string responseBody = /* await */ response.Content.ReadAsStringAsync().Result; ;) – Zeek2 Jun 27 '19 at 10:55
  • There appears to be a 25MB limit. Any ideas ? – djack109 Apr 23 '20 at 15:03
  • 1
    @maxshuty, wrapping HttpClient in a using block would work for cases where you're only ever using it in that block. If you wanted to re-use it or use it to call a different endpoint, it would re-instantiate the HttpClient. Setting it to static would also work for cases where your HttpClient is only ever using one DNS. Depending on the .NET version, you may want to use an IHttpClientFactory for typed Clients and then registering that client as a singleton. Singleton in this case is better than static. – Saturn K Nov 14 '21 at 04:25
179

The easiest way is to use a StringContent, with the JSON representation of your JSON object.

httpClient.Post(
    "",
    new StringContent(
        myObject.ToString(),
        Encoding.UTF8,
        "application/json"));
carlosfigueira
  • 85,035
  • 14
  • 131
  • 171
81

Depending on your .NET version you could also use HttpClientExtensions.PostAsJsonAsync method.

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

Matthew Steven Monkan
  • 8,170
  • 4
  • 53
  • 71
user3285954
  • 4,499
  • 2
  • 27
  • 19
  • 7
    Now found in Microsoft.AspNet.Client.WebApi nuget – jle Feb 09 '16 at 19:08
  • 3
    I just installed it from Microsoft.AspNet.WebApi.Client – Adriaan Davel Mar 19 '16 at 12:23
  • This solved my problem. I was messing around for a (long) while passing a C# class that contained some properties that were Lists using client.PostAsync, client.SendAsync.. I was getting very mixed results. If the array was empty my API solution would pick it up, but if the array had an item the controller method was not able to model bind the JSON. Thanks for this.. Seems to me that PostAsJsonAsync more reliably converts a complex C# object to JSON. – Franklin Tarter Mar 18 '17 at 01:01
  • Is there a nuget package for this? I hate it when I transfer the project to a new machine, and this reference is always missing. – Zapnologica Jun 27 '17 at 04:53
  • Looking for this or something else? https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Client/ – user3285954 Jun 27 '17 at 09:08
76

If using 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");
}

Example:

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())
Matthew Steven Monkan
  • 8,170
  • 4
  • 53
  • 71
  • this is not asp.net core specific, its actually generic down to even 4.5.6 – danatcofo Jul 20 '17 at 15:08
  • 1
    `JsonConvert.SerializeObject` issues using ***DateTimes ISO 8601*** Kind: Local or UTC... http://www.hackered.co.uk/articles/useful-c-extensions-for-working-with-iso-8601-dates-and-strings – Kiquenet Feb 23 '18 at 13:42
  • @Kiquenet Thank you for this information! I'd been debugging my code for hours. I couldn't find the reason why my post works in Postman and not in my actual C# code. – Annie Lagang Jun 09 '23 at 08:11
34

I don't have enough reputation to add a comment on the answer from pomber so I'm posting another answer. Using pomber's approach I kept receiving a "400 Bad Request" response from an API I was POSTing my JSON request to (Visual Studio 2017, .NET 4.6.2). Eventually the problem was traced to the "Content-Type" header produced by StringContent() being incorrect (see https://github.com/dotnet/corefx/issues/7864).

tl;dr

Use pomber's answer with an extra line to correctly set the header on the request:

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
  • 504
  • 4
  • 12
  • Thank you, anthls. var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json") was not enough. It needs content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); Thank you for saving my sanity. – Gail Foad Aug 13 '18 at 14:11
  • 1
    This worked great. Any reason why "application/json" needs to be set twice, one in the constructor and once via the property? Is it a bug? – Festus Martingale Feb 07 '19 at 15:53
  • @FestusMartingale: good question! From my reading of the github issue (linked in the answer) the passing of `"application/json"` in the `StringContent` constructor is probably not required as it is being explicitly set on the resulting `content.Headers.ContentType` property. However, I haven't tested this in code. – anthls Feb 12 '19 at 01:40
  • It looks like the server does not support the full content type string. When you use the constructor without overriding the ContentType, it sets the value as `application/json; charset=utf-8`. – Bertm13 Mar 03 '20 at 13:41
7

I spent hours trying to solve this. But @anthls anwser saved my skin.

var data = new StringContent(JsonConvert.SerializeObject(new
   {
      abc = "jsjs",
      xyz = "hhhh"
    }));
data.Headers.ContentType = new MediaTypeHeaderValue("application/json"); // <--
var response = await client.PostAsync(url, data);
kachi_dk
  • 211
  • 2
  • 5
  • I for the life of me don't get why JsonContent.Create("serialised JSON here") didnt give the same result, even the headers were appropriate! This worked though, thank you! – divay pandey Aug 25 '22 at 17:43
2

the code over it in 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)

Hope this help

DaFois
  • 2,197
  • 8
  • 26
  • 43
2

In my case, I am using .NET 7.0; the StringContent did not work for me. I adapted some of the answers already provided to my use case. I used JsonContent to supply the body for the POST request.

var jsonContent = JsonContent.Create(new { ... });
var response = await _client.PostAsync("/", jsonContent);
codejockie
  • 9,020
  • 4
  • 40
  • 46
1

Thank you pomber but for

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

I used

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

because Result makes app lock for high request

Shojaeddin
  • 1,851
  • 1
  • 18
  • 16
  • That is because first example is blocking call, and second works as continuation. [Maybe this explanation helps](https://stackoverflow.com/a/13159176/578453) – miki Dec 26 '20 at 20:47
1

I Faced same issue i.e

var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json"); 

gave

"400 Bad Request"

Serializing JsonObject separately and passing the string in StringContent() solved issue for me, no need to set Encoding.UTF8 separately.

shA.t
  • 16,580
  • 5
  • 54
  • 111
0

I want to answer all in one response when doing this job as a note for all and myself:

According to Serez's answer HttpContent derived classes list as below https://stackoverflow.com/a/42380027/914284

HttpClient PostAsync has some background depending on the context you working on!

  • You can post data by the type that you want to send to server in cases Server context waits it as bellow
    [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)

When writing FromForm or Body it has working as FromForm. FromBody needs json content otherwise it requires KeyValuePairs as rows. There is some implementations for both of them such as below:

For FromForm: I have used an extension

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

For FromBody: Use any json converter library Newtonsoft or microsoft

using Newtonsoft.Json;

var jsonString = JsonConvert.SerializeObject(obj);

In both of them, content type should be defined according the requirement, for example for json (Write to header)

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

or another usage

        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
        }

If you should use authorization on the context also you can provide authorization as below:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");
Hamit YILDIRIM
  • 4,224
  • 1
  • 32
  • 35