Dynamically Updating the Request Header of a HttpClientFactory Generated HttpClient, Part 1

Full source code here.

There are some subtle issues in the way I use DI in this post, see here for an alternative if you don’t want to follow this approach

While using the HttpClientFactory I hit a scenario where I needed to update the value of a token passed in the header of requests, the token changed frequently, so I had to repeatedly update it throughout the lifetime of my application.

You have a couple of options for this, the first is to do it after you have taken a HttpClient from the factory at the point where you make your outbound request, this is straightforward, but now everywhere use a HttpClient you have to be able to get a new token. For some this might be fine, and you can use -

    httpClient.DefaultRequestHeaders.Add("Token", _tokenGenerator.GetToken());

Doing it with HttpClientFactory

The better approach is to put all this logic in the Startup.cs and update the header when the factory returns a new HttpClient, now everywhere you use the HttpClient gets the updated token without any work for you.

In my example case I have a token generator and memory cache. If there is a token in the cache, that one is used, if not the token generator generates and stores the new token in the cache for specified period.

In my Startup.cs all I need is this -

1services.AddHttpClient("RemoteServer", client =>
2{
3    client.BaseAddress = new Uri("http://localhost:5000/api/");
4    client.DefaultRequestHeaders.Add("Accept", "application/json");
5    client.DefaultRequestHeaders.Add("Token", TokenGenerator.GetToken());
6});

Read on to see how to wire everything up.

A little known feature of .NET Core is the ability to DI from Program.cs into Startup.cs, I have written about this before in Using Dependency Injection with Startup.cs in ASP.NET Core and am using it again here.

In Program.cs I add a memory cache and a token generator to the service collection.

Adding to the service collection this way can have some unexpected side effects, check this post for an alternative approach.

1public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
2    WebHost.CreateDefaultBuilder(args)
3    .ConfigureServices(cs => cs.AddMemoryCache())
4    .ConfigureServices(cs => cs.AddSingleton<ITokenGenerator, TokenGenerator>())
5        .UseStartup<Startup>();

In Startup.cs I pass a ITokenGenerator to the constructor.

1public Startup(IConfiguration configuration, ITokenGenerator tokenGenerator)
2{
3    Configuration = configuration;
4    TokenGenerator = tokenGenerator;
5    string token = tokenGenerator.GetToken(); // do something with the token
6}
7
8private ITokenGenerator TokenGenerator { get; }
9// snip

Then a simple call the TokenGenerator.GetToken() updates the header of the client the factory returns to callers.

For completeness, here is the implementation of the TokenGenerator.cs -

 1public class TokenGenerator : ITokenGenerator
 2{
 3    private readonly IMemoryCache _memoryCache;
 4    public TokenGenerator(IMemoryCache memoryCache)
 5    {
 6        _memoryCache = memoryCache;
 7    }
 8
 9    public string GetToken()
10    {
11        string token;
12        if (_memoryCache.TryGetValue("Token", out token))
13        {
14            return token;
15        }
16        else
17        {
18            // here you would have a more realistic way of generating a new token
19            token = Guid.NewGuid().ToString();
20            _memoryCache.Set("Token", token, TimeSpan.FromSeconds(10));
21
22            return token;
23        }
24    }
25}
 
Full source code here.

comments powered by Disqus

Related