这是使用 HttpClient 的错误模式吗?
Is this a bad pattern for using HttpClient?
我在 .NET Core 中有一个利用第三方 api 的 Web 应用程序,我想知道 'better' 模式是否可以将所有这些类型化的 http 客户端服务封装到然后将单个服务注入到每个服务中,而不是将新客户端注入到它们中。从我在 HttpClient 上读到的内容来看,最佳用法似乎是让一个 HttpClient 实例用于整个应用程序。它们都针对相同的碱基 api 但被不同的 endpoints/features.
分开
我的 Startup class 中有一些代码是这样的
var _thirdPartyAppKey = Configuration["ThirdPartyConfig:ThirdPartyAppKey"];
services.AddHttpClient<IAuthenticationService, AuthenticationService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddHttpClient<ICustomerService, CustomerService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddHttpClient<ITransactionService, TransactionService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddHttpClient<IConsumerService, ConsumerService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
我正在考虑重构为这样的东西:
services.AddHttpClient<IThirdPartyClientService, ThirdPartyClientService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddScoped<IAuthenticationService, AuthenticationService>();
services.AddScoped<ICustomerService, CustomerService>();
... the rest
然后如果我的 ThirdPartyClientService class 就是这样:
public class ThirdPartyClientService {
public HttpClient _httpClient;
public ThirdPartyClientService(HttpClient httpClient) {
_httpClient = httpClient;
}
}
我可以将它注入到我的其他服务中,然后像这样使用它:
_thirPartyClientService._httpClient.PostAsync() etc..
如果您所做的只是在类型化客户端 class 中公开公开底层 HttpClient 并直接调用其 PostAsync()
方法,并且所有不同版本共享完全相同的设置,那么您无论如何,我们并没有真正从类型化的客户端中获得任何价值;该类型化客户端的很多价值在于在位于下方的 HttpClient 顶部提供显式抽象,例如使用 DoSomeTransactionServiceSpecificOperation()
而不是将 HttpClient 暴露给消费者。此外,每个类型化的客户端都在 HttpClientFactory 的处理程序池中创建一个不同的命名基处理程序,如果该基处理程序本质上是相同的,则可以在所有这些处理程序中重复使用一个处理程序。
就是说,如果您确实开始为每个不同的接口利用 HttpClient 顶部的抽象方法,您可以:
1) 如果有合理的预期他们的传入参数和方法将是唯一的,则保留单独的客户,从而让他们继续承担单一职责。
或
2) 保留单独的接口,但仍然只有一个涵盖所有接口的具体实现,即 public class ThirdPartyClient : IAuthenticationService, ITransactionService ...
并在启动时将单一类型的客户端注册到每个接口。这样,当客户端通过一个接口注入某处时,它的范围将仅限于该接口的方法,但您可以继续在单个实现中管理共享代码,直到这样做不再有意义为止,并且作为奖金,底层处理程序将在池中共享。
我在 .NET Core 中有一个利用第三方 api 的 Web 应用程序,我想知道 'better' 模式是否可以将所有这些类型化的 http 客户端服务封装到然后将单个服务注入到每个服务中,而不是将新客户端注入到它们中。从我在 HttpClient 上读到的内容来看,最佳用法似乎是让一个 HttpClient 实例用于整个应用程序。它们都针对相同的碱基 api 但被不同的 endpoints/features.
分开我的 Startup class 中有一些代码是这样的
var _thirdPartyAppKey = Configuration["ThirdPartyConfig:ThirdPartyAppKey"];
services.AddHttpClient<IAuthenticationService, AuthenticationService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddHttpClient<ICustomerService, CustomerService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddHttpClient<ITransactionService, TransactionService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddHttpClient<IConsumerService, ConsumerService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
我正在考虑重构为这样的东西:
services.AddHttpClient<IThirdPartyClientService, ThirdPartyClientService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddScoped<IAuthenticationService, AuthenticationService>();
services.AddScoped<ICustomerService, CustomerService>();
... the rest
然后如果我的 ThirdPartyClientService class 就是这样:
public class ThirdPartyClientService {
public HttpClient _httpClient;
public ThirdPartyClientService(HttpClient httpClient) {
_httpClient = httpClient;
}
}
我可以将它注入到我的其他服务中,然后像这样使用它:
_thirPartyClientService._httpClient.PostAsync() etc..
如果您所做的只是在类型化客户端 class 中公开公开底层 HttpClient 并直接调用其 PostAsync()
方法,并且所有不同版本共享完全相同的设置,那么您无论如何,我们并没有真正从类型化的客户端中获得任何价值;该类型化客户端的很多价值在于在位于下方的 HttpClient 顶部提供显式抽象,例如使用 DoSomeTransactionServiceSpecificOperation()
而不是将 HttpClient 暴露给消费者。此外,每个类型化的客户端都在 HttpClientFactory 的处理程序池中创建一个不同的命名基处理程序,如果该基处理程序本质上是相同的,则可以在所有这些处理程序中重复使用一个处理程序。
就是说,如果您确实开始为每个不同的接口利用 HttpClient 顶部的抽象方法,您可以:
1) 如果有合理的预期他们的传入参数和方法将是唯一的,则保留单独的客户,从而让他们继续承担单一职责。
或
2) 保留单独的接口,但仍然只有一个涵盖所有接口的具体实现,即 public class ThirdPartyClient : IAuthenticationService, ITransactionService ...
并在启动时将单一类型的客户端注册到每个接口。这样,当客户端通过一个接口注入某处时,它的范围将仅限于该接口的方法,但您可以继续在单个实现中管理共享代码,直到这样做不再有意义为止,并且作为奖金,底层处理程序将在池中共享。