在类库项目的基类中定义 HttpClient 实例的最佳方法是啥?
Posted
技术标签:
【中文标题】在类库项目的基类中定义 HttpClient 实例的最佳方法是啥?【英文标题】:What is the best approach to define an HttpClient instance in a base class of a class library project?在类库项目的基类中定义 HttpClient 实例的最佳方法是什么? 【发布时间】:2019-07-31 12:29:54 【问题描述】:有一个应用程序有 3 个接口,想要使用这个应用程序的人需要实现这些接口。我创建了一个类库项目,它具有这些接口实现,我从同一个基类继承了所有这些接口实现,以便能够拥有一个 HttpClient。这是我到目前为止所做的:
public class BaseProxy
protected static readonly HttpClient Client;
static BaseProxy()
Client = new HttpClient();
我在所有派生类中都使用了这个客户端来发出 GetAsync 和 PostAsync 请求,如下所示:
public class XProxyImplementation
var response = Client.GetAsync(BaseUrl + "XXXApi/GetClientSettings/").Result;
response.EnsureSuccessStatusCode();
顺便说一句,Web API 中的方法都不是异步的,我选择了单例解决方案,因为我不想对每个请求都使用 using 块。我的问题是我应该选择 DI 解决方案还是该代码足以用于内部使用的应用程序?欢迎提出任何改进建议。
我已经阅读了很多关于使用 DI 容器的答案,但这只是一个带有代理实现的类库。
我的另一个问题是即使我想使用 DI,目前我无法在我的构造函数类中引入 DI,因为使用我的实现的其他应用程序正在寻找一个空的构造函数。当我尝试将 HttpClient 参数传递给构造函数时,出现以下错误:
当前类型 System.Net.Http.HttpMessageHandler 是一个抽象 类,无法构造
使用我的 dll 的应用程序不允许我将任何参数传递给使用任何抽象类的构造函数。我猜这个应用程序使用 Unity 进行握手,并以某种方式寻找一个空的构造函数。一旦我尝试进行以下更改,我就会收到错误消息:
public BaseProxy() : this(Service.HttpClient)
public XProxyImplementation(HttpClient client) : base(client)
这就是为什么我实际上更喜欢单例实例而不是 DI 实现。
【问题讨论】:
【参考方案1】:DI?是的
DI 将使您的代理类具有可测试性,而您当前的实现无法进行单元测试。它还将改进关注点分离:remove the responsibility 控制来自代理的 HttpClient
生命周期。
通常,您会这样做:
public abstract class BaseProxy
protected readonly HttpClient Client;
protected BaseProxy(HttpClient client)
Client = client;
// ... other members
public class XProxyImplementation : BaseProxy
public XProxyImplementation(HttpClient client) : base(client)
// ... other members
public Task SendRequest() // for example
return Client.GetAsync("....");
在测试期间,您将初始化HttpClient
的不同实例,注入HttpMessageHandler
的测试友好实现:
// you implement TestHttpMessageHandler that aids your tests
var httpClient = new HttpClient(new TestHttpMessageHandler());
var proxyUnderTest = new XProxyImplementation(httpClient);
请参阅 this blog post 以了解使用 HttpClient
和 HttpMessageHandler
进行单元测试的说明。
DI 容器?没有
既然我们在代码中引入了依赖注入,接下来的问题是,应该使用什么注入机制。
在您的具体情况下,我会投票反对耦合到任何特定的 DI 容器,因为您希望您的库被许多不同的应用程序使用,并且您不想膨胀它们的依赖项(应用程序可能已经在使用不同的 DI 容器)。
此外,由于您发布的代码非常简单,所以一个成熟的 DI 容器将是一个矫枉过正。在生产代码中,您可以将您的单例 HttpClient
移动到“服务定位器”:
public static class SingletonServices
public static readonly HttpClient HttpClient;
static SingletonServices()
HttpClient = new HttpClient();
因此,当您在生产代码中实例化代理时,您可以这样做:
var proxy = new XProxyImplementation(SingletonServices.HttpClient);
【讨论】:
我猜我不太清楚,所以我刚刚更新了我的问题。我已经尝试将参数传递给构造函数,但是使用我的 dll 的应用程序阻止我传递 HttpClient 对象,因为它在作为抽象类的构造函数之一上具有 HttpMessageHandler 参数。我猜该项目使用 Unity 进行握手,它只是寻找一个空的构造函数【参考方案2】:我肯定会使用 Microsoft.Extensions.DependencyInjection 包为此提供 DI 解决方案。 https://dzone.com/articles/dependency-injection-in-net-core-console-applicati
您还应该非常清楚如何使用 GetAsync 等异步方法。 使用 .Result 几乎永远不会得到想要的结果,最好使方法异步并使用 await 关键字,如下所示:
var response = await Client.GetAsync(BaseUrl + "XXXApi/GetClientSettings/");
https://montemagno.com/c-sharp-developers-stop-calling-dot-result/
是一个很好的资源,用于了解此最佳实践的原因和方法
【讨论】:
【参考方案3】:DI 就是答案。如果您不想使用 ID,则可以实现一个 HttpClientFactory。 你可以在这里阅读更多 https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
【讨论】:
以上是关于在类库项目的基类中定义 HttpClient 实例的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章