-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix conflicting HttpClient configs #110999
base: main
Are you sure you want to change the base?
Conversation
Changed DefaultHttpClientBuilder name and added new test Fix dotnet#110996
@dotnet-policy-service agree |
@@ -450,7 +450,7 @@ public static IHttpClientBuilder AddHttpClient(this IServiceCollection services, | |||
|
|||
AddHttpClient(services); | |||
|
|||
string name = TypeNameHelper.GetTypeDisplayName(typeof(TClient), fullName: false); | |||
string name = TypeNameHelper.GetTypeDisplayName(typeof(TImplementation), fullName: false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes the documentation on the method (and any that call it) now incorrect. (line 440)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right I'm gonna change the documentation
Isn't this documented and expected behavior?
Also doesn't it just create the inverse problem? Ie. you'd not be able to reuse a single concrete type for multiple interfaces (though I don't know if that's a valid use case to begin with) |
It's so common to have one interface with multiple implementation and it is also common to add one interface with multiple implementations; actually it is the whole definition of IoC. Meanwhile it's not common to add one implementation with two interface. |
@@ -450,7 +450,7 @@ public static IHttpClientBuilder AddHttpClient(this IServiceCollection services, | |||
|
|||
AddHttpClient(services); | |||
|
|||
string name = TypeNameHelper.GetTypeDisplayName(typeof(TClient), fullName: false); | |||
string name = TypeNameHelper.GetTypeDisplayName(typeof(TImplementation), fullName: false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this introduces a breaking change. However, if you're looking to have multiple implementations for an interface, you can take advantage of the options available for named and typed HttpClients. It’s a great way to handle different scenarios!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are indeed several ways to achieve this, but this is something that ought to work as expected out of the box. With dependency injection, the expected behavior is to allow different implementations of an interface to coexist without conflicts. However, the current setup leads to issues, as evidenced by many people encountering this problem.
Here’s a related discussion on StackOverflow that highlights this issue: https://stackoverflow.com/questions/74005464/add-httpclients-with-same-interface-ended-up-having-the-same-base-url-asp-net-co
Also, what is the problem with changing this behavior? What do you think could go wrong with allowing multiple HttpClient registrations under the same interface to retain their independent configurations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that this code might be missing a few other implementations. Just wanted to point it out.
Lines 707 to 716 in 6eba467
public static IHttpClientBuilder AddHttpClient<TClient, TImplementation>(this IServiceCollection services, Func<HttpClient, TImplementation> factory) | |
where TClient : class | |
where TImplementation : class, TClient | |
{ | |
ThrowHelper.ThrowIfNull(services); | |
ThrowHelper.ThrowIfNull(factory); | |
string name = TypeNameHelper.GetTypeDisplayName(typeof(TClient), fullName: false); | |
return AddHttpClient<TClient, TImplementation>(services, name, factory); | |
} |
About your question:
what is the problem with changing this behavior?
I don't know, but @CarnaViire can help.
I encourage you to explore these links and review the related issues.
Lines 765 to 785 in 95814d0
if (registry.NamedClientRegistrations.TryGetValue(name, out Type? otherType) && | |
// Allow using the same name with multiple types in some cases (see callers). | |
validateSingleType && | |
// Allow registering the same name twice to the same type. | |
type != otherType) | |
{ | |
string message = | |
$"The HttpClient factory already has a registered client with the name '{name}', bound to the type '{otherType.FullName}'. " + | |
$"Client names are computed based on the type name without considering the namespace ('{otherType.Name}'). " + | |
$"Use an overload of AddHttpClient that accepts a string and provide a unique name to resolve the conflict."; | |
throw new InvalidOperationException(message); | |
} | |
if (validateSingleType) | |
{ | |
registry.NamedClientRegistrations[name] = type; | |
} | |
} | |
Changed DefaultHttpClientBuilder name and added new test
Fix #110996