Make HTTP Requests keeps getting easier with AspNetCore. But the document doesn’t have a sample for PostAsync method, which is quite annoying. This article provides a sample for reference.

Client

public class WeatherForecastClient : IWeatherForecastClient
{
    private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };

    private readonly HttpClient _httpClient;

    public WeatherForecastClient(HttpClient httpClient)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
    }

    public async Task AddWeatherForecastAsync(WeatherForecast weatherForecast, CancellationToken cancellationToken = default)
    {
        var weatherForecastString = JsonSerializer.Serialize(weatherForecast, JsonSerializerOptions);
        var stringContent = new StringContent(weatherForecastString, Encoding.UTF8, MediaTypeNames.Application.Json);
        AddRequestHeader("Weather-Forecast-Header-Name", "WeatherForecastHeaderValue");
        await _httpClient.PostAsync("r/1fwlgic1", stringContent, cancellationToken);
    }

    private void AddRequestHeader(string name, string value)
    {
        _httpClient.DefaultRequestHeaders.Remove(name);
        _httpClient.DefaultRequestHeaders.Add(name, value);
    }
}

This sample uses Typed clients. I have created a RequestBin to accept the post message. The tricky part is to generate a JSON string for the request. And if you need to pass a different header value for each individual request, remember to remove the header before add the new value. Otherwise, the new value will be appended to the previous value because the same HttpClient instance is shared between requests. You can use Header Propagation if the header is from the incoming request.

Extension

public static class WeatherForecastClientExtensions
{
    private static readonly Random Jitterer = new Random();
    private const int RetryCount = 5;

    public static IHttpClientBuilder AddWeatherForecastClient(this IServiceCollection services, Uri baseAddress)
    {
        return services
            .AddHttpClient<IWeatherForecastClient, WeatherForecastClient>(httpClient =>
            {
                httpClient.BaseAddress = baseAddress;
                httpClient.DefaultRequestHeaders.Add("Weather-Forecast-Api-Key-Name", "WeatherForecastApiKeyValue");
            })
            .AddTransientHttpErrorPolicy(p =>
                p.WaitAndRetryAsync(
                    RetryCount,
                    currentRetryNumber => TimeSpan.FromSeconds(Math.Pow(2, currentRetryNumber)) + TimeSpan.FromMilliseconds(Jitterer.Next(0, 100)),
                    (currentException, currentSleepDuration, currentRetryNumber, currentContext) => 
                    {
                        if (currentRetryNumber == RetryCount)
                        {
                            throw currentException.Exception;
                        }
#if DEBUG
                        Debug.WriteLine($"=== Attempt {currentRetryNumber} ===");
                        Debug.WriteLine(nameof(currentException) + ": " + currentException.Exception);
                        Debug.WriteLine(nameof(currentSleepDuration) + ": " + currentSleepDuration);
#endif
                    }));
    }
}

An interface IWeatherForecastClient can be bound to the client WeatherForecastClient at registration. To use the Polly policy, Microsoft.Extensions.Http.Polly nuget package should be installed.