-
Notifications
You must be signed in to change notification settings - Fork 72
Adding a HttpMessageHandlerType that is registred as Transint in IServiceCollection creates strange results #166
Comments
Hi @petterek - the factory creates and caches a 'chain' of handlers. Since they are all linked together they need to all have the same lifetime, which is why we end up caching your class for the same lifetime as the underlying http client handler. It sounds like what you want is for |
That is correct. I solved it by creating and accepting an The issue for me was that no documentaion says anything about "we will take your transient handler and cache it, so it is not transient any more". |
@petterek - can I ask what the problem is that you're trying to solve by doing this? Are you trying to tunnel information from the ambient DI scope into the client? |
I was trying to inject a "per call" value into the header, of the request. I solved it in my testcase by injecting a factory that is singleton, and then altering the value in the singleton, in front of every call. My point is that the Handler that is registered as transient, is cached by the factory, and suddenly is it not transient any more, but kind of "transient every 10 sec", so the you are "changing" the lifetime of the handler from what is registerd in the DI container. |
@petterek I have a similar problems where I want to flow input request headers like correlation ID to the outbound request.
@rynowak This works well when the configuration doesn't depends of the request/response. To do this, I need a scoped middleware. And before moving to HttpClientFactory, I managed to only cache the HttpClientHandler and resolve the handler chain each time. If you re not considering scoping the lifetime of the handler, I think AddHttpMessageHandler should be clear either in its name or in its documentation as to which scope will be applied. For now, there is a remarks which is very confusing : "The configureHandler delegate should return a new instance of the message handler each time it is invoked." |
Any more response to this from the team? @rynowak |
Yes, this is what the factory does. We can clear up the docs about this, but the answer is that the factory manages the lifetime of the handlers. We also discovered that the 2.1 behavior with handlers that come from DI is broken in a significant way other than what's being discussed on this thread. Namely, that a transient handler can outlive the DI scope that it was resolved in, which means that it will be disposed while it's still in use. We're making a change to how the factory uses DI in 2.2 - each time the factory instantiates a handler chain, we will create a scope associated with that handler chain, and keep the scope open until the handler is ready to be disposed. This allows handlers to use other services of any lifetime which is currently broken. So it sounds like what you're asking is to pass data into a handler - there are 2 major ways I can think of to do that:
Is your data per-request or per client? |
My data is pr request, and I have solved my issue, by injecting a factory to handle the data.. And I belive the miss behavior you describe, is part of the same issue that @cortex93 and I is describing. I might not see all the use cases here, but caching the last handler only, whitch is really the one you know and care about, should solve the problem? Letting the DI cache everything else.. |
No this is not possible sadly. |
It's per-request. But as you mentioned there is way to circumvent that and I use ihttpcontextaccessor. With the current implementation, I think it's best to think the whole chain as a singleton even if it's recreated from time to time. The fact is that your handler must depend only on singleton dependencies. About disposed handlers issues, shouldn't be the responsability of the developpers to create the appropriate scope to resolve from ? |
It's not a workable solution for the handlers resolved by the factory to share DI scope with the current request. You should use If you have business logic concerns to handle, I would suggest writing a typed client, and putting the functionality there. That is designed to share DI scope with the current request (if you're in ASP.NET COre). |
You create something like what you're asking for by using the new
You lose the ability to use the builder to configure HttpClient, but I can't think of anything else you'd miss out on. |
Sounds ok for me. |
Closing this since we provided a solution. We've already made changes to have handlers interact with DI in 2.2 to fix several other blocking issues. If you want handlers to share DI scope with the currrent request, you will need to do something like this - you can also use |
If you add a HttpMessageHandler to the list of handlers you want,
httpClientBuilder.AddHttpMessageHandler<CustomHeadersAuth>();
then you need to register the type in the ServiceContainer.
They are now all registered as transient, but..
if I then do a loop creating and using the
HttpClientFactory.CreateClient("somename")
the value of the first created SomeContextValue is used for all instances of the Client.
I have looked at the code and this is probably by design , because the messageHandlers are cached, but this is kind of hard to figure out, and also frustating..
I do not know what a good solution for this whould be, I solved it in my case by using a factory and getting the value in the SendAsync method in stead.
Should there be some inspection of how the handlers is registerd?
The text was updated successfully, but these errors were encountered: