Efficient post calls with HttpClient and JSON.NET

3 minute read

Getting started

Before going further I highly recommend that you read the previous post explaining how to create efficient GET calls since it serves as this post starting point.

We now know how to create efficient HTTP Get calls with HttpClient and JSON.NET. This time we will go a bit further and implement POST calls.

Standard code

The following code is little more evolved version of what we can see in most projects :

private static async Task PostBasicAsync(object content, CancellationToken cancellationToken)
{
    using (var client = new HttpClient())
    using (var request = new HttpRequestMessage(HttpMethod.Post, Url))
    {
        var json = JsonConvert.SerializeObject(content);
        using (var stringContent = new StringContent(json, Encoding.UTF8, "application/json"))
        {
            request.Content = stringContent;

            using (var response = await client
                .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
                .ConfigureAwait(false))
            {
                response.EnsureSuccessStatusCode();
            }
        }
    }
}

Here is what we do step by step :

  • Instantiate the HttpClient
  • Instantiate the HttpRequestMessage
  • Serialize the content to send into a JSON string
  • Create a StringContent object and add it the request’s body without forgetting to set the encoding and content type.

This code works perfectly but, as I stated in the previous post, working with strings this way can have a negative effect on memory usage, and, at the end of the day, on performance.

Improving the code

Serialize JSON into a Stream

Instead of serializing our JSON into a string we will use a stream :

public static void SerializeJsonIntoStream(object value, Stream stream)
{
    using (var sw = new StreamWriter(stream, new UTF8Encoding(false), 1024, true))
    using (var jtw = new JsonTextWriter(sw) { Formatting = Formatting.None })
    {
        var js = new JsonSerializer();
        js.Serialize(jtw, value);
        jtw.Flush();
    }
}

In this code we create a StreamWriter using one of its overloads :

  • We first pass the stream to write to
  • We create a new UTF8Encoding instance passing false to it’s constructor to deal with UTF-8 BOM issues.
  • We pass the buffer default buffer size as shown in the StreamWriter source code
  • We finally use a boolean to inform the StreamWriter that we don’t want it to become the owner of the stream in parameter and that we will clean up resources ourselves.

Do not forget to flush the JsonTextWriter or you’ll end with an empty stream…

You can notice that our previous code is not asynchronous. We could make it asynchronous but it is not sure whether it will improve performance or not. It all depends on the serialization time because starting a new Task is costly.

Create the HttpContent

We will not be using a StringContent but a StreamContent instead.

Here is how we create it :

private static HttpContent CreateHttpContent(object content)
{
    HttpContent httpContent = null;

    if (content != null)
    {
        var ms = new MemoryStream();
        SerializeJsonIntoStream(content, ms);
        ms.Seek(0, SeekOrigin.Begin);
        httpContent = new StreamContent(ms);
        httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    }

    return httpContent;
}

As you can see, we serialize the content into the stream and return the HttpContent instance to the caller.

Calling the Api

Finally we just need to post the data to the API with a code relatively similar to the classical one.

private static async Task PostStreamAsync(object content, CancellationToken cancellationToken)
{
    using (var client = new HttpClient())
    using (var request = new HttpRequestMessage(HttpMethod.Post, Url))
    using (var httpContent = CreateHttpContent(content))
    {
        request.Content = httpContent;

        using (var response = await client
            .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
            .ConfigureAwait(false))
        {
            response.EnsureSuccessStatusCode();
        }
    }
}

You will maybe notice that the CreateHttpContent method can return null. The using syntax handles this particular case and nothing bad will happen in case of a null value.

Conclusion

You now know how to use streams and JSON.NET to improve your code. I have updated the Github project to include this post code. Please feel free to download it and give it a try.

And as always, I’ll be more than happy to answer any of you questions in the comments.

Comments