这是使用 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 ... 并在启动时将单一类型的客户端注册到每个接口。这样,当客户端通过一个接口注入某处时,它的范围将仅限于该接口的方法,但您可以继续在单个实现中管理共享代码,直到这样做不再有意义为止,并且作为奖金,底层处理程序将在池中共享。