Summary
Today the .AsHttpClient() / .AsHttpClient(name) selectors register every scanned class as a typed HttpClient, but they don't offer a way to give each client its own configuration. Consumers either point everything at a single pre-registered named client (shared base address/handlers) or get a default client per type with no configuration hook.
This is precisely the limitation the Scrutor maintainer raised when declining native support in khellang/Scrutor#180:
convention registration gives every scanned client the same Action<HttpClient> config … at that point I don't see much value in scanning and registering based on convention.
Closing that gap would make the library useful for fleets of clients that share a scheme but not identical config (per-client base path, headers, timeouts, resilience).
Proposed API (sketch)
Add overloads that thread a configuration callback through to the underlying AddHttpClient<TClient,TImplementation>(...):
// Configure by client type — invoked once per scanned (service, implementation) pair.
public static IServiceTypeSelector AsHttpClient(
this IServiceTypeSelector selector,
Action<IServiceProvider, Type, HttpClient> configureClient);
// Named base client + per-client tweaks on top.
public static IServiceTypeSelector AsHttpClient(
this IServiceTypeSelector selector,
string name,
Action<IServiceProvider, Type, HttpClient> configureClient);
The Type argument lets a single callback branch per client (e.g. derive the base path from the client name/attribute), keeping the convention-registration ergonomics while allowing divergence.
Considerations
- Overload resolution / reflection:
AsHttpClient already resolves the right AddHttpClient<,> generic via reflection; the config-taking overloads ((IServiceCollection, Action<HttpClient>), (IServiceCollection, string, Action<HttpClient>), and the IServiceProvider-aware variants) would be matched the same way. Keep the current shape-matching logic in HttpClientRegistrationStrategy general enough to select them.
- Trim/AOT: stays behind the existing
[RequiresUnreferencedCode]/[RequiresDynamicCode] annotations — no change to the AOT story.
- Public API: additive; existing overloads unchanged. Would need new
PublicApiGenerator baseline entries + specs (a per-type config callback firing with the expected HttpClient).
- Design question: is a per-
Type discriminator enough, or do we also want an attribute-driven config (e.g. [HttpClient(BaseAddress = "...")]) read at registration time? The attribute route overlaps with the source-generator direction we deliberately deferred.
Priority
Future enhancement — not urgent. Filed to capture the idea; the library is fully functional without it (named-client pattern covers the common case).
Summary
Today the
.AsHttpClient()/.AsHttpClient(name)selectors register every scanned class as a typedHttpClient, but they don't offer a way to give each client its own configuration. Consumers either point everything at a single pre-registered named client (shared base address/handlers) or get a default client per type with no configuration hook.This is precisely the limitation the Scrutor maintainer raised when declining native support in khellang/Scrutor#180:
Closing that gap would make the library useful for fleets of clients that share a scheme but not identical config (per-client base path, headers, timeouts, resilience).
Proposed API (sketch)
Add overloads that thread a configuration callback through to the underlying
AddHttpClient<TClient,TImplementation>(...):The
Typeargument lets a single callback branch per client (e.g. derive the base path from the client name/attribute), keeping the convention-registration ergonomics while allowing divergence.Considerations
AsHttpClientalready resolves the rightAddHttpClient<,>generic via reflection; the config-taking overloads ((IServiceCollection, Action<HttpClient>),(IServiceCollection, string, Action<HttpClient>), and theIServiceProvider-aware variants) would be matched the same way. Keep the current shape-matching logic inHttpClientRegistrationStrategygeneral enough to select them.[RequiresUnreferencedCode]/[RequiresDynamicCode]annotations — no change to the AOT story.PublicApiGeneratorbaseline entries + specs (a per-type config callback firing with the expectedHttpClient).Typediscriminator enough, or do we also want an attribute-driven config (e.g.[HttpClient(BaseAddress = "...")]) read at registration time? The attribute route overlaps with the source-generator direction we deliberately deferred.Priority
Future enhancement — not urgent. Filed to capture the idea; the library is fully functional without it (named-client pattern covers the common case).