使用特定的 HttpMessageHandler 注入单个实例 HttpClient [英] Injecting Single Instance HttpClient with specific HttpMessageHandler

查看:37
本文介绍了使用特定的 HttpMessageHandler 注入单个实例 HttpClient的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为我正在处理的 ASP.Net Core 项目的一部分,我需要从我的 WebApi 中与许多不同的基于 Rest 的 API 端点进行通信.为了实现这一点,我使用了许多服务类,每个类都实例化一个静态 HttpClient.基本上,我为 WebApi 连接到的每个基于 Rest 的端点都有一个服务类.

As part of an ASP.Net Core project that I am working on I have a requirement to communicate with a number of different Rest based API Endpoints from within my WebApi. To achieve this I am using a number of service classes that each instantiate a static HttpClient. Essentially I have a service class for each of the Rest based endpoints that the WebApi connects to.

下面是如何在每个服务类中实例化静态 HttpClient 的示例.

An example of how the static HttpClient is instantiated in each of the service classes can be seen below.

private static HttpClient _client = new HttpClient()
{
    BaseAddress = new Uri("http://endpointurlexample"),            
};

虽然上述方法运行良好,但它无法对使用 HttpClient 的服务类进行有效的单元测试.为了让我能够进行单元测试,我有一个假的 HttpMessageHandler,我想在我的单元测试中用于 HttpClient,而 HttpClient> 已按上述方式实例化,但是我无法将伪造的 HttpMessageHandler 作为我的单元测试的一部分.

Whilst the above is working well, it does not allow for effective unit testing of the service classes that are using HttpClient. To enable me to carry out unit testing I have a fake HttpMessageHandler that I would like to use for the HttpClient in my unit tests, whilst the HttpClient is instantiated as above however I am unable to apply the fake HttpMessageHandler as part of my unit tests.

服务类中的 HttpClient 在整个应用程序中保持单个实例的最佳方式是什么(每个端点一个实例),但允许不同的 HttpMessageHandler在单元测试期间应用?

What is the best way for the HttpClient in the service classes to remain a single instance throughout the application (one instance per endpoint), but to allow a different HttpMessageHandler to be applied during the unit tests?

我想到的一种方法是不使用静态字段将 HttpClient 保存在服务类中,而是允许使用单例生命周期通过构造函数注入来注入它,这将允许我在单元测试期间使用所需的 HttpMessageHandler 指定 HttpClient,我想到的另一个选项是使用 HttpClient 工厂类在静态字段中实例化 HttpClient ,然后可以通过将 HttpClient 工厂注入服务类来检索这些静态字段,再次允许使用相关的 HttpMessageHandler 在单元测试中返回.然而,以上都没有感觉特别干净,感觉必须有更好的方法吗?

One approach I have thought of would be not to use a static field to hold the HttpClient in the service classes, rather to allow it to be injected via constructor injection using a singleton lifecycle, which would allow me to specify a HttpClient with the desired HttpMessageHandler during unit tests, the other option I thought of would be to use a HttpClient Factory Class that instantiated the HttpClients in static fields that could then be retrieved by injecting the HttpClient factory into the service classes, again allowing a different implementation with the relevant HttpMessageHandler to be returned in unit tests. None of the above feel particularly clean however and it feels like there must be a better way?

有任何问题,请告诉我.

Any questions, let me know.

推荐答案

从评论中添加到对话中看起来您需要一个 HttpClient 工厂

Adding to the conversation from the comments looks like you would need a HttpClient factory

public interface IHttpClientFactory {
    HttpClient Create(string endpoint);
}

核心功能的实现可能看起来像这样.

and the implementation of the core functionality could look something like this.

public class DefaultHttpClientFactory : IHttpClientFactory, IDisposable
{
    private readonly ConcurrentDictionary<string, HttpClient> _httpClients;

    public DefaultHttpClientFactory()
    {
        this._httpClients = new ConcurrentDictionary<string, HttpClient>();
    }

    public HttpClient Create(string endpoint)
    {
        if (this._httpClients.TryGetValue(endpoint, out var client))
        {
            return client;
        }

        client = new HttpClient
        {
            BaseAddress = new Uri(endpoint),
        };

        this._httpClients.TryAdd(endpoint, client);

        return client;
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        foreach (var httpClient in this._httpClients)
        {
            httpClient.Value.Dispose();
        }
    }
}

也就是说,如果您对上述设计不是特别满意.您可以抽象出服务背后的 HttpClient 依赖项,这样客户端就不会成为实现细节.

That said, if you are not particularly happy with the above design. You could abstract away the HttpClient dependency behind a service so that the client does not become an implementation detail.

服务的消费者不需要确切知道数据是如何检索的.

That consumers of the service need not know exactly how the data is retrieved.

这篇关于使用特定的 HttpMessageHandler 注入单个实例 HttpClient的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆