注入的 HttpClient 忽略 IHttpClientFactory 配置
Injected HttpClient ignores IHttpClientFactory configuration
我创建了一个自定义库,它会自动为依赖于 HttpClient
的特定服务设置 Polly 策略。
这是使用 IServiceCollection
扩展方法和类型化客户端方法完成的。一个简化的例子:
public static IHttpClientBuilder SetUpFooServiceHttpClient(this IServiceCollection services)
{
return services
.AddHttpClient<FooService>()
.AddPolicyHandler(GetRetryPolicy());
}
示例服务:
public class FooService
{
private readonly HttpClient _client;
// OPTION 1
public FooService(HttpClient httpClient)
{
_client = httpClient;
}
// OPTION 2
public FooService(IHttpClientFactory httpClientFactory)
{
_client = httpClientFactory.CreateClient(GetType().Name);
}
public void DoJob()
{
var test = _client.GetAsync("http://example.com");
}
}
正在从 DI 容器中获取服务(这是来自测试项目):
var services = new ServiceCollection();
services.SetUpFooServiceHttpClient();
services.AddSingleton<FooService>();
var fooService = services
.BuildServiceProvider()
.GetRequiredService<FooService>();
// Perform test
fooService.DoJob();
注意:在这个测试项目中,我还添加了一个额外的模拟处理程序,因为我试图模拟 http 状态响应,但模拟处理程序是否存在与 Polly 策略相同存在与否,所以我从示例代码中省略了模拟处理程序。
请注意 FooService
中的两个不同的构造函数。根据我注释掉哪一个和保留哪一个,我会得到不同的结果。所有其他代码保持不变。
- 选项 1,直接注入
HttpClient
,忽略我所有的配置。我得到一个没有 Polly 策略处理程序的标准 http 客户端。
- 选项 2,注入
IHttpClientFactory
并使用当前类型名称(即 FooService
)请求客户端遵守我的配置。我得到一个自定义的 http 客户端,其中包含 Polly 策略处理程序(以及我可能配置的任何其他处理程序,例如我的测试套件中的模拟处理程序)
策略处理程序的 absence/existence 在两种情况下都使用调试检查得到确认。
根据我在该主题上找到的所有文档,这两个选项应该是等效的,至少就我最终获得的构造 HttpClient
而言是这样。但这里不是这样。
我找到的文档指定 HttpClient
可以在使用 typed 客户端时注入:
- MSDN documentation,特别是“typed clients”部分示例。
- 这个
- 这个blog post
我正在使用类型化客户端,但注入 HttpClient
显然对我不起作用。
为什么在我的情况下注入 HttpClient
与注入 IHttpClientFactory
的工作方式不同?
实际上,您的 FooService
class 有以下两个注册:
services.AddHttpClient<FooService>()
services.AddSingleton<FooService>();
由于 DI 容器在幕后的工作方式,第二次注册会覆盖第一次。如果删除第二个注册,将使用第一个,因此将调用带有 HttpClient
参数的构造函数。
我创建了一个自定义库,它会自动为依赖于 HttpClient
的特定服务设置 Polly 策略。
这是使用 IServiceCollection
扩展方法和类型化客户端方法完成的。一个简化的例子:
public static IHttpClientBuilder SetUpFooServiceHttpClient(this IServiceCollection services)
{
return services
.AddHttpClient<FooService>()
.AddPolicyHandler(GetRetryPolicy());
}
示例服务:
public class FooService
{
private readonly HttpClient _client;
// OPTION 1
public FooService(HttpClient httpClient)
{
_client = httpClient;
}
// OPTION 2
public FooService(IHttpClientFactory httpClientFactory)
{
_client = httpClientFactory.CreateClient(GetType().Name);
}
public void DoJob()
{
var test = _client.GetAsync("http://example.com");
}
}
正在从 DI 容器中获取服务(这是来自测试项目):
var services = new ServiceCollection();
services.SetUpFooServiceHttpClient();
services.AddSingleton<FooService>();
var fooService = services
.BuildServiceProvider()
.GetRequiredService<FooService>();
// Perform test
fooService.DoJob();
注意:在这个测试项目中,我还添加了一个额外的模拟处理程序,因为我试图模拟 http 状态响应,但模拟处理程序是否存在与 Polly 策略相同存在与否,所以我从示例代码中省略了模拟处理程序。
请注意 FooService
中的两个不同的构造函数。根据我注释掉哪一个和保留哪一个,我会得到不同的结果。所有其他代码保持不变。
- 选项 1,直接注入
HttpClient
,忽略我所有的配置。我得到一个没有 Polly 策略处理程序的标准 http 客户端。 - 选项 2,注入
IHttpClientFactory
并使用当前类型名称(即FooService
)请求客户端遵守我的配置。我得到一个自定义的 http 客户端,其中包含 Polly 策略处理程序(以及我可能配置的任何其他处理程序,例如我的测试套件中的模拟处理程序)
策略处理程序的 absence/existence 在两种情况下都使用调试检查得到确认。
根据我在该主题上找到的所有文档,这两个选项应该是等效的,至少就我最终获得的构造 HttpClient
而言是这样。但这里不是这样。
我找到的文档指定 HttpClient
可以在使用 typed 客户端时注入:
- MSDN documentation,特别是“typed clients”部分示例。
- 这个
- 这个blog post
我正在使用类型化客户端,但注入 HttpClient
显然对我不起作用。
为什么在我的情况下注入 HttpClient
与注入 IHttpClientFactory
的工作方式不同?
实际上,您的 FooService
class 有以下两个注册:
services.AddHttpClient<FooService>()
services.AddSingleton<FooService>();
由于 DI 容器在幕后的工作方式,第二次注册会覆盖第一次。如果删除第二个注册,将使用第一个,因此将调用带有 HttpClient
参数的构造函数。