How To Correctly Register FhirClient for Dependency Injection

To avoid socket exhaustion, you need to pool HttpClient instances and inject them into FhirClient. The default constructor will instantiate its own HttpClient, which can leak sockets and cause latency spikes in your app.

You can use IHttpClientFactory to help with this. See the documentation for all the nitty-gritty details.

Note that while this example considers the open source FhirClient library, you can use this pattern with any HttpClient wrapper that lets you inject the HttpClient instance.

In your Startup class:

Startup.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
var url = Configuration["FHIR_ROOT_URL"];

var settings = new FhirClientSettings
{
PreferredFormat = ResourceFormat.Json,
VerifyFhirVersion = false, // avoids calling /metadata on every request
PreferredParameterHandling = SearchParameterHandling.Lenient
};


// register a named HttpClient configured for FHIR
services.AddHttpClient("FhirHttpClient")
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() {
// FhirClient configures its internal HttpClient this way
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
});

services.AddTransient(ctx => {

// Get a new HttpClient that is using pooled socket handlers behind the scenes
var httpClient = ctx.GetRequiredService<IHttpClientFactory>()
.CreateClient("FhirHttpClient");

// inject it into your client
return new FhirClient(url, httpClient, settings);
});
}
}

Registering Custom Handlers

What if you are using a custom HttpMessageHandler to do some authorization such as adding a client credentialed bearer token?

You can still set that up with DI. Just make your custom handler inherit from DelegatingHandler so that the AddHttpClient machinery can reuse the inner-most HttpMessageHandler.

ClientCredentialHandler.cs
1
2
3
4
5
6
7
8
9
10
11
public class CustomAuthHandler : DelegatingHandler {
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var token = await GetTokenFromSomewhere()

request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

return await base.SendAsync(request, cancellationToken);
}
}

Now you can configure the HttpClient instances to be automatically wired up with this handler.

Startup.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// register your auth handler
services.AddTransient<WebAuthMessageHandler>();

// register a named HttpClient configured to use it
services.AddHttpClient("FhirHttpClient")
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() {
// FhirClient configures its internal HttpClient this way
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
})
.AddHttpMessageHandler<CustomAuthHandler>();
}
}