When using services.AddHttpClient, where is the HttpClient created?
Asked Answered
G

2

22

I am trying to understand how HttpClient has been implemented for Captcha in Nop Commerce and for the sake of testability how creating new instance of HttpClient has been manage in Nop Commerce project.

I came across ValidateCaptchaAttribute and ValidateCaptchaFilter and I see HttpClient has been wrapped inside CaptchaHttpClient class but I don't understand from where does CaptchaHttpClient receive dependency for HttpClient and from where constructor of CaptchaHttpClient class is being called.

Inside ServiceCollectionExtensions class I see below code:

public static void AddNopHttpClients(this IServiceCollection services)
 {
    //default client
    services.AddHttpClient(NopHttpDefaults.DefaultHttpClient).WithProxy();

    //client to request current store
    services.AddHttpClient<StoreHttpClient>();

    //client to request nopCommerce official site
    services.AddHttpClient<NopHttpClient>().WithProxy();

    //client to request reCAPTCHA service
    services.AddHttpClient<CaptchaHttpClient>().WithProxy();
 }

But I don't see where HttpClient object is created:

var client = new HttpClient() // Where this is done?

Am I perhaps missing something?

Nop Commerce Version = 4.20

Greenman answered 31/5, 2019 at 7:51 Comment(0)
D
47

From the documentation:

Adds the IHttpClientFactory and related services to the IServiceCollection and configures a binding between the TClient type and a named HttpClient. The client name will be set to the type name of TClient.

Roughly translated, services.AddHttpClient<CaptchaHttpClient>() means that CaptchaHttpClient has a dependency on HttpClient. This says that when injecting HttpClient into CaptchaHttpClient, don't just create a new one - use an implementation of IHttpClientFactory to provide one and inject the HttpClient it creates.

This means that you're not managing the lifetime of the HttpClient. The ServiceProvider is doing that behind the scenes. (You're not responsible for creating the client factory either.)

This documentation explains why this exists and how it works.

A Typed Client is, effectively, a transient object, meaning that a new instance is created each time one is needed and it will receive a new HttpClient instance each time it's constructed. However, the HttpMessageHandler objects in the pool are the objects that are reused by multiple Http requests.

This means:

  • The thing you're registering - in this case CaptchaHttpClient is transient so that each time it's resolved, a new instance is created.
  • Each time it is created, a new HttpClient is created and injected.
  • Although the HttpClient is new, the HttpMessageHandler it depends upon is reused.

This uses a pool of HttpMessageHandler instances that we don't have to manage. Our class just depends on HttpClient without having to worry about the negative side effects that happen when we create/dispose an HttpClient each time we need one.

Derisible answered 31/5, 2019 at 13:21 Comment(9)
Thank you so much for the answer.So does that mean AddHttpClient is doing new HttpClient() behind the scenes?Greenman
Here's some more documentation. I gathered that this exists because developers were using HttpClient wrong, but I don't know what it does to make it right. So now I'm reading this too.Derisible
This last documentation link is really helpful and cleared some of my doubts.Thanks alot.But still I dont know what does WithProxy() means?Does it creates HttpClient as Singleton object or what it does exactly?Greenman
That's here, back in the Nop source code. Apparently it gets some proxy settings from configuration and configures the HttpMessageHandler to use them.Derisible
Suppose If I dont want to use this method WithProxy(),then what other suitable method I should use on AddHttpClient?Greenman
Wherever it gets those proxy settings from, if they aren't specified then default credentials are used. So, while you can decide whether you want to use WithProxy() in your own code is an option, this is in Nop's code. So unless they provide some way to override all of this configuration you're just using the dependencies however they registered them. But since it doesn't appear to do anything unless the configuration settings provide proxy credentials, it seems like you could just ignore it.Derisible
in background service it's live evertime :(( And ServerCertificatCustomValidationCallback used only on first start... and some times.... it's crazyWiedmann
This seems opposite of the general guidance that the same instance of a HttpClient should be used for the lifetime of the process, and it can even cause memory leaks if you treat it as a short lived object: https://mcmap.net/q/76441/-do-httpclient-and-httpclienthandler-have-to-be-disposed-between-requestsZest
That's true, but it's because of the HttpMessageHandler. It's used by the HttpClient, and it's the thing that really needs to be reused.Derisible
P
6

I found this article helpful in furthering my understanding of IHttpClientFactory patterns.

When defining typed clients in your ConfigureServices method, the typed service is registered with transient scope. This means that a new instance is created by the DI container every time one is needed. The reason this occurs is that a [sic] HttpClient instance is injected into the typed client instance. That HttpClient instance is intended to be short lived so that the HttpClientFactory can ensure that the underlying handlers (and connections) are released and recycled.

Peptic answered 2/12, 2020 at 23:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.