9

What is the best practice for IHttpClientFactory and changing HttpClient.BaseAddress?

When creating my Dependency Injection I am doing it like this:

services.AddHttpClient("MyApp", c =>
    {
        c.BaseAddress = new Uri("https://myurl/");
        c.DefaultRequestHeaders.Add("Accept", "application/json");
    }).ConfigurePrimaryHttpMessageHandler(handler => new HttpClientHandler()
    { AutomaticDecompression = DecompressionMethods.GZip });

Then when I need an HttpClient doing this:

var client = clientFactory.CreateClient("MyApp");

This works great, but there are times, during runtime that the BaseAddress needs to change. During the run I am not able to change the BaseAddress after it has been injected. Now I could ignore BaseAddress altogether and just send the entire address in the API call, however, I do not know if this is the correct way of doing it. Something like this:

await using var stream = await client.GetStreamAsync($"{addresss}/{api}");
using var streamReader = new StreamReader(stream);
using var textReader = new JsonTextReader(streamReader);
var serializer = new JsonSerializer();
data = serializer.Deserialize<List<T>>(textReader);
Xaphann
  • 3,195
  • 10
  • 42
  • 70
  • 1
    Couple thoughts, i could have swore the base address was configurable after injection but if it's not you may want to use two clients and move the `CreateClient` closer to where you need the specific base address and use your second client there. just some ideas – JSteward Jun 01 '20 at 20:00
  • @JSteward I have had no luck changing the BaseAddress when I switch from straight HttpClient to IHttpClientFactory. Having two clients is unwanted, basically, the app allows for configuration change at run time. – Xaphann Jun 01 '20 at 20:45

1 Answers1

15

there are times, during runtime that the BaseAddress needs to change. During the run I am not able to change the BaseAddress after it has been injected.

BaseAddress can be changed up until the first request is sent. At that point, it is locked down and cannot be changed again. The HttpClient factory pattern assumes that each injected client has only one BaseAddress (which may be unset).

Now I could ignore BaseAddress altogether and just send the entire address in the API call, however, I do not know if this is the correct way of doing it.

Your options are:

  1. Define multiple clients, one for each BaseAddress. This is the normal approach if you have a few well-known hosts.
  2. Define a single client and do not use BaseAddress, passing the entire url in each call. This is perfectly permissible.
  3. Define your own factory type that uses IHttpClientFactory to pass out HttpClient instances where each instance can specify its own BaseAddress. I would only use this more complex approach if you had some code that required a BaseAddress (e.g., Refit) but needed to use it with a dynamic host.
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • So regarding the second point, will something like httpClientFactory.Create() work? Or even just with DI: constructor(HttpClient client)? – Ssss Jun 25 '21 at 14:20
  • 1
    @Ssss: Either of those would work to get an `HttpClient` instance. – Stephen Cleary Jun 25 '21 at 16:01
  • Option 2 seems the best for me, however I could find any docs on the issue. I keep getting an exception : InvalidOperationException: BaseAddress must be set on the HttpClient instance. – Xserge Sep 11 '21 at 07:22
  • @Xserge: Some third-party libraries (i.e., Refit) *require* a `BaseAddress`. – Stephen Cleary Sep 11 '21 at 13:20
  • in same case where 3 is required, option 4 is to use a DelegatingHandler as described here https://stackoverflow.com/a/75478523/169336 – drewburlingame Feb 16 '23 at 22:27