2

I need to make a series of HTTP calls to obtain database credentials from a 3rd party vault, given that I need to run this code in Program.cs or, at the very latest, Startup.cs before adding the DBContext, I need to be able to make these calls without using IHttpClientFactory, as that requires Dependency Injection to already have been initialized.

The following code works fine when called during runtime, but doesn't work during the ConfigureAppConfiguration step.

HttpClient client = _clientFactory.CreateClient();

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, $"{_configuration["CredentialsVault:vaultUrl"]}Auth/SignAppIn");
request.Headers.Add("Authorization", $"PS-Auth key={_apiKey}; runas={_runAsUser};");

var response = await client.SendAsync(request);

Is there a way I can either make an HTTP call without having to rely on Dependency Injection, or delay AddDbContext until after Dependency Injection has been set up?

I have tried creating an instance of HttpClient like this:

HttpClient client = new HttpClient();

However this did not seem to work, and according to this question it should not be instantiated like that.

Danyx
  • 574
  • 7
  • 34

1 Answers1

5

You don't need to inject a typed HttpClient, or use IHttpClientFactory. It is recommended to solve certain historical issues, but it's not mandatory.

If you really need to resolve dependencies and configuration to call remote resources (during or before configuration construction), or you have a typed client that needs to be instantiated, you could just spin up a new ConfigurationBuilder (if needed) and ServiceProvider.

var preConfiguration = new ConfigurationBuilder()
    //... add your sources
    .Build();

var collection = new ServiceCollection()
     .AddHttpClient<...>(...)
     ...

var provider = collection.BuildServiceProvider();

var client = provider.GetService<SomeClient>();

client.YourCall();

//... normal configuration here

The above approach also gives you the ability and flexibility to insert Handlers to the request chain if needed.

However, this is likely not your problem. From your question all you need to do is make the same call with a standard HttpClient and call it in the same way you already call it. Once again, you can spinup a ConfigurationBuilder if you need to resolve configuration.

var preConfiguration = new ConfigurationBuilder()
    //... add your sources
    .Build();

HttpClient client = new HttpClient();

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, $"{preConfiguration["CredentialsVault:vaultUrl"]}Auth/SignAppIn");
request.Headers.Add("Authorization", $"PS-Auth key={_apiKey}; runas={_runAsUser};");

var response = await client.SendAsync(request);

//... normal configuration here

Fun Fact

.net 6 is adding a ConfigurationManager to address some of these configuration problems, as such it implements both IConfigurationBuilder and IConfigurationRoot and gives you the ability to partially build sources and providers, to resolve and in turn add to the builder again.

This solves the use-case of needing to resolve and build configuration in a consistent way. For instance, you could use the Manager to resolve a configuration, spin up a Service Provider and resolve a dependency to call a remote resource, and then add more configuration to the IConfigurationRoot.

halfer
  • 19,824
  • 17
  • 99
  • 186
TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • Do I need to manually add `appsettings.json` as a source when doing `_configuration = new ConfigurationBuilder().Build();`? When doing `_configuration["CredentialsVault:apiKey"]` it's returning `null`, I was able to get it to work by passing a configuration instance from Program.cs. – Danyx Oct 28 '21 at 21:40
  • 1
    Yes you would need to add any json configration as sources to the new peeconfiguraiton to use it – TheGeneral Oct 28 '21 at 21:42