HttpClient factory is an opinionated factory for creating HttpClient instances to be used in your applications. It is designed to:
- Provide a central location for naming and configuring logical HttpClients. For example, you may configure a client that is pre-configured to access the github API.
- Codify the concept of outgoing middleware via delegating handlers in HttpClient and implementing Polly based middleware to take advantage of that.
- HttpClient already has the concept of delegating handlers that could be linked together for outgoing HTTP requests. The factory will make registering of these per named client more intuitive as well as implement a Polly handler that allows Polly policies to be used for Retry, CircuitBreakers, etc.
- Manage the lifetime of HttpClientMessageHandlers to avoid common problems that can occur when managing HttpClient lifetimes yourself.
There are several ways that you can use HttpClient factory in your application. For the sake of brevity we will only show you one of the ways to use it here, but all options are being documented and are currently listed in the HttpClientFactory repo wiki.
In the rest of this section we will use HttpClient factory to create a HttpClient to call the default API template from Visual Studio, the ValuesController API.
1. Create a typed client
Typed clients are a class that accepts a HttpClient and optionally uses it to call some HTTP service. For example:
NOTE: The Content.ReadAsAsync method comes from the Microsoft.AspNet.WebApi.Client package. You will need to add that to your application if you want to use it.
The typed client is activated by DI, meaning that it can accept any registered service in its constructor.
2. Register the typed client
Once you have a type that accepts a HttpClient you can register it with the following:
The function here will execute to configure your HttpClient instance before it is passed to the ValuesClient. A typed client is, effectively, a transient service, meaning that a new instance is created each time one is needed and it will receive a new HttpClient instance each time it is constructed. This means that your configuration func, in this case retrieving the URI from configuration, will run every time something needs a ValuesClient.
3. Use the client
Now that you have registerd your client you can use it anywhere that can have services injected by DI, for example I could have a Razor Pages page model like this:
or perhaps like this:
By default, when you use a HttpClient created by HttpClient factory, you will see logs like the following appear:
The log messages about starting and processing a HTTP request are being logged because we are using a HttpClient created by the HttpClient factory. From these 7 log messages you can see:
- An incoming request in to localhost:5001 in this case this is the browser navigating to my Razor Pages page.
- MVC selecting a handler for the request, the OnGetAsync method of my PageModel.
- The beginning of an outgoing HTTP request, this marks the start of the outgoing pipeline that we will discuss in the next section.
- We send a HTTP request with the given verb.
- Recieve the request back in 439.6606 ms, with a status of OK.
- End the outgoing HTTP pipeline.
- End and return from our handler.
If you set the LogLevel to at least Debug then we will also log header information. In the following screenshot I added an accept header to my request, and you can see the response headers:
The outgoing middleware pipeline
For sometime now ASP.NET has had the concept of middleware that operates on an incoming request. With HttpClientFactory we are going to bring a similar concept to outgoing HTTP requests using the existing DelegatingHttpHandler type that has been in .NET for some time. As an example of how this works we will look at how we generate the log messages that we looked at in the previous section:
NOTE: This code is simplified for the sake of brevity and ease of understanding, the actual class can be found here
Let’s look at another example that isn’t already built in. When using client based service discovery systems then you will ask another service for the host/port combination that you should use to communicate to a given service type. For example, you could be using the HTTP API of Consul.io to resolve the name ‘values’ to an IP and port combination. In the following handler we will replace the incoming host name with the result of a request to an IServiceRegistry type that would be implemented to communicate with whatever service discovery system you used. In this way we could make a request to ‘http://values/api/values’ and it would actually be connect to ‘http://:/api/values’.
NOTE: This sample is inspired by the CondensorDotNet project. Which has a HttpClientHandler that works the same way.
We can then register this with the following:
The type being give to the AddHttpMessageHandler must be registered as a transient service. However, because we have the IServiceRegistry as its own service it can have a different lifetime to the handler, allowing caching and other features to be implemented in the service registry instead of the handler itself.
Now that we’ve registered the handler all requests will have their Host and Port set to whatever is returned from the IServiceRegistry type. If we continued our example we would implement IServiceRegistry to call the Consul.io HTTP Endpoint to resolve the URI from the requested HostName.
In general you should get a HttpClient from the factory per unit of work. In the case of MVC this means you would generally accept a typed client in the constructor of your controller and let it be garbage collected when the controller does. If you are using IHttpClientFactory directly, which we don’t talk about in this post but can be done, then the equivalent would be to create a HttpClient in the constructor and let it be collected the same way.
Disposing of the client is not mandatory, but doing so will cancel any ongoing requests and ensure the given instance of HttpClient cannot be used after Dispose is called. The factory takes care of tracking and disposing of the important resources that instances of HttpClient use, which means that HttpClient instances can be generally be treated as .NET objects that don’t require disposing.
One effect of this is that some common patterns that people use today to handle HttpClient instances, such as keeping a single HttpClient instance alive for a long time, are no longer required. Documentation about what exactly the factory does and what patterns it resolves will be available, but hasn’t been completed yet.
In the future we hope that a new HttpClientHandler will mean that HttpClient instances created without the factory will also be able to be treated this way. We are working on this in the corefx GitHub repositories now.
Before 2.1 is released
- Polly integration.
- Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner. We will be building a package that allows easy integration of Polly policies with HttpClients created by the HttpClient factory.
- Auth Handlers.
- The ability to have auth headers automatically added to outgoing HTTP requests.
The HTTPClient factory is available in 2.1 Preview 1 apps. You can ask questions and file feedback in the HttpClientFactory github repository.