Skip to content
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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

stormaref
Copy link

Changed DefaultHttpClientBuilder name and added new test

Fix #110996

Changed DefaultHttpClientBuilder name and added new test

Fix dotnet#110996
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Dec 30, 2024
@stormaref
Copy link
Author

@stormaref please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@dotnet-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@dotnet-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@dotnet-policy-service agree company="Microsoft"

Contributor License Agreement

@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);

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)

Copy link
Author

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

@pinkfloydx33
Copy link

Fix #110996

Isn't this documented and expected behavior?
From https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory-troubleshooting#different-typed-clients-are-registered-on-a-common-interface

DO NOT register multiple typed clients on a single interface without explicitly specifying the name.

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)

@stormaref
Copy link
Author

stormaref commented Dec 31, 2024

Fix #110996

Isn't this documented and expected behavior? From https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory-troubleshooting#different-typed-clients-are-registered-on-a-common-interface

DO NOT register multiple typed clients on a single interface without explicitly specifying the name.

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.
Also if you think that's a serious problem, I can simply concat the name of the interface with the name of the implementation so it would fix both problems

@@ -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);
Copy link
Contributor

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!

Copy link
Author

@stormaref stormaref Jan 1, 2025

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?

Copy link
Contributor

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.

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.

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;
}
}

  1. Two httpclients configured by httpclientfactory for classes with same name conflict extensions#960
  2. Fix #519 extensions#1563

@stormaref stormaref requested a review from ArminShoeibi January 2, 2025 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Extensions-HttpClientFactory community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Adding multiple HttpClients with same Interface results in conflicting configurations
3 participants