# PostAsJsonAsync - Now with headers

## The issue

I feel like I've run into this particular problem a few times now.

How can I use those lovely extensions in `HttpClientJsonExtensions` but send them with some additional custom headers? Or, are there any other options?

If you're unfamiliar with the extensions, they essentially allow you to do the following

```csharp
// This...
var message = new HttpRequestMessage
{
    Content = JsonContent.Create(new { Foo = "foo" }),
    Method = HttpMethod.Post,
    RequestUri = new Uri("https://example.com/some-endpoint"),
};

var response = await _httpClient.SendAsync(message);

// Or this...
var content = JsonContent.Create(new { Foo = "foo" });
var response = await _httpClient.PostAsync(
    "https://example.com/some-endpoint", 
    content);

// Turns into...
var response = _httpClient.PostAsJsonAsync(
    "https://example.com/some-endpoint", 
    new { Foo = "foo" });
```

Much simpler, right?

There is a whole raft of overloads on `PostAsJsonAsync` which you can look over [here](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.json.httpclientjsonextensions.postasjsonasync?view=net-8.0) but most of the time, you would probably just end up using something similar to the above.

But what do we do if we want some custom headers? That's not part of the extensions.

We have three options at this point:

1. Go back to hand-crafting the `HttpResponseMessage` / `JsonContent` and adding the headers there.
    
2. Create some extensions ourselves
    
3. Use delegating handlers
    

Let's just go through what that might look like.

## Option 1 - Hand-crafting

Arguably the simplest, we just go back to what we were doing in the first example.

```csharp
// Using the less verbose syntax...
var content = JsonContent.Create(new { Foo = "foo" });
content.Headers.Add("Bar-Header", "Bar");

var response = await _httpClient.PostAsync(
    "https://example.com/some-endpoint", 
    content);
```

Why would you go for this? I would start here if I only needed to do this [&lt;=3 times](https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)). The code maintenance overhead is not that much and if you needed to add a second header then it would not be too cumbersome to refactor. You could even add a helper method accepting `HttpContent` which added those headers on whilst leaving the rest of the code untouched.

This means we are not using the extensions but in this case, keeping it simple is fine.

## Option 2 - Extension methods

Our first stop is to see how the originals work under the hood, and they are quite simple for the most part.

```csharp
public static Task<HttpResponseMessage> PostAsJsonAsync<TValue>(
    this HttpClient client,
    [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri,
    TValue value,
    JsonSerializerOptions? options = null,
    CancellationToken cancellationToken = default)
{
    if (client is null)
    {
        throw new ArgumentNullException(nameof(client));
    }

    JsonContent content = JsonContent.Create(value, mediaType: null, options);
    return client.PostAsync(requestUri, content, cancellationToken);
}
```

> Taken from - [https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Post.cs](https://github.com/dotnet/runtime/blob/47f325ea3781c3bb16880fb98adadf272a6d4126/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Post.cs)

Simple enough, right?

If we wanted to add headers, we could just make our own extension class

```csharp
public static class HttpClientExtensions
{
    public static Task<HttpResponseMessage> PostAsJsonAsync<TValue>(
        this HttpClient client,
        [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri,
        TValue value,
        IReadOnlyDictionary<string, string> headers, // Add to the parameters
        JsonSerializerOptions? options = null,
        CancellationToken cancellationToken = default)
    {
        if (client is null)
        {
            throw new ArgumentNullException(nameof(client));
        }

        JsonContent content = JsonContent.Create(value, mediaType: null, options);
        foreach (var header in headers)
        {
            content.Headers.Remove(header.Key); // Just in case it was added elsewhere
            content.Headers.Add(header.Key, header.Value);
        }

        return client.PostAsync(requestUri, content, cancellationToken);
    }
}

// And then we use it much like in the first example
var response = _httpClient.PostAsJsonAsync(
    "https://example.com/some-endpoint",
    new { Foo = "foo" },
    new Dictionary<string, string>
    {
        ["Bar-Header"] = "Bar"
    });
```

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">As mentioned before, there are a lot of overloads you could potentially add header support to. If going down this route, just add them as you need them.</div>
</div>

We now have a reusable method for anywhere in our codebase that needs this sort of thing. This is an excellent choice for adding simple ad-hoc headers to requests.

But what if we had something that we needed to add to all requests for a particular client but that value could change over time?

## Option 3 - Delegating Handlers

If you have not come across these before, `DelegatingHandlers` in C# are like filters or middleware for your HTTP requests and responses when attached to an `HttpClient`.

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Microsoft's documentation on the subject is fairly thorough and I'd recommend reading it - <a target="_blank" rel="noopener noreferrer nofollow" href="https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers" style="pointer-events: none">link here</a>. Worth noting that it talks about some now fairly old ASP.NET WebAPI bits which can be ignored.</div>
</div>

These are great for when you want to do *something* with every request and response. In our case, let's imagine that we need our `Bar-Header` to change on every request. We could still do that with our other two options but it would be great if we could forget that we even needed to add that header in every request.

Here is what a simple `DelegatingHandler` would look like for adding a header.

```csharp
public class BarHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        // Assume GenerateBarHeader() returns some dynamic string
        request.Headers.Add("Bar-Header", GenerateBarHeader());
        return base.SendAsync(request, cancellationToken);
    }
}
```

Very simple.

And then to hook this up, assuming you are using DI to inject your `HttpClient`.

```csharp
builder.Services.AddTransient<BarHandler>();
builder.Services.AddHttpClient().AddHttpMessageHandler<BarHandler>()
```

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">A quick note here - this would add our header to <strong>all</strong> <code>HttpClient</code> instances we inject. You probably do not want this. Typed or named clients are far more flexible which is covered in the documentation <a target="_blank" rel="noopener noreferrer nofollow" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-8.0#named-clients" style="pointer-events: none">here</a>.</div>
</div>

Now whenever you send a request out, our header will be added to the collection of headers, even when using `PostAsJsonAsync`.

The most common use for this is adding an `Authorization` header for all requests, but it can also be used for doing things like custom logging.

## Honourable mention - DefaultRequestHeaders

Leaving this until last as I have seen this horribly abused. If we go back to our original scenario of adding `Bar-Header: Bar` to all requests, you could do the following

```csharp
httpClient.DefaultRequestHeaders.Add("Bar-Header", "Bar");
```

Very simple, you can do this when registering with the DI container as well. You can do it whenever you have access to the `HttpClient` and that is where the problem comes in.

I imagine some or most have you have come across something like this

```csharp
var content = JsonContent.Create(new { Foo = "foo" });
_httpClient.DefaultRequestHeaders.Add("Bar-Header", "Bar");
var response = await _httpClient.PostAsync(
    "https://example.com/some-endpoint", 
    content);

// And then sometimes you see this...
_httpClient.DefaultRequestHeaders.Remove("Bar-Header");
// Or worse...
_httpClient.DefaultRequestHeaders.Clear();
```

Please, **do not do this**.

Will it work? Yes, assuming that `HttpClient` is not being used in other threads and `DefaultRequestHeaders` is empty before you add anything. These are fairly big assumptions and hinder refactoring. If you want to use `DefaultRequestHeaders` only do it when you first create the client and then leave it alone after that.

## Which should I use?

In my opinion

* Handcraft the requests if you only have a few places that need an additional header
    
* Create the extension(s) if you have more than three places that have an additional header, or if you have different classes that need this shared functionality
    
* Use `DelegatingHandler` if you have a dynamic header you always need setting
    
* Use `DefaultRequestHeaders` when you have a static header you always need a setting **but only set this once!**
