如何将 RateLimiter 的 HttpClient DelegatingHandler 与依赖注入一起使用?
How can I use RateLimiter's HttpClient DelegatingHandler with dependency injection?
我已经在我的项目中成功使用 RateLimiter (github) 一段时间了。我最近发现了依赖注入,并试图按原样迁移我的代码以使用它,但我被困在 RateLimiter 上。
文档中的正常用法是
var handler = TimeLimiter
.GetFromMaxCountByInterval(25, TimeSpan.FromMinutes(1))
.AsDelegatingHandler();
var Client = new HttpClient(handler)
但是,如果我尝试在依赖注入期间复制它
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient<IMyApiClient, MyApiClient>(client => client.BaseAddress = new Uri("https://api.myapi.com/"))
.AddPolicyHandler(GetRetryPolicy())
.AddHttpMessageHandler(() => TimeLimiter.GetFromMaxCountByInterval(25, TimeSpan.FromMinutes(1)).AsDelegatingHandler());
}
我收到错误:
[2021-05-17T21:58:00.116Z] Microsoft.Extensions.Http: The 'InnerHandler' property must be null. 'DelegatingHandler' instances provided to 'HttpMessageHandlerBuilder' must not be reused or cached.
[2021-05-17T21:58:00.117Z] Handler: 'ComposableAsync.DispatcherDelegatingHandler'.
[2021-05-17T21:58:00.122Z] An unhandled host error has occurred.
我的 class 客户端结构(主要是为了单元测试)如下所示:
using System.Net.Http;
using System.Threading.Tasks;
public class MyApiClient : HumbleHttpClient, IMyApiClient
{
public MyApiClient(HttpClient client)
: base(client)
{
}
}
public class HumbleHttpClient : IHttpClient
{
public HumbleHttpClient(HttpClient httpClient)
{
this.Client = httpClient;
}
public HttpClient Client { get; }
public Task<HttpResponseMessage> GetAsync(string requestUri)
{
return this.Client.GetAsync(requestUri);
}
public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content)
{
return this.Client.PostAsync(requestUri, content);
}
}
public interface IHttpClient
{
Task<HttpResponseMessage> GetAsync(string requestUri);
Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content);
HttpClient Client { get; }
}
我不知道这是错误还是功能,但要解决此问题,我认为您还需要将委托处理程序添加为 Transient。
因此,在典型情况下,您会这样设置 DelegatingHandler
:
public sealed class MyDelegatingHandler : DelegatingHandler
{
// ...
}
那你需要注册一下;但这就是问题所在……您还需要将处理程序注册为 Transient:
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient<IMyHttpService, MyHttpService>()
.AddHttpMessageHandler<MyDelegatingHandler>();
// this needs to be added:
builder.Services.AddTransient<MyDelegatingHandler>();
}
话虽如此,您处于一种独特的情况,因为您要使用的处理程序是在运行时从第 3 方创建的。你可以让这个工作...如果它是正确的,不是 100%,但它应该工作:
public override void Configure(IFunctionsHostBuilder builder)
{
var handler = TimeLimiter
.GetFromMaxCountByInterval(25, TimeSpan.FromMinutes(1))
.AsDelegatingHandler();
builder.Services.AddHttpClient<IMyApiClient, MyApiClient>(
client => client.BaseAddress = new Uri("https://api.myapi.com/"))
.AddPolicyHandler(GetRetryPolicy())
.AddHttpMessageHandler(() => handler);
builder.Services.AddTransient(_ => handler);
}
编辑添加...
您看到的问题与 ComposableAsync
Nuget 包有关。具体来说 this line。代码 不应 设置此 属性。 ASP.NET核心should/will去做吧。幸运的是,这只是一个简单的扩展方法。
让我们以正确的方式编写自己的扩展程序:
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace YourNamespace
{
public static class DispatcherExtension
{
private sealed class DispatcherDelegatingHandler : DelegatingHandler
{
private readonly ComposableAsync.IDispatcher _Dispatcher;
public DispatcherDelegatingHandler(ComposableAsync.IDispatcher dispatcher)
{
_Dispatcher = dispatcher;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
return _Dispatcher.Enqueue(() =>
base.SendAsync(request, cancellationToken), cancellationToken);
}
}
public static DelegatingHandler AsDelegatingHandler(
this ComposableAsync.IDispatcher dispatcher)
{
return new DispatcherDelegatingHandler(dispatcher);
}
}
}
确保通过注册处理程序将您的代码更改为我上面的代码。另外,确保它使用的是 new 扩展而不是库中的扩展。
我测试了它,你可以看到它设置正确:
我也会考虑在 ComposableAsync github 页面中打开一个错误。
我已经在我的项目中成功使用 RateLimiter (github) 一段时间了。我最近发现了依赖注入,并试图按原样迁移我的代码以使用它,但我被困在 RateLimiter 上。
文档中的正常用法是
var handler = TimeLimiter
.GetFromMaxCountByInterval(25, TimeSpan.FromMinutes(1))
.AsDelegatingHandler();
var Client = new HttpClient(handler)
但是,如果我尝试在依赖注入期间复制它
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient<IMyApiClient, MyApiClient>(client => client.BaseAddress = new Uri("https://api.myapi.com/"))
.AddPolicyHandler(GetRetryPolicy())
.AddHttpMessageHandler(() => TimeLimiter.GetFromMaxCountByInterval(25, TimeSpan.FromMinutes(1)).AsDelegatingHandler());
}
我收到错误:
[2021-05-17T21:58:00.116Z] Microsoft.Extensions.Http: The 'InnerHandler' property must be null. 'DelegatingHandler' instances provided to 'HttpMessageHandlerBuilder' must not be reused or cached.
[2021-05-17T21:58:00.117Z] Handler: 'ComposableAsync.DispatcherDelegatingHandler'.
[2021-05-17T21:58:00.122Z] An unhandled host error has occurred.
我的 class 客户端结构(主要是为了单元测试)如下所示:
using System.Net.Http;
using System.Threading.Tasks;
public class MyApiClient : HumbleHttpClient, IMyApiClient
{
public MyApiClient(HttpClient client)
: base(client)
{
}
}
public class HumbleHttpClient : IHttpClient
{
public HumbleHttpClient(HttpClient httpClient)
{
this.Client = httpClient;
}
public HttpClient Client { get; }
public Task<HttpResponseMessage> GetAsync(string requestUri)
{
return this.Client.GetAsync(requestUri);
}
public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content)
{
return this.Client.PostAsync(requestUri, content);
}
}
public interface IHttpClient
{
Task<HttpResponseMessage> GetAsync(string requestUri);
Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content);
HttpClient Client { get; }
}
我不知道这是错误还是功能,但要解决此问题,我认为您还需要将委托处理程序添加为 Transient。
因此,在典型情况下,您会这样设置 DelegatingHandler
:
public sealed class MyDelegatingHandler : DelegatingHandler
{
// ...
}
那你需要注册一下;但这就是问题所在……您还需要将处理程序注册为 Transient:
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient<IMyHttpService, MyHttpService>()
.AddHttpMessageHandler<MyDelegatingHandler>();
// this needs to be added:
builder.Services.AddTransient<MyDelegatingHandler>();
}
话虽如此,您处于一种独特的情况,因为您要使用的处理程序是在运行时从第 3 方创建的。你可以让这个工作...如果它是正确的,不是 100%,但它应该工作:
public override void Configure(IFunctionsHostBuilder builder)
{
var handler = TimeLimiter
.GetFromMaxCountByInterval(25, TimeSpan.FromMinutes(1))
.AsDelegatingHandler();
builder.Services.AddHttpClient<IMyApiClient, MyApiClient>(
client => client.BaseAddress = new Uri("https://api.myapi.com/"))
.AddPolicyHandler(GetRetryPolicy())
.AddHttpMessageHandler(() => handler);
builder.Services.AddTransient(_ => handler);
}
编辑添加...
您看到的问题与 ComposableAsync
Nuget 包有关。具体来说 this line。代码 不应 设置此 属性。 ASP.NET核心should/will去做吧。幸运的是,这只是一个简单的扩展方法。
让我们以正确的方式编写自己的扩展程序:
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace YourNamespace
{
public static class DispatcherExtension
{
private sealed class DispatcherDelegatingHandler : DelegatingHandler
{
private readonly ComposableAsync.IDispatcher _Dispatcher;
public DispatcherDelegatingHandler(ComposableAsync.IDispatcher dispatcher)
{
_Dispatcher = dispatcher;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
return _Dispatcher.Enqueue(() =>
base.SendAsync(request, cancellationToken), cancellationToken);
}
}
public static DelegatingHandler AsDelegatingHandler(
this ComposableAsync.IDispatcher dispatcher)
{
return new DispatcherDelegatingHandler(dispatcher);
}
}
}
确保通过注册处理程序将您的代码更改为我上面的代码。另外,确保它使用的是 new 扩展而不是库中的扩展。
我测试了它,你可以看到它设置正确:
我也会考虑在 ComposableAsync github 页面中打开一个错误。