Remove ApiHttpMessageHandler and update ElsaClient configuration

The ApiHttpMessageHandler and the related configuration in ElsaClientOptions have been removed. The configuration for creating Api clients in DependencyInjectionExtensions has been updated to handle these changes. This includes the addition of a retry policy in case of transient HTTP error and changing the way clients are created to use the HttpClientFactory directly.
This commit is contained in:
Sipke Schoorstra 2023-12-09 20:44:11 +01:00
parent cc18e99d24
commit c97282f7d5
5 changed files with 30 additions and 73 deletions

View file

@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props"/>
<Import Project="..\..\..\configureawait.props"/>
<Import Project="..\..\..\frameworks.props"/>
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Description>
Provides HTTP clients for the Elsa Workflows REST API.
</Description>
@ -12,17 +12,22 @@
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.14"/>
<PackageReference Include="Polly" Version="7.2.4"/>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.0"/>
<PackageReference Include="Polly" Version="8.2.0"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Refit" Version="7.0.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="7.0.0" />
<PackageReference Include="Humanizer.Core" Version="2.14.1"/>
<PackageReference Include="Polly.Extensions.Http" Version="3.0.0"/>
<PackageReference Include="Refit" Version="7.0.0"/>
<PackageReference Include="Refit.HttpClientFactory" Version="7.0.0"/>
</ItemGroup>
</Project>

View file

@ -2,9 +2,8 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Elsa.Api.Client.Contracts;
using Elsa.Api.Client.Converters;
using Elsa.Api.Client.HttpMessageHandlers;
using Elsa.Api.Client.Options;
using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;
using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;
using Elsa.Api.Client.Resources.ActivityDescriptors.Contracts;
using Elsa.Api.Client.Resources.ActivityExecutions.Contracts;
using Elsa.Api.Client.Resources.Features.Contracts;
@ -21,6 +20,7 @@ using Elsa.Api.Client.Services;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Polly;
using Refit;
namespace Elsa.Api.Client.Extensions;
@ -38,11 +38,9 @@ public static class DependencyInjectionExtensions
{
var builderOptions = new ElsaClientBuilderOptions();
configureBuilderOptions?.Invoke(builderOptions);
services.Configure(configureOptions ?? (_ => { }));
services.AddScoped<ApiHttpMessageHandler>();
services.AddScoped<IElsaClient, ElsaClient>();
services.Configure(configureOptions ?? (_ => { }));
services.AddScoped<IElsaClient, ElsaClient>();
services.AddApi<IWorkflowDefinitionsApi>(builderOptions);
services.AddApi<IWorkflowInstancesApi>(builderOptions);
services.AddApi<IActivityDescriptorsApi>(builderOptions);
@ -68,18 +66,22 @@ public static class DependencyInjectionExtensions
/// <typeparam name="T">The type representing the API.</typeparam>
public static void AddApi<T>(this IServiceCollection services, ElsaClientBuilderOptions? httpClientBuilderOptions = default) where T : class
{
var builder = services.AddRefitClient<T>(CreateRefitSettings).ConfigureHttpClient(ConfigureElsaApiHttpClient);
var builder = services.AddRefitClient<T>(CreateRefitSettings, typeof(T).Name).ConfigureHttpClient(ConfigureElsaApiHttpClient);
httpClientBuilderOptions?.ConfigureHttpClientBuilder?.Invoke(builder);
builder.AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))));
}
/// <summary>
/// Creates an API client for the specified API type.
/// </summary>
public static T CreateApi<T>(this IServiceProvider serviceProvider, Uri baseAddress) where T : class
{
return RestService.For<T>(baseAddress.ToString(), CreateRefitSettings(serviceProvider));
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
var httpClient = httpClientFactory.CreateClient(typeof(T).Name);
httpClient.BaseAddress = baseAddress;
return CreateApi<T>(serviceProvider, httpClient);
}
/// <summary>
/// Creates an API client for the specified API type.
/// </summary>
@ -105,18 +107,14 @@ public static class DependencyInjectionExtensions
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
var elsaClientOptions = serviceProvider.GetRequiredService<IOptions<ElsaClientOptions>>().Value;
serializerOptions.Converters.Add(new JsonStringEnumConverter());
serializerOptions.Converters.Add(new VersionOptionsJsonConverter());
var settings = new RefitSettings
{
ContentSerializer = new SystemTextJsonContentSerializer(serializerOptions),
HttpMessageHandlerFactory = () => elsaClientOptions.HttpMessageHandlerFactory(serviceProvider)
};
};
return settings;
}
}

View file

@ -1,19 +0,0 @@
using Elsa.Api.Client.Contracts;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Api.Client.HttpMessageHandlers;
/// <summary>
/// An HTTP message handler that delegates its processing to another <see cref="HttpMessageHandler"/> instance provided by an <see cref="IHttpMessageHandlerProvider"/>.
/// </summary>
public class ApiHttpMessageHandler : DelegatingHandler
{
/// <inheritdoc />
public ApiHttpMessageHandler(IServiceProvider serviceProvider)
{
var provider = serviceProvider.GetService<IHttpMessageHandlerProvider>();
if (provider != null)
InnerHandler = provider.GetHandler();
}
}

View file

@ -1,6 +1,3 @@
using Elsa.Api.Client.HttpMessageHandlers;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Api.Client.Options;
/// <summary>
@ -22,9 +19,4 @@ public class ElsaClientOptions
/// Gets or sets a delegate that can be used to configure the HTTP client.
/// </summary>
public Action<IServiceProvider, HttpClient>? ConfigureHttpClient { get; set; }
/// <summary>
/// Gets or sets a delegate that can be used to configure the HTTP client message handler.
/// </summary>
public Func<IServiceProvider, HttpMessageHandler> HttpMessageHandlerFactory { get; set; } = sp => sp.GetRequiredService<ApiHttpMessageHandler>();
}

View file

@ -1,19 +0,0 @@
using Elsa.Api.Client.Contracts;
using Elsa.Api.Client.Extensions;
using Elsa.Api.Client.Options;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Api.Client.Services;
/// <inheritdoc />
[PublicAPI]
public class ElsaClientFactory : IElsaClientFactory
{
/// <inheritdoc />
public IElsaClient CreateClient(Action<ElsaClientOptions> configureOptions, Action<ElsaClientBuilderOptions>? configureHttpClientBuilder = default)
{
var services = new ServiceCollection().AddElsaClient(configureOptions, configureHttpClientBuilder).BuildServiceProvider();
return services.GetRequiredService<IElsaClient>();
}
}