diff --git a/Refit.HttpClientFactory/HttpClientFactoryCore.cs b/Refit.HttpClientFactory/HttpClientFactoryCore.cs
new file mode 100644
index 000000000..5684070da
--- /dev/null
+++ b/Refit.HttpClientFactory/HttpClientFactoryCore.cs
@@ -0,0 +1,291 @@
+
+using System;
+using System.Linq;
+using System.Net.Http;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Http;
+
+namespace Refit
+{
+ ///
+ /// HttpClientFactoryExtensions
+ ///
+ internal static class HttpClientFactoryCore
+ {
+
+ internal static IHttpClientBuilder AddRefitClientCore(
+ IServiceCollection services,
+ Type refitInterfaceType,
+ Func? settings,
+ string? httpClientName
+ )
+ {
+ if (services == null) throw new ArgumentNullException(nameof(services));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+
+ // register settings
+ var settingsType = typeof(SettingsFor<>).MakeGenericType(refitInterfaceType);
+ services.AddSingleton(
+ settingsType,
+ provider => Activator.CreateInstance(
+ typeof(SettingsFor<>).MakeGenericType(refitInterfaceType)!,
+ settings?.Invoke(provider)
+ )!
+ );
+
+ // register RequestBuilder
+ var requestBuilderType = typeof(IRequestBuilder<>).MakeGenericType(refitInterfaceType);
+ services.AddSingleton(
+ requestBuilderType,
+ provider => RequestBuilderGenericForTypeMethod
+ .MakeGenericMethod(refitInterfaceType)
+ .Invoke(
+ null,
+ [((ISettingsFor)provider.GetRequiredService(settingsType)).Settings]
+ )!
+ );
+
+ // create HttpClientBuilder
+ var builder = services.AddHttpClient(httpClientName ?? UniqueName.ForType(refitInterfaceType));
+
+ // configure message handler
+ builder.ConfigureHttpMessageHandlerBuilder(builderConfig =>
+ {
+ var handler = CreateInnerHandlerIfProvided(
+ ((ISettingsFor)builderConfig.Services.GetRequiredService(settingsType)).Settings
+ );
+ if (handler != null)
+ {
+ builderConfig.PrimaryHandler = handler;
+ }
+ });
+
+ // add typed client (register transient that resolves HttpClient from IHttpClientFactory and creates Refit client)
+ builder.Services.AddTransient(
+ refitInterfaceType,
+ s =>
+ {
+ var httpClientFactory = s.GetRequiredService();
+ var httpClient = httpClientFactory.CreateClient(builder.Name);
+ return RestService.For(
+ refitInterfaceType,
+ httpClient,
+ (IRequestBuilder)s.GetRequiredService(requestBuilderType)
+ );
+ }
+ );
+
+ return builder;
+ }
+
+ internal static IHttpClientBuilder AddKeyedRefitClientCore(
+ IServiceCollection services,
+ Type refitInterfaceType,
+ object? serviceKey,
+ Func? settings,
+ string? httpClientName
+ )
+ {
+ if (services == null) throw new ArgumentNullException(nameof(services));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ // register settings
+ var settingsType = typeof(SettingsFor<>).MakeGenericType(refitInterfaceType);
+ services.AddKeyedSingleton(
+ settingsType,
+ serviceKey,
+ (provider, _) => Activator.CreateInstance(
+ typeof(SettingsFor<>).MakeGenericType(refitInterfaceType)!,
+ settings?.Invoke(provider)
+ )!
+ );
+
+ // register RequestBuilder
+ var requestBuilderType = typeof(IRequestBuilder<>).MakeGenericType(refitInterfaceType);
+ services.AddKeyedSingleton(
+ requestBuilderType,
+ serviceKey,
+ (provider, _) => RequestBuilderGenericForTypeMethod
+ .MakeGenericMethod(refitInterfaceType)
+ .Invoke(
+ null,
+ [((ISettingsFor)provider.GetRequiredKeyedService(settingsType, serviceKey)).Settings]
+ )!
+ );
+
+ // create HttpClientBuilder
+ var builder = services.AddHttpClient(httpClientName ?? UniqueName.ForType(refitInterfaceType, serviceKey));
+
+ // configure primary handler
+ builder.ConfigurePrimaryHttpMessageHandler(serviceProvider =>
+ {
+ var settingsInstance = (ISettingsFor)serviceProvider.GetRequiredKeyedService(settingsType, serviceKey);
+ return settingsInstance.Settings?.HttpMessageHandlerFactory?.Invoke() ?? new HttpClientHandler();
+ });
+
+ // configure additional handlers
+ builder.ConfigureAdditionalHttpMessageHandlers((handlers, serviceProvider) =>
+ {
+ var settingsInstance = (ISettingsFor)serviceProvider.GetRequiredKeyedService(settingsType, serviceKey);
+ if (settingsInstance.Settings?.AuthorizationHeaderValueGetter is { } getToken)
+ {
+ handlers.Add(new AuthenticatedHttpClientHandler(null, getToken));
+ }
+ });
+
+ // add keyed typed client (register keyed transient that resolves HttpClient and creates Refit client)
+ builder.Services.AddKeyedTransient(
+ refitInterfaceType,
+ serviceKey,
+ (s, _) =>
+ {
+ var httpClientFactory = s.GetRequiredService();
+ var httpClient = httpClientFactory.CreateClient(builder.Name);
+ return RestService.For(
+ refitInterfaceType,
+ httpClient,
+ (IRequestBuilder)s.GetRequiredKeyedService(requestBuilderType, serviceKey)
+ );
+ }
+ );
+
+ return builder;
+ }
+
+ internal static IHttpClientBuilder AddRefitClientCore(
+ IServiceCollection services,
+ Type refitInterfaceType,
+ Func? settings,
+ string? httpClientName
+ ) where T : class
+ {
+ if (services == null) throw new ArgumentNullException(nameof(services));
+
+ // register settings
+ services.AddSingleton(provider => new SettingsFor(settings?.Invoke(provider)));
+
+ // register RequestBuilder
+ services.AddSingleton(provider =>
+ RequestBuilder.ForType(provider.GetRequiredService>().Settings)
+ );
+
+ // create HttpClientBuilder
+ var builder = services.AddHttpClient(httpClientName ?? UniqueName.ForType());
+
+ // configure message handler
+ builder.ConfigureHttpMessageHandlerBuilder(builderConfig =>
+ {
+ var handler = CreateInnerHandlerIfProvided(
+ builderConfig.Services.GetRequiredService>().Settings
+ );
+ if (handler != null)
+ {
+ builderConfig.PrimaryHandler = handler;
+ }
+ });
+
+ // add typed client using framework AddTypedClient
+ return builder.AddTypedClient((client, serviceProvider) =>
+ RestService.For(
+ client,
+ serviceProvider.GetRequiredService>()
+ )
+ );
+ }
+
+ internal static IHttpClientBuilder AddKeyedRefitClientCore(
+ IServiceCollection services,
+ Type refitInterfaceType,
+ object? serviceKey,
+ Func? settings,
+ string? httpClientName
+ ) where T : class
+ {
+ if (services == null) throw new ArgumentNullException(nameof(services));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ // register settings
+ services.AddKeyedSingleton(
+ serviceKey,
+ (provider, _) => new SettingsFor(settings?.Invoke(provider))
+ );
+
+ // register RequestBuilder
+ services.AddKeyedSingleton(
+ serviceKey,
+ (provider, _) =>
+ RequestBuilder.ForType(
+ provider.GetRequiredKeyedService>(serviceKey).Settings
+ )
+ );
+
+ // create HttpClientBuilder
+ var builder = services.AddHttpClient(httpClientName ?? UniqueName.ForType(serviceKey));
+
+ // configure primary handler
+ builder.ConfigurePrimaryHttpMessageHandler(serviceProvider =>
+ {
+ var settingsInstance = serviceProvider.GetRequiredKeyedService>(serviceKey).Settings;
+ return settingsInstance?.HttpMessageHandlerFactory?.Invoke() ?? new HttpClientHandler();
+ });
+
+ // configure additional handlers
+ builder.ConfigureAdditionalHttpMessageHandlers((handlers, serviceProvider) =>
+ {
+ var settingsInstance = serviceProvider.GetRequiredKeyedService>(serviceKey).Settings;
+ if (settingsInstance?.AuthorizationHeaderValueGetter is { } getToken)
+ {
+ handlers.Add(new AuthenticatedHttpClientHandler(null, getToken));
+ }
+ });
+
+ // add keyed typed client (inline keyed registration)
+ builder.Services.AddKeyedTransient(
+ serviceKey,
+ (s, _) =>
+ {
+ var httpClientFactory = s.GetRequiredService();
+ var httpClient = httpClientFactory.CreateClient(builder.Name);
+ return RestService.For(
+ httpClient,
+ s.GetRequiredKeyedService>(serviceKey)
+ );
+ }
+ );
+
+ return builder;
+ }
+
+ // helper - used by AddRefitClientCore and AddRefitClientCore
+ private static HttpMessageHandler? CreateInnerHandlerIfProvided(RefitSettings? settings)
+ {
+ HttpMessageHandler? innerHandler = null;
+ if (settings != null)
+ {
+ if (settings.HttpMessageHandlerFactory != null)
+ {
+ innerHandler = settings.HttpMessageHandlerFactory();
+ }
+
+ if (settings.AuthorizationHeaderValueGetter != null)
+ {
+ innerHandler = new AuthenticatedHttpClientHandler(
+ settings.AuthorizationHeaderValueGetter,
+ innerHandler
+ );
+ }
+ }
+
+ return innerHandler;
+ }
+
+ private static readonly MethodInfo RequestBuilderGenericForTypeMethod =
+ typeof(RequestBuilder)
+ .GetMethods(BindingFlags.Public | BindingFlags.Static)
+ .Single(z => z.IsGenericMethodDefinition && z.GetParameters().Length == 1);
+ }
+}
diff --git a/Refit.HttpClientFactory/HttpClientFactoryExtensions.cs b/Refit.HttpClientFactory/HttpClientFactoryExtensions.cs
index ff188bf9c..348e2d023 100644
--- a/Refit.HttpClientFactory/HttpClientFactoryExtensions.cs
+++ b/Refit.HttpClientFactory/HttpClientFactoryExtensions.cs
@@ -12,183 +12,185 @@ namespace Refit
///
public static class HttpClientFactoryExtensions
{
+ ///
+ /// Adds a Refit client to the DI container
+ ///
+ /// Type of the Refit interface
+ /// container
+ /// Optional. Settings to configure the instance with
+ /// Optional. Allows users to change the HttpClient name.
+ ///
+ public static IHttpClientBuilder AddRefitClient(
+ this IServiceCollection services,
+ Type refitInterfaceType,
+ RefitSettings? settings = null,
+ string? httpClientName = null
+ )
+ {
+ if (services == null) throw new ArgumentNullException(nameof(services));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+
+ return HttpClientFactoryCore.AddRefitClientCore(services, refitInterfaceType, _ => settings, httpClientName);
+ }
+
+ ///
+ /// Adds a Refit client to the DI container with a specified service key
+ ///
+ /// Type of the Refit interface
+ /// container
+ /// An optional key to associate with the specific Refit client instance
+ /// Optional. Settings to configure the instance with
+ /// Optional. Allows users to change the HttpClient name.
+ ///
+ public static IHttpClientBuilder AddKeyedRefitClient(
+ this IServiceCollection services,
+ Type refitInterfaceType,
+ object? serviceKey,
+ RefitSettings? settings = null,
+ string? httpClientName = null
+ )
+ {
+ if (services == null) throw new ArgumentNullException(nameof(services));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ return HttpClientFactoryCore.AddKeyedRefitClientCore(services, refitInterfaceType, serviceKey, _ => settings, httpClientName);
+ }
+
///
/// Adds a Refit client to the DI container
///
/// Type of the Refit interface
/// container
/// Optional. Settings to configure the instance with
+ /// Optional. Allows users to change the HttpClient name.
///
public static IHttpClientBuilder AddRefitClient(
this IServiceCollection services,
- RefitSettings? settings = null
+ RefitSettings? settings = null,
+ string? httpClientName = null
)
where T : class
{
- return AddRefitClient(services, _ => settings);
+ if (services == null) throw new ArgumentNullException(nameof(services));
+
+ return HttpClientFactoryCore.AddRefitClientCore(services, typeof(T), _ => settings, httpClientName);
}
///
- /// Adds a Refit client to the DI container
+ /// Adds a Refit client to the DI container with a specified service key
///
/// Type of the Refit interface
/// container
/// An optional key to associate with the specific Refit client instance
/// Optional. Settings to configure the instance with
+ /// Optional. Allows users to change the HttpClient name.
///
public static IHttpClientBuilder AddKeyedRefitClient(
this IServiceCollection services,
object? serviceKey,
- RefitSettings? settings = null
+ RefitSettings? settings = null,
+ string? httpClientName = null
)
where T : class
{
- return AddKeyedRefitClient(services, serviceKey, _ => settings);
+ if (services == null) throw new ArgumentNullException(nameof(services));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ return HttpClientFactoryCore.AddKeyedRefitClientCore(services, typeof(T), serviceKey, _ => settings, httpClientName);
}
///
/// Adds a Refit client to the DI container
///
- /// container
/// Type of the Refit interface
+ /// The HTTP client builder
/// Optional. Settings to configure the instance with
///
public static IHttpClientBuilder AddRefitClient(
- this IServiceCollection services,
+ this IHttpClientBuilder builder,
Type refitInterfaceType,
RefitSettings? settings = null
)
{
- return AddRefitClient(services, refitInterfaceType, _ => settings);
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+
+ return HttpClientFactoryCore.AddRefitClientCore(builder.Services, refitInterfaceType, _ => settings, builder.Name);
}
///
- /// Adds a Refit client to the DI container
+ /// Adds a Refit client to the DI container with a specified service key
///
- /// container
/// Type of the Refit interface
+ /// The HTTP client builder
/// An optional key to associate with the specific Refit client instance
/// Optional. Settings to configure the instance with
///
public static IHttpClientBuilder AddKeyedRefitClient(
- this IServiceCollection services,
+ this IHttpClientBuilder builder,
Type refitInterfaceType,
object? serviceKey,
RefitSettings? settings = null
)
{
- return AddKeyedRefitClient(services, refitInterfaceType, serviceKey, _ => settings);
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ return HttpClientFactoryCore.AddKeyedRefitClientCore(builder.Services, refitInterfaceType, serviceKey, _ => settings, builder.Name);
}
///
/// Adds a Refit client to the DI container
///
/// Type of the Refit interface
- /// container
- /// Optional. Action to configure refit settings. This method is called once and only once, avoid using any scoped dependencies that maybe be disposed automatically.
- /// Optional. Allows users to change the HttpClient name as provided to IServiceCollection.AddHttpClient. Useful for logging scenarios.
+ /// The HTTP client builder
+ /// Optional. Settings to configure the instance with
///
public static IHttpClientBuilder AddRefitClient(
- this IServiceCollection services,
- Func? settingsAction,
- string? httpClientName = null
+ this IHttpClientBuilder builder,
+ RefitSettings? settings = null
)
where T : class
{
- services.AddSingleton(provider => new SettingsFor(settingsAction?.Invoke(provider)));
- services.AddSingleton(
- provider =>
- RequestBuilder.ForType(
- provider.GetRequiredService>().Settings
- )
- );
-
- return services
- .AddHttpClient(httpClientName ?? UniqueName.ForType())
- .ConfigureHttpMessageHandlerBuilder(builder =>
- {
- // check to see if user provided custom auth token
- if (
- CreateInnerHandlerIfProvided(
- builder.Services.GetRequiredService>().Settings
- ) is
- { } innerHandler
- )
- {
- builder.PrimaryHandler = innerHandler;
- }
- })
- .AddTypedClient(
- (client, serviceProvider) =>
- RestService.For(
- client,
- serviceProvider.GetService>()!
- )
- );
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+
+ return HttpClientFactoryCore.AddRefitClientCore(builder.Services, typeof(T), _ => settings, builder.Name);
}
///
/// Adds a Refit client to the DI container with a specified service key
///
/// Type of the Refit interface
- /// container
+ /// The HTTP client builder
/// An optional key to associate with the specific Refit client instance
- /// Optional. Action to configure refit settings. This method is called once and only once, avoid using any scoped dependencies that maybe be disposed automatically.
- /// Optional. Allows users to change the HttpClient name as provided to IServiceCollection.AddHttpClient. Useful for logging scenarios.
+ /// Optional. Settings to configure the instance with
///
public static IHttpClientBuilder AddKeyedRefitClient(
- this IServiceCollection services,
+ this IHttpClientBuilder builder,
object? serviceKey,
- Func? settingsAction,
- string? httpClientName = null
+ RefitSettings? settings = null
)
where T : class
{
- services.AddKeyedSingleton(serviceKey,
- (provider, _) => new SettingsFor(settingsAction?.Invoke(provider)));
- services.AddKeyedSingleton(
- serviceKey,
- (provider, _) =>
- RequestBuilder.ForType(
- provider.GetRequiredKeyedService>(serviceKey).Settings
- )
- );
-
- return services
- .AddHttpClient(httpClientName ?? UniqueName.ForType(serviceKey))
- .ConfigurePrimaryHttpMessageHandler(serviceProvider =>
- {
- var settings = serviceProvider.GetRequiredKeyedService>(serviceKey).Settings;
- return
- settings?.HttpMessageHandlerFactory?.Invoke()
- ?? new HttpClientHandler();
- })
- .ConfigureAdditionalHttpMessageHandlers((handlers, serviceProvider) =>
- {
- var settings = serviceProvider.GetRequiredKeyedService>(serviceKey).Settings;
- if (settings?.AuthorizationHeaderValueGetter is { } getToken)
- {
- handlers.Add(new AuthenticatedHttpClientHandler(null, getToken));
- }
- })
- .AddKeyedTypedClient(
- serviceKey,
- (client, serviceProvider) =>
- RestService.For(
- client,
- serviceProvider.GetKeyedService>(serviceKey)!
- )
- );
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ return HttpClientFactoryCore.AddKeyedRefitClientCore(builder.Services, typeof(T), serviceKey, _ => settings, builder.Name);
}
///
/// Adds a Refit client to the DI container
///
- /// container
/// Type of the Refit interface
- /// Optional. Action to configure refit settings. This method is called once and only once, avoid using any scoped dependencies that maybe be disposed automatically.
- /// Optional. Allows users to change the HttpClient name as provided to IServiceCollection.AddHttpClient. Useful for logging scenarios.
+ /// container
+ /// Optional. Action to configure refit settings.
+ /// Optional. Allows users to change the HttpClient name.
///
+#if NET9_0_OR_GREATER
+ [System.Runtime.CompilerServices.OverloadResolutionPriority(1)]
+#endif
public static IHttpClientBuilder AddRefitClient(
this IServiceCollection services,
Type refitInterfaceType,
@@ -196,66 +198,24 @@ public static IHttpClientBuilder AddRefitClient(
string? httpClientName = null
)
{
- var settingsType = typeof(SettingsFor<>).MakeGenericType(refitInterfaceType);
- var requestBuilderType = typeof(IRequestBuilder<>).MakeGenericType(refitInterfaceType);
- services.AddSingleton(
- settingsType,
- provider =>
- Activator.CreateInstance(
- typeof(SettingsFor<>).MakeGenericType(refitInterfaceType)!,
- settingsAction?.Invoke(provider)
- )!
- );
- services.AddSingleton(
- requestBuilderType,
- provider =>
- RequestBuilderGenericForTypeMethod
- .MakeGenericMethod(refitInterfaceType)
- .Invoke(
- null,
- [
- ((ISettingsFor)provider.GetRequiredService(settingsType)).Settings
- ]
- )!
- );
-
- return services
- .AddHttpClient(httpClientName ?? UniqueName.ForType(refitInterfaceType))
- .ConfigureHttpMessageHandlerBuilder(builder =>
- {
- // check to see if user provided custom auth token
- if (
- CreateInnerHandlerIfProvided(
- (
- (ISettingsFor)builder.Services.GetRequiredService(settingsType)
- ).Settings
- ) is
- { } innerHandler
- )
- {
- builder.PrimaryHandler = innerHandler;
- }
- })
- .AddTypedClient(
- refitInterfaceType,
- (client, serviceProvider) =>
- RestService.For(
- refitInterfaceType,
- client,
- (IRequestBuilder)serviceProvider.GetRequiredService(requestBuilderType)
- )
- );
+ if (services == null) throw new ArgumentNullException(nameof(services));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+
+ return HttpClientFactoryCore.AddRefitClientCore(services, refitInterfaceType, settingsAction, httpClientName);
}
///
/// Adds a Refit client to the DI container with a specified service key
///
- /// container
/// Type of the Refit interface
+ /// container
/// An optional key to associate with the specific Refit client instance
- /// Optional. Action to configure refit settings. This method is called once and only once, avoid using any scoped dependencies that maybe be disposed automatically.
- /// Optional. Allows users to change the HttpClient name as provided to IServiceCollection.AddHttpClient. Useful for logging scenarios.
+ /// Optional. Action to configure refit settings.
+ /// Optional. Allows users to change the HttpClient name.
///
+#if NET9_0_OR_GREATER
+ [System.Runtime.CompilerServices.OverloadResolutionPriority(1)]
+#endif
public static IHttpClientBuilder AddKeyedRefitClient(
this IServiceCollection services,
Type refitInterfaceType,
@@ -264,178 +224,153 @@ public static IHttpClientBuilder AddKeyedRefitClient(
string? httpClientName = null
)
{
- var settingsType = typeof(SettingsFor<>).MakeGenericType(refitInterfaceType);
- var requestBuilderType = typeof(IRequestBuilder<>).MakeGenericType(refitInterfaceType);
- services.AddKeyedSingleton(
- settingsType,
- serviceKey,
- (provider, _) =>
- Activator.CreateInstance(
- typeof(SettingsFor<>).MakeGenericType(refitInterfaceType)!,
- settingsAction?.Invoke(provider)
- )!
- );
- services.AddKeyedSingleton(
- requestBuilderType,
- serviceKey,
- (provider, _) =>
- RequestBuilderGenericForTypeMethod
- .MakeGenericMethod(refitInterfaceType)
- .Invoke(
- null,
- [
- ((ISettingsFor)provider.GetRequiredKeyedService(settingsType, serviceKey)).Settings
- ]
- )!
- );
-
- return services
- .AddHttpClient(httpClientName ?? UniqueName.ForType(refitInterfaceType, serviceKey))
- .ConfigurePrimaryHttpMessageHandler(serviceProvider =>
- {
- var settings = (ISettingsFor)serviceProvider.GetRequiredKeyedService(settingsType, serviceKey);
- return
- settings.Settings?.HttpMessageHandlerFactory?.Invoke()
- ?? new HttpClientHandler();
- })
- .ConfigureAdditionalHttpMessageHandlers((handlers, serviceProvider) =>
- {
- var settings = (ISettingsFor)serviceProvider.GetRequiredKeyedService(settingsType, serviceKey);
- if (settings.Settings?.AuthorizationHeaderValueGetter is { } getToken)
- {
- handlers.Add(new AuthenticatedHttpClientHandler(null, getToken));
- }
- })
- .AddKeyedTypedClient(
- refitInterfaceType,
- serviceKey,
- (client, serviceProvider) =>
- RestService.For(
- refitInterfaceType,
- client,
- (IRequestBuilder)serviceProvider.GetRequiredKeyedService(requestBuilderType, serviceKey)
- )
- );
+ if (services == null) throw new ArgumentNullException(nameof(services));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ return HttpClientFactoryCore.AddKeyedRefitClientCore(services, refitInterfaceType, serviceKey, settingsAction, httpClientName);
}
- private static readonly MethodInfo RequestBuilderGenericForTypeMethod =
- typeof(RequestBuilder)
- .GetMethods(BindingFlags.Public | BindingFlags.Static)
- .Single(z => z.IsGenericMethodDefinition && z.GetParameters().Length == 1);
+ ///
+ /// Adds a Refit client to the DI container
+ ///
+ /// Type of the Refit interface
+ /// container
+ /// Optional. Action to configure refit settings.
+ /// Optional. Allows users to change the HttpClient name.
+ ///
+#if NET9_0_OR_GREATER
+ [System.Runtime.CompilerServices.OverloadResolutionPriority(1)]
+#endif
+ public static IHttpClientBuilder AddRefitClient(
+ this IServiceCollection services,
+ Func? settingsAction,
+ string? httpClientName = null
+ )
+ where T : class
+ {
+ if (services == null) throw new ArgumentNullException(nameof(services));
+
+ return HttpClientFactoryCore.AddRefitClientCore(services, typeof(T), settingsAction, httpClientName);
+ }
- static HttpMessageHandler? CreateInnerHandlerIfProvided(RefitSettings? settings)
+ ///
+ /// Adds a Refit client to the DI container with a specified service key
+ ///
+ /// Type of the Refit interface
+ /// container
+ /// An optional key to associate with the specific Refit client instance
+ /// Optional. Action to configure refit settings.
+ /// Optional. Allows users to change the HttpClient name.
+ ///
+#if NET9_0_OR_GREATER
+ [System.Runtime.CompilerServices.OverloadResolutionPriority(1)]
+#endif
+ public static IHttpClientBuilder AddKeyedRefitClient(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func? settingsAction,
+ string? httpClientName = null
+ )
+ where T : class
{
- HttpMessageHandler? innerHandler = null;
- if (settings != null)
- {
- if (settings.HttpMessageHandlerFactory != null)
- {
- innerHandler = settings.HttpMessageHandlerFactory();
- }
-
- if (settings.AuthorizationHeaderValueGetter != null)
- {
- innerHandler = new AuthenticatedHttpClientHandler(
- settings.AuthorizationHeaderValueGetter,
- innerHandler
- );
- }
- }
-
- return innerHandler;
+ if (services == null) throw new ArgumentNullException(nameof(services));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ return HttpClientFactoryCore.AddKeyedRefitClientCore(services, typeof(T), serviceKey, settingsAction, httpClientName);
}
- static IHttpClientBuilder AddTypedClient(
+ ///
+ /// Adds a Refit client to the DI container
+ ///
+ /// Type of the Refit interface
+ /// The HTTP client builder
+ /// Optional. Action to configure refit settings.
+ ///
+#if NET9_0_OR_GREATER
+ [System.Runtime.CompilerServices.OverloadResolutionPriority(1)]
+#endif
+ public static IHttpClientBuilder AddRefitClient(
this IHttpClientBuilder builder,
- Type type,
- Func factory
+ Type refitInterfaceType,
+ Func? settingsAction
)
{
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- if (factory == null)
- {
- throw new ArgumentNullException(nameof(factory));
- }
-
- builder.Services.AddTransient(
- type,
- s =>
- {
- var httpClientFactory = s.GetRequiredService();
- var httpClient = httpClientFactory.CreateClient(builder.Name);
-
- return factory(httpClient, s);
- }
- );
-
- return builder;
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+
+ return HttpClientFactoryCore.AddRefitClientCore(builder.Services, refitInterfaceType, settingsAction, builder.Name);
}
- static IHttpClientBuilder AddKeyedTypedClient(
+ ///
+ /// Adds a Refit client to the DI container with a specified service key
+ ///
+ /// Type of the Refit interface
+ /// The HTTP client builder
+ /// An optional key to associate with the specific Refit client instance
+ /// Optional. Action to configure refit settings.
+ ///
+#if NET9_0_OR_GREATER
+ [System.Runtime.CompilerServices.OverloadResolutionPriority(1)]
+#endif
+ public static IHttpClientBuilder AddKeyedRefitClient(
this IHttpClientBuilder builder,
- Type type,
+ Type refitInterfaceType,
object? serviceKey,
- Func factory
+ Func? settingsAction
)
{
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- if (factory == null)
- {
- throw new ArgumentNullException(nameof(factory));
- }
-
- builder.Services.AddKeyedTransient(
- type,
- serviceKey,
- (s, _) =>
- {
- var httpClientFactory = s.GetRequiredService();
- var httpClient = httpClientFactory.CreateClient(builder.Name);
-
- return factory(httpClient, s);
- }
- );
-
- return builder;
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ return HttpClientFactoryCore.AddKeyedRefitClientCore(builder.Services, refitInterfaceType, serviceKey, settingsAction, builder.Name);
}
- static IHttpClientBuilder AddKeyedTypedClient(
+ ///
+ /// Adds a Refit client to the DI container
+ ///
+ /// Type of the Refit interface
+ /// The HTTP client builder
+ /// Optional. Action to configure refit settings.
+ ///
+#if NET9_0_OR_GREATER
+ [System.Runtime.CompilerServices.OverloadResolutionPriority(1)]
+#endif
+ public static IHttpClientBuilder AddRefitClient(
+ this IHttpClientBuilder builder,
+ Func? settingsAction
+ )
+ where T : class
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+
+ return HttpClientFactoryCore.AddRefitClientCore(builder.Services, typeof(T), settingsAction, builder.Name);
+ }
+
+ ///
+ /// Adds a Refit client to the DI container with a specified service key
+ ///
+ /// Type of the Refit interface
+ /// The HTTP client builder
+ /// An optional key to associate with the specific Refit client instance
+ /// Optional. Action to configure refit settings.
+ ///
+#if NET9_0_OR_GREATER
+ [System.Runtime.CompilerServices.OverloadResolutionPriority(1)]
+#endif
+ public static IHttpClientBuilder AddKeyedRefitClient(
this IHttpClientBuilder builder,
object? serviceKey,
- Func factory
+ Func? settingsAction
)
where T : class
{
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- if (factory == null)
- {
- throw new ArgumentNullException(nameof(factory));
- }
-
- builder.Services.AddKeyedTransient(
- serviceKey,
- (s, _) =>
- {
- var httpClientFactory = s.GetRequiredService();
- var httpClient = httpClientFactory.CreateClient(builder.Name);
-
- return factory(httpClient, s);
- }
- );
-
- return builder;
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+
+ return HttpClientFactoryCore.AddKeyedRefitClientCore(builder.Services, typeof(T), serviceKey, settingsAction, builder.Name);
}
+
}
-}
+}
\ No newline at end of file
diff --git a/Refit.HttpClientFactory/HttpClientFactoryExtensions.tt b/Refit.HttpClientFactory/HttpClientFactoryExtensions.tt
new file mode 100644
index 000000000..b4992f3c4
--- /dev/null
+++ b/Refit.HttpClientFactory/HttpClientFactoryExtensions.tt
@@ -0,0 +1,181 @@
+<#@ template debug="false" hostspecific="false" language="C#" #>
+<#@ output extension=".cs" #>
+using System;
+using System.Linq;
+using System.Net.Http;
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Http;
+
+namespace Refit
+{
+ ///
+ /// HttpClientFactoryExtensions.
+ ///
+ public static class HttpClientFactoryExtensions
+ {
+<#
+ // 位运算控制组合
+ for (int bits = 0; bits < 16; bits++)
+ {
+ bool hasKeyed = (bits & 1) != 0; // 0001: 是否有Keyed
+ bool hasGeneric = (bits & 2) != 0; // 0010: 是否泛型
+ bool isBuilder = (bits & 4) != 0; // 0100: 是否builder扩展
+ bool hasAction = (bits & 8) != 0; // 1000: 是否有settingsAction
+
+ // 生成XML注释
+#>
+ ///
+<# if (hasKeyed) { #>
+ /// Adds a Refit client to the DI container with a specified service key
+<# } else { #>
+ /// Adds a Refit client to the DI container
+<# } #>
+ ///
+<# if (hasGeneric) { #>
+ /// Type of the Refit interface
+<# } else { #>
+ /// Type of the Refit interface
+<# } #>
+<# if (isBuilder) { #>
+ /// The HTTP client builder
+<# } else { #>
+ /// container
+<# } #>
+<# if (hasKeyed) { #>
+ /// An optional key to associate with the specific Refit client instance
+<# } #>
+<# if (hasAction) { #>
+ /// Optional. Action to configure refit settings.
+<# } else { #>
+ /// Optional. Settings to configure the instance with
+<# } #>
+<# if (!isBuilder) { #>
+ /// Optional. Allows users to change the HttpClient name.
+<# } #>
+ ///
+<# if(hasAction) { #>
+#if NET9_0_OR_GREATER
+ [System.Runtime.CompilerServices.OverloadResolutionPriority(1)]
+#endif
+<# } #>
+<#
+ // 生成方法签名
+ string methodName = (hasKeyed ? "AddKeyedRefitClient" : "AddRefitClient");
+#>
+ public static IHttpClientBuilder <#= methodName #><#= hasGeneric ? "" : "" #>(
+ this <#= isBuilder ? "IHttpClientBuilder" : "IServiceCollection" #> <#= isBuilder ? "builder" : "services" #>,
+<# if (!hasGeneric) { #>
+ Type refitInterfaceType,
+<# } #>
+<# if (hasKeyed) { #>
+ object? serviceKey,
+<# } #>
+<# if (hasAction) { #>
+ Func? settingsAction<#= !isBuilder ? "," : "" #>
+<# } else { #>
+ RefitSettings? settings = null<#= !isBuilder ? "," : "" #>
+<# } #>
+<# if (!isBuilder) { #>
+ string? httpClientName = null
+<# } #>
+ )
+<# if (hasGeneric) { #>
+ where T : class
+<# } #>
+ {
+<#
+ // 生成参数验证
+ if (isBuilder)
+ {
+#>
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+<#
+ }
+ else
+ {
+#>
+ if (services == null) throw new ArgumentNullException(nameof(services));
+<#
+ }
+
+ if (!hasGeneric)
+ {
+#>
+ if (refitInterfaceType == null) throw new ArgumentNullException(nameof(refitInterfaceType));
+<#
+ }
+
+ if (hasKeyed)
+ {
+#>
+ if (serviceKey == null) throw new ArgumentNullException(nameof(serviceKey));
+<#
+ }
+#>
+
+<#
+ // 生成Core方法调用
+ string coreMethod = "HttpClientFactoryCore.";
+ coreMethod += (hasKeyed ? "AddKeyedRefitClient" : "AddRefitClient") + "Core";
+ coreMethod += hasGeneric ? "" : "";
+ coreMethod += "(";
+
+ // 第一个参数:services
+ if (isBuilder)
+ {
+ coreMethod += "builder.Services";
+ }
+ else
+ {
+ coreMethod += "services";
+ }
+
+ // 第二个参数:Type
+ if (hasGeneric)
+ {
+ coreMethod += ", typeof(T)";
+ }
+ else
+ {
+ coreMethod += ", refitInterfaceType";
+ }
+
+ // 第三个参数:serviceKey
+ if (hasKeyed)
+ {
+ coreMethod += ", serviceKey";
+ }
+
+ // 第四个参数:settingsAction
+ if (hasAction)
+ {
+ coreMethod += ", settingsAction";
+ }
+ else
+ {
+ coreMethod += ", _ => settings";
+ }
+
+ // 第五个参数:httpClientName
+ if (isBuilder)
+ {
+ // IHttpClientBuilder 版本使用 builder.Name
+ coreMethod += ", builder.Name";
+ }
+ else
+ {
+ // IServiceCollection 版本使用传入的 httpClientName
+ coreMethod += ", httpClientName";
+ }
+
+ coreMethod += ")";
+#>
+ return <#= coreMethod #>;
+ }
+
+<#
+ }
+#>
+ }
+}
\ No newline at end of file
diff --git a/Refit.HttpClientFactory/Refit.HttpClientFactory.csproj b/Refit.HttpClientFactory/Refit.HttpClientFactory.csproj
index a6f6558f2..4920d7e0d 100644
--- a/Refit.HttpClientFactory/Refit.HttpClientFactory.csproj
+++ b/Refit.HttpClientFactory/Refit.HttpClientFactory.csproj
@@ -13,4 +13,23 @@
+
+
+ TextTemplatingFileGenerator
+ HttpClientFactoryExtensions.cs
+
+
+
+
+
+
+
+
+
+ True
+ True
+ HttpClientFactoryExtensions.tt
+
+
+
diff --git a/Refit.Tests/HttpClientFactoryExtensionsTests.cs b/Refit.Tests/HttpClientFactoryExtensionsTests.cs
index ec64b65d0..0f903a2f9 100644
--- a/Refit.Tests/HttpClientFactoryExtensionsTests.cs
+++ b/Refit.Tests/HttpClientFactoryExtensionsTests.cs
@@ -190,11 +190,12 @@ public void ProvidedHttpClientIsUsedAsNamedClient()
var baseUri = new Uri("https://0:1337");
var services = new ServiceCollection();
- services.AddHttpClient("MyHttpClient", client => {
+ services.AddHttpClient("MyHttpClient", client =>
+ {
client.BaseAddress = baseUri;
client.DefaultRequestHeaders.Add("X-Powered-By", Environment.OSVersion.VersionString);
});
- services.AddRefitClient(null, "MyHttpClient");
+ services.AddRefitClient(settingsAction: null, "MyHttpClient");
var sp = services.BuildServiceProvider();
var httpClientFactory = sp.GetRequiredService();