Closed poke closed 3 years ago
The answer to this really depends what you expect to get out of using the client factory. I get asked this question a lot, and the answer depends on a lot more details about what you want to accomplish. It sounds like you might have a few different scenarios so I'm providing a lot of information with the hopes that it's helpful.
If you are building a library that you plan to distribute, I would strongly suggest that you don't take a dependency on IHttpClientFactory
at all, and have your consumers pass in an HttpClient
instance.
If you are building a non-ASP.NET-Core application, consider using Microsoft.Extensions.Hosting
to create a DI container.
If you are building a non-ASP.NET-Core application and you really don't want to use Microsoft.Extensions.Hosting
then it depends what you want to use the client factory for.
A. I want an opinionated builder abstraction for configuring HttpClientFactory.
B. I don't need that. I just want an HttpClient
with good network behavior by default.
If you are in category A then I have to explain that DI and IOptions<>
is the opinionated builder abstraction. Everything in category A is built on top of DI and IOptions<>
. The features exposed by the builder assume that you have a DI system, and we have no plans to try and separate these.
Based on the information you provided, I'm guessing you fall into category B.
If you are on .NET Core - you should use a single HttpClient
directly and set SocketsHttpHandler.PooledConnectionTimeout
here to an appropriate value.
If you are on .NET Framework - you should use a single HttpClient
and use ServicePoint
to configure the similar settings.
The good news for anyone interested in connection management is that .NET now has reasonable behavior on Linux (as of 2.1 and SocketsHttpHandler
) but it requires configuration.
I've logged a doc issue to clear this up further. aspnet/Docs#11882
Thank you for the detailed answer (and that doc request), that is going to help a lot!
In my case, I’m somewhere between A and B actually. The client library I am working on is mostly targeted to ASP.NET Core applications, so I have a helper extension method on the service collection that just adds all the clients as typed clients using the HttpClientFactory (using a custom options object to configure a few parameters). That is working just fine the way it is.
However, I now have the requirement to use this in a legacy ASP.NET application that uses Autofac as DI container. So I cannot really use any M.E.DI-specific stuff, and I certainly don’t want to bring all the stuff into that project just to use the HttpClientFactory.
Ideally, what I would like to do is to set up a HttpClientFactory, configure a few clients and then have something I can call to create a factory method that I then register with the existing DI container. What you say makes sense though; the factory makes heavy use of DI and options, so using it without it would be really difficult.
In my case, the various clients each have different DelegatingHandler
s that are configured ahead, so using a single HttpClient
will not really work for me. But I guess I will just have to set up separate HttpClient instances then for each client.
If you are on .NET Framework - you should use a single HttpClient and use ServicePoint to configure the similar settings.
It would be really nice if there was some guidance along with the docs on what would be good defaults for setting up a HttpClient.
However, I now have the requirement to use this in a legacy ASP.NET application that uses Autofac as DI container. So I cannot really use any M.E.DI-specific stuff, and I certainly don’t want to bring all the stuff into that project just to use the HttpClientFactory.
Sure you can, in this mode, think of M.E.DI stuff as an implementation detail for how to get an IHttpClientFactory instance. The other features though, rely on this internal detail in order to do more complex things (like typed clients etc).
Alternatively, if you really wanted to try to integrate the 2 things (autofac and the HttpClientFactory), you can use the ServiceCollection as the configuration API for the HttpClient and use Autofac.Extensions.DependencyInjection to wire it up to your existing autofac container:
public class MyTypedClient
{
public MyTypedClient(HttpClient client)
{
}
}
public class Program
{
public static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddHttpClient()
.AddHttpClient<MyTypedClient>();
var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
var factory = container.Resolve<IHttpClientFactory>();
var typedClient = container.Resolve<MyTypedClient>();
}
}
It would be really nice if there was some guidance along with the docs on what would be good defaults for setting up a HttpClient.
+1
Hey everyone
I've made a fork to address this issue. https://github.com/uhaciogullari/HttpClientFactoryLite Let me know what you think.
A. I want an opinionated builder abstraction for configuring HttpClientFactory. B. I don't need that. I just want an HttpClient with good network behavior by default.
I'd like to stress out that using the DI container for absolutely everything is not the best example of design. Insisting that all developers need to use the container for things like HTTP client factory, in-memory cache, distributed cache, logging and so on, creates impediments for developers that would otherwise avoid using DI containers altogether. I personally prefer to construct my dependency tree in the Startup
class explicitly and don't rely on something else that I don't control to resolve dependencies for me.
There's was an issue https://github.com/aspnet/Extensions/issues/615 for using ILogger
without the container and it is fixed for .NET Core 3. I would expect that all other factories would follow the same approach and expose a way to instantiate factories without using the container, at least for the reason of consistency.
I also feel like this seems like a strange way to strongarm people out of using other DI containers.
I maintain Xamarin Forms projects that predate the M.E.DI stuff. One uses TinyIoC because it chose the FreshMVVM framework. The other uses AutoFac. It would be a PITA for me to rework them to use the Microsoft DI container. I shouldn't have to in order to use HttpClient.
In the AutoFac project, I'd like to reuse a REST utility someone else made for interacting with an internal web service. That utility was written in an ASP .NET Core environment so it already expects me to give it an HttpClient. So he's using the Web Host container and gets all the stuff I want for free.
I don't see good guidance for how to configure AutoFac to do the same thing your DI does. I do see a kind of janky path to set up the M.E.DI container, then somehow convert that to AutoFac. That feels ridiculous. I get that HttpClientFactory is opinionated, but I don't think part of that opinion should be "your app must use Microsoft DI".
Especially in the Xamarin context, I'm bewildered. I've scanned a few articles and issues related to this and I can't tell if I should or shouldn't be using IHttpClientFactory at all. Some seem to suggest I should just set SocketsHttpHandler
properties, but that's not available in Xamarin. Some articles vaguely gesture at whether I should configure Xamarin project properties to use platform-specific handlers.
I don't see a clear message for how devs outside of ASP .NET Core are supposed to use HttpClient. I thought when I found this library it would be the clear message. But it looks like even within this library, I see confusing comments that imply outside of ASP .NET Core I might not even want to use HttpClientFactory. Even if that was the wrong read, I don't see an explanation for what the magic inside M.E.DI is so I can realize it with a different DI container (or no container at all.)
If Generic Host container isn't suitable for all platforms, where is the documentation that outlines its limitations and explains, for every Microsoft-supported platform, what the best practice for using HttpClient is? I shouldn't have to read half a dozen blogs to understand such a crucial type, it should be explained by MS.
Incidentally AutoFac has MS DI integration - I think it boils down to a one-liner (builder.Populate(services);
).
That said, we went ahead with AutoFac for many internal projects but are struggling to see any advantage to it considering all MS packages use IServiceCollection
etc. There's a big old GH issue I have somewhere to remove it just to avoid that one extra step..
I have a net core unit test project without DI and having a static HttpClientFactory
would have been handy to build a HttpClient
with many handlers in a one liner using HttpClientFactory.Create(params DelegatingHandler[] handlers)
A. I want an opinionated builder abstraction for configuring HttpClientFactory. B. I don't need that. I just want an HttpClient with good network behavior by default.
I'd like to stress out that using the DI container for absolutely everything is not the best example of design. Insisting that all developers need to use the container for things like HTTP client factory, in-memory cache, distributed cache, logging and so on, creates impediments for developers that would otherwise avoid using DI containers altogether. I personally prefer to construct my dependency tree in the
Startup
class explicitly and don't rely on something else that I don't control to resolve dependencies for me.There's was an issue dotnet/extensions#615 for using
ILogger
without the container and it is fixed for .NET Core 3. I would expect that all other factories would follow the same approach and expose a way to instantiate factories without using the container, at least for the reason of consistency.
You are absolutely right that dependency injection is not everything I need. Sometimes dependency injection is more cumbersome and increases my workload. It seems useful but it is meaningless, 666
Is it possible to use the app.ApplicationServices.GetService
Is it possible to use the app.ApplicationServices.GetService() in the Configure method to get the IHttpClientFactory, then assign it to public static, and then globally.
I'm sure you can do this, and I'm sure that the code will work.
However you're baking in a dependency on a global static. Do you really want to do that? Will you ever want to test any of that code?
Is it possible to use the app.ApplicationServices.GetService() in the Configure method to get the IHttpClientFactory, then assign it to public static, and then globally.
I'm sure you can do this, and I'm sure that the code will work.
However you're baking in a dependency on a global static. Do you really want to do that? Will you ever want to test any of that code?
Thank you for your help. Our code is converted from asp.net. Not only httpclient needs to be called in the controller, but also in many classes. It's not easy to rely on the injection method, so I made a global static, maybe I didn't find a better way.
Which NuGet, assembly and namespace is this mythical HttpClientFactory class located in? I can't find it anywhere...
@davidfowl Why not to extract IHttpClientFactory
interface as a separate NuGet package which won't have all ASP.NET Core dependencies?
Maybe even containing some lame default implementation of the factory which just reuses same SocketsHttpHandler
per client name?
@rynowak I don’t need ‘an alternative’, I just want reusable abstraction, so my library works nice and has stable interface both in the context of ASP.NET Core and in a stand-alone application.
Hi @rynowak,
Let me question you why SocketsHttpHandler
doesn't target netstandard2.1
?
SocketsHttpHandler is part of .NET Core, I'm not sure if there's a technical reason why it's not in netstandard2.1. I'd suggest asking at dotnet/standard or dotnet/runtime.
@rynowak we're seeing .NET Core racing ahead without waiting for other platforms to catch up. This is dangerous and devalues .NET Standard. Xamarin and UWP both need the same APIs and further bifurcation is going to lead to more fragmentation of the .NET ecosystem.
@rynowak Is there a recommended value for SocketsHttpHandler.PooledConnectionTimeout
?
It depend on your scenario. I'd suggest starting with a value like 15 minutes and if that gives you good results, leave it.
The tight coupling between the new IHttpClientFactory
and Microsoft.Extensions.DependencyInjection
is not a good design because it is trying to fix a defect by another defect. Now it is easy to use for ASP .NET Core apps but we are confused regarding how to use it in other apps, eg, Xamarin, WPF, etc. There are lots of DI tools in the world. I think it would be better to allow developers to freely use IHttpClientFactory
without any specific DI tools.
Currently I would try to get the instance of IHttpClientFactory
and register it again in another DI tool - which is ugly.
@yanxiaodi . Exactly.
All that needs to be done is to give DefaultHttpClientFactory
a public constructor as far as I can tell. To not give it a public constructor seems like bias against other IoC containers.
@rynowak does configuring SocketsHttpHandler.PooledConnectionTimeout solve DNS issue as mentioned here https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net-core ?
I want exactly all benefits of HttpClientFactory but without DI in a normal console app (.NET Core 3.1).
I am curious about how does Azure key vault work when we add it as a configuration provider during the ConfigureAppConfiguration
stage.
Azure SDK has a concept, HttpPipeline
, which represents a primitive for sending HTTP requests and receiving responses.
https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/src/Pipeline/HttpPipeline.cs
Is the HttpPipeline
a sibling of the HttpClient
? Does .NET have something similar to the HttpPipeline
that is primitive and can be used during the generic host build stage?
It is a sibling. The AzureSDK team built their own primitive for outgoing http requests. The equivalent in the BCL is a MessageHandler
For what it's worth, I wrote my own version of this that implements @rynowak 's recommended behavior above for .NET Core and .NET Standard. It doesn't use the exact interface because it's only available if you pull in a ton of ASP.NET dependencies but is pretty easy to use and I'm happy to make changes if needed.
For what it's worth, I wrote my own version of this that implements @rynowak 's recommended behavior above for .NET Core and .NET Standard. It doesn't use the exact interface because it's only available if you pull in a ton of ASP.NET dependencies but is pretty easy to use and I'm happy to make changes if needed.
@rynowak , @davidfowl : For targeting .Net framework, is this a good solution to use under heavy load? What happens when the ServicePoint.ConnectionLeaseTimeout expires?
heavy load
Heavy load is a very contextual term. What does it mean for your use case?
From the docs
When the ConnectionLeaseTimeout property is set to a value other than -1, and after the specified time elapses, an active ServicePoint connection is closed after servicing a request by setting KeepAlive to false in that request.
@davidsh may be able to say more.
When the ConnectionLeaseTimeout property is set to a value other than -1, and after the specified time elapses, an active ServicePoint connection is closed after servicing a request by setting KeepAlive to false in that request.
Keep in mind the ServicePointManager and ServicePoint objects are no-op in .NET Core. So, using these properties only affect .NET Framework applications.
But the docs describe the behavior of the ConnectionLeaseTimeout
correctly.
Today, I was refactoring a legacy piece of code which could be improved using IHttpClientFactory
. However, as the class has a default constructor, a default implementation of the IHttpClientFactory
will be useful.
It would be really nice if there was some guidance along with the docs on what would be good defaults for setting up a HttpClient.
+1
For ASP.NET Core 3+ the Autofac configuration is automatic and simpler. See https://autofaccn.readthedocs.io/en/stable/integration/aspnetcore.html#asp-net-core-3-0-and-generic-hosting
Have you considered projects without DI?
void Get(string url, Dictionary<string, string> Headers = null, int timeout = 30)
{
var client = new HttpClient();
if (Headers != null && Headers.Count > 0)
{
foreach (var item in Headers)
{
client.DefaultRequestHeaders.TryAddWithoutValidation(item.Key, item.Value);
}
}
client.Timeout = TimeSpan.FromSeconds(timeout);
client.GetAsync(url);
}
This is a simple example, we know there are static and DI can solve the problem, but I really want to use static to achieve this, because I want to use static, will it automatically manage the release of the problem??As far as I know, the static should not be freed, I guess, if the object can't be freed it can, I like to know TCP will it automatically close?Let's say I do it statically
The factory won’t help you with your static method. Just use a singleton http client or write and extension method. I’m not sure what most of those points have to do with the client factory. It’s unclear why you need to use it when your scenario is as simplistic as the sample code you wrote
@davidfowl Hello, here's the thing. When we use static, we can't share the parameters, for example, Timeout can't be set, because this project might request multiple webapi under the domain name, we are considering less code implementation, which obviously doesn't seem to work, like I have a.com b.com c.com XXXXX Webapi, I seem to have to have them statically stored separately, otherwise they will report errors when setting Shared parameters such as Timeout. I've been looking for a better solution, but of course we're doing it statically
What’s the exact code do you want to write? What would this type solve for you and how would it do that?
@davidfowl
static void Main(string[] args)
{
var url1 = "http//a.com/use/a";
var url2 = "http//b.com/use/b";
var url3 = "http//c.com/use/c";
Get(url1, null, 10);
Get(url2, null, 15);
Get(url3, null, 30);
}
static HttpClient client = new HttpClient();
static void Get(string url, Dictionary<string, string> Headers = null, int timeout = 30)
{
if (Headers != null && Headers.Count > 0)
{
foreach (var item in Headers)
{
client.DefaultRequestHeaders.TryAddWithoutValidation(item.Key, item.Value);
}
}
client.Timeout = TimeSpan.FromSeconds(timeout);
client.GetAsync(url);
}
}
For example, a public method can run multiple web apis, including Settings
I’m not seeing how you wound use the factory. Could you also write a sample of that
@davidfowl
namespace ConsoleApp1
{
class Program
{
static HttpClient httpClient = new HttpClient();
static void Main(string[] args)
{
var msUrl = "https://docs.microsoft.com/en-us/dotnet/core/";
var edgeUrl = "https://www.microsoft.com/zh-cn/edge";
var http = new UseHttpClient();
var rs = http.Get(msUrl);
//I can't change the timeout when I have other requests
var edgeRs = http.Get(edgeUrl);
Console.WriteLine(rs);
}
}
/// <summary>
/// HttpClient
/// </summary>
public class UseHttpClient
{
static HttpClient httpClient = new HttpClient();
/// <summary>
/// get
/// </summary>
/// <param name="url"></param>
/// <param name="timeout"></param>
/// <param name="Headers"></param>
/// <returns></returns>
public string Get(string url, int timeout = 30, Dictionary<string, string> Headers = null)
{
if (Headers != null && Headers.Count > 0)
{
foreach (var item in Headers)
{
httpClient.DefaultRequestHeaders.TryAddWithoutValidation(item.Key, item.Value);
}
}
httpClient.Timeout = TimeSpan.FromSeconds(timeout);
return httpClient.GetAsync(url).Result.Content.ReadAsStringAsync().Result;
}
}
}
You can try running this, I think the public method of this class can access any webapi.And you can set related parameters, such as timeouts
Use a cancellation token to have a timeout on the request itself. Is that all you’re trying to solve?
@davidfowl Yes, I want to be able to set a timeout and also close the connection automatically
Close the connection? Are you trying to use http 1.0? You can use a cancellation token for timeouts instead of setting the property on the client
@davidfowl It doesn't seem like it can be done. Some of the interface timeouts are longer, like 15 seconds, and some are shorter
I’m not sure what you mean. What can’t be done?
@davidfowl The connection can only be broken on the client side
That’s not true. But I’m really confused as to what you’re trying to do. I think I gave you answers:
@davidfowl I want to be able to dynamically set the timeout, to share one method and one HttpClient, because using does not release properly, so I want to use the singleton HttpClient on a static basis to dynamically set the timeout, but this one doesn't seem to be set
You can if you use a cancellation token for the call instead of setting the timeout property
Is there any way to consume the
HttpClientFactory
without using dependency injection, or more precisely without using M.E.DI?I am creating a client library for which I want to make use of HttpClientFactory but I am going to use this library also in a legacy project that does not not have a M.E.DI-compatible DI.
Ideally, I would just new up a
DefaultHttpClientFactory
and consume that directly but since the factory itself is internal, this is not possible.Why is the
HttpClientFactory
internal and is there any way to consume it explicitly without using DI?