Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ public static partial class HttpClientBuilderExtensions
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder AddTypedClient<TClient>(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Func<System.Net.Http.HttpClient, System.IServiceProvider, TClient> factory) where TClient : class { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder AddTypedClient<TClient>(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Func<System.Net.Http.HttpClient, TClient> factory) where TClient : class { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder AddTypedClient<TClient, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder) where TClient : class where TImplementation : class, TClient { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder ConfigureAdditionalHttpMessageHandlers(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Action<System.Collections.Generic.IList<System.Net.Http.DelegatingHandler>, System.IServiceProvider> configureAdditionalHandlers) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder ConfigureHttpClient(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Action<System.IServiceProvider, System.Net.Http.HttpClient> configureClient) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder ConfigureHttpClient(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Action<System.Net.Http.HttpClient> configureClient) { throw null; }
[System.Obsolete("This method has been deprecated. Use ConfigurePrimaryHttpMessageHandler or ConfigureAdditionalHttpMessageHandlers instead.")]
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder ConfigureHttpMessageHandlerBuilder(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Action<Microsoft.Extensions.Http.HttpMessageHandlerBuilder> configureBuilder) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder ConfigurePrimaryHttpMessageHandler(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Func<System.IServiceProvider, System.Net.Http.HttpMessageHandler> configureHandler) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder ConfigurePrimaryHttpMessageHandler(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Func<System.Net.Http.HttpMessageHandler> configureHandler) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder ConfigurePrimaryHttpMessageHandler<THandler>(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder) where THandler : System.Net.Http.HttpMessageHandler { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder ConfigurePrimaryHttpMessageHandler(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Action<System.Net.Http.HttpMessageHandler, System.IServiceProvider> configureHandler) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder RedactLoggedHeaders(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Collections.Generic.IEnumerable<string> redactedLoggedHeaderNames) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder RedactLoggedHeaders(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.Func<string, bool> shouldRedactHeaderValue) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder SetHandlerLifetime(this Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder, System.TimeSpan handlerLifetime) { throw null; }
Expand All @@ -44,6 +47,7 @@ public static partial class HttpClientFactoryServiceCollectionExtensions
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder AddHttpClient<TClient, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, string name, System.Action<System.Net.Http.HttpClient> configureClient) where TClient : class where TImplementation : class, TClient { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder AddHttpClient<TClient, TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, string name, System.Func<System.Net.Http.HttpClient, System.IServiceProvider, TImplementation> factory) where TClient : class where TImplementation : class, TClient { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHttpClientBuilder AddHttpClient<TClient, TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, string name, System.Func<System.Net.Http.HttpClient, TImplementation> factory) where TClient : class where TImplementation : class, TClient { throw null; }
public static Microsoft.Extensions.DependencyInjection.IServiceCollection ConfigureHttpClientDefaults(Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.Extensions.DependencyInjection.IHttpClientBuilder> configure) { throw null; }
}
public partial interface IHttpClientBuilder
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Linq;

namespace Microsoft.Extensions.DependencyInjection
{
internal sealed class DefaultHttpClientBuilder : IHttpClientBuilder
{
public DefaultHttpClientBuilder(IServiceCollection services, string name)
{
Services = services;
Name = name;
// The tracker references a descriptor. It marks the position of where default services are added to the collection.
var tracker = (DefaultHttpClientConfigurationTracker?)services.Single(sd => sd.ServiceType == typeof(DefaultHttpClientConfigurationTracker)).ImplementationInstance;
Debug.Assert(tracker != null);

Services = new DefaultHttpClientBuilderServiceCollection(services, name == null, tracker);
Name = name!;
}

public string Name { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.DependencyInjection
{
internal sealed class DefaultHttpClientBuilderServiceCollection : IServiceCollection
{
private readonly IServiceCollection _services;
private readonly bool _isDefault;
private readonly DefaultHttpClientConfigurationTracker _tracker;

public DefaultHttpClientBuilderServiceCollection(IServiceCollection services, bool isDefault, DefaultHttpClientConfigurationTracker tracker)
{
_services = services;
_isDefault = isDefault;
_tracker = tracker;
}

public void Add(ServiceDescriptor item)
{
if (item.ServiceType != typeof(IConfigureOptions<HttpClientFactoryOptions>))
{
_services.Add(item);
return;
}

if (_isDefault)
{
// Insert IConfigureOptions<HttpClientFactoryOptions> services into the collection before named config descriptors.
// This ensures they run and apply configuration first. Configuration for named clients run afterwards.
if (_tracker.InsertDefaultsAfterDescriptor != null &&
_services.IndexOf(_tracker.InsertDefaultsAfterDescriptor) is var index && index != -1)
{
index++;
_services.Insert(index, item);
}
else
{
_services.Add(item);
}

_tracker.InsertDefaultsAfterDescriptor = item;
}
else
{
// Track the location of where the first named config descriptor was added.
_tracker.InsertDefaultsAfterDescriptor ??= _services.Last();

_services.Add(item);
}
}

public ServiceDescriptor this[int index]
{
get => _services[index];
set => _services[index] = value;
}
public int Count => _services.Count;
public bool IsReadOnly => _services.IsReadOnly;
public void Clear() => _services.Clear();
public bool Contains(ServiceDescriptor item) => _services.Contains(item);
public void CopyTo(ServiceDescriptor[] array, int arrayIndex) => _services.CopyTo(array, arrayIndex);
public IEnumerator<ServiceDescriptor> GetEnumerator() => _services.GetEnumerator();
public int IndexOf(ServiceDescriptor item) => _services.IndexOf(item);
public void Insert(int index, ServiceDescriptor item) => _services.Insert(index, item);
public bool Remove(ServiceDescriptor item) => _services.Remove(item);
public void RemoveAt(int index) => _services.RemoveAt(index);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.Extensions.DependencyInjection
{
internal sealed class DefaultHttpClientConfigurationTracker
{
public ServiceDescriptor? InsertDefaultsAfterDescriptor { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,40 @@ public static IHttpClientBuilder ConfigurePrimaryHttpMessageHandler<THandler>(th
return builder;
}

/// <summary>
/// Adds a delegate that will be used to configure the primary <see cref="HttpMessageHandler"/> for a
/// named <see cref="HttpClient"/>.
/// </summary>
/// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param>
/// <param name="configureHandler">A delegate that is used to configure a previously set or default primary <see cref="HttpMessageHandler"/>.</param>
/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
/// <remarks>
/// <para>
/// The <see cref="IServiceProvider"/> argument provided to <paramref name="configureHandler"/> will be
/// a reference to a scoped service provider that shares the lifetime of the handler being constructed.
/// </para>
/// </remarks>
public static IHttpClientBuilder ConfigurePrimaryHttpMessageHandler(this IHttpClientBuilder builder, Action<HttpMessageHandler, IServiceProvider> configureHandler)
{
ThrowHelper.ThrowIfNull(builder);
ThrowHelper.ThrowIfNull(configureHandler);

builder.Services.Configure<HttpClientFactoryOptions>(builder.Name, options =>
{
options.HttpMessageHandlerBuilderActions.Add(b => configureHandler(b.PrimaryHandler, b.Services));
});

return builder;
}

/// <summary>
/// Adds a delegate that will be used to configure message handlers using <see cref="HttpMessageHandlerBuilder"/>
/// for a named <see cref="HttpClient"/>.
/// </summary>
/// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param>
/// <param name="configureBuilder">A delegate that is used to configure an <see cref="HttpMessageHandlerBuilder"/>.</param>
/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
[Obsolete("This method has been deprecated. Use ConfigurePrimaryHttpMessageHandler or ConfigureAdditionalHttpMessageHandlers instead.")]
public static IHttpClientBuilder ConfigureHttpMessageHandlerBuilder(this IHttpClientBuilder builder, Action<HttpMessageHandlerBuilder> configureBuilder)
{
ThrowHelper.ThrowIfNull(builder);
Expand Down Expand Up @@ -275,6 +302,11 @@ public static IHttpClientBuilder ConfigureHttpMessageHandlerBuilder(this IHttpCl
this IHttpClientBuilder builder, bool validateSingleType)
where TClient : class
{
if (builder.Name is null)
{
throw new InvalidOperationException($"{nameof(HttpClientBuilderExtensions.AddTypedClient)} isn't supported with {nameof(HttpClientFactoryServiceCollectionExtensions.ConfigureHttpClientDefaults)}.");
}

ReserveClient(builder, typeof(TClient), builder.Name, validateSingleType);

builder.Services.AddTransient(s => AddTransientHelper<TClient>(s, builder));
Expand Down Expand Up @@ -531,6 +563,26 @@ public static IHttpClientBuilder SetHandlerLifetime(this IHttpClientBuilder buil
return builder;
}

/// <summary>
/// Adds a delegate that will be used to configure additional message handlers using <see cref="HttpMessageHandlerBuilder"/>
/// for a named <see cref="HttpClient"/>.
/// </summary>
/// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param>
/// <param name="configureAdditionalHandlers">A delegate that is used to configure a collection of <see cref="DelegatingHandler"/>s.</param>
/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
public static IHttpClientBuilder ConfigureAdditionalHttpMessageHandlers(this IHttpClientBuilder builder, Action<IList<DelegatingHandler>, IServiceProvider> configureAdditionalHandlers)
{
ThrowHelper.ThrowIfNull(builder);
ThrowHelper.ThrowIfNull(configureAdditionalHandlers);

builder.Services.Configure<HttpClientFactoryOptions>(builder.Name, options =>
{
options.HttpMessageHandlerBuilderActions.Add(b => configureAdditionalHandlers(b.AdditionalHandlers, b.Services));
});

return builder;
}

// See comments on HttpClientMappingRegistry.
private static void ReserveClient(IHttpClientBuilder builder, Type type, string name, bool validateSingleType)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Http;
Expand Down Expand Up @@ -50,6 +53,9 @@ public static IServiceCollection AddHttpClient(this IServiceCollection services)
// because we access it by reaching into the service collection.
services.TryAddSingleton(new HttpClientMappingRegistry());

// This is used to store configuration for the default builder.
services.TryAddSingleton(new DefaultHttpClientConfigurationTracker());

// Register default client as HttpClient
services.TryAddTransient(s =>
{
Expand All @@ -59,6 +65,24 @@ public static IServiceCollection AddHttpClient(this IServiceCollection services)
return services;
}

/// <summary>
/// Adds a delegate that will be used to configure all <see cref="HttpClient"/> instances.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <param name="configure">A delegate that is used to configure an <see cref="IHttpClientBuilder"/>.</param>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection ConfigureHttpClientDefaults(this IServiceCollection services, Action<IHttpClientBuilder> configure)
{
ThrowHelper.ThrowIfNull(services);
ThrowHelper.ThrowIfNull(configure);

AddHttpClient(services);

configure(new DefaultHttpClientBuilder(services, name: null!));

return services;
}

/// <summary>
/// Adds the <see cref="IHttpClientFactory"/> and related services to the <see cref="IServiceCollection"/> and configures
/// a named <see cref="HttpClient"/>.
Expand Down
Loading