使用 AddHttpClient 时如何配置其他依赖项

Posted

技术标签:

【中文标题】使用 AddHttpClient 时如何配置其他依赖项【英文标题】:How to configure other dependencies when using AddHttpClient 【发布时间】:2021-02-05 20:01:09 【问题描述】:

根据 Microsoft 文档中的最佳实践,我正在使用 AddHttpClient 注入一个 HttpClient(使用 Typed clients),但这种方法不允许我配置我的服务的其余依赖项。

假设我有这个设置:

    public interface ISerializer  /*...*/ 

    public class Serializer : ISerializer  

        private readonly string _options;

        public Serializer(string options) 
        
            _options = options;
        
        
        /*...*/
    

    public class ServiceA
    
        private readonly HttpClient _client;
        private readonly ISerializer _serializer;

        public ServiceA(HttpClient httpClient, ISerializer serializer)
        
            _client = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
            _serializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
        

        public string MakeCall()
        
            // var body = _serializer.Serialize();
            return _client.GetAsync("").Result.StatusCode.ToString();
        
    

    public class ServiceB  
        /*
          Similar to ServiceA
        */ 
    

    public static class ServiceCollectionExtensions
    
        public static IServiceCollection AddServiceAandB(this IServiceCollection services)
                    
            /* 
               If I do this below, Serializer("option 1") will be used in Services A and B
               however, what I would like is to have different Serializer's configurations for A and B (see full question below) 
            */
            services.AddTransient<ISerializer>((p) => new Serializer("option 1"));
            
            services.AddHttpClient<ServiceA>(c =>
            
                c.BaseAddress = new Uri("api-url-for-A");
                c.Timeout = TimeSpan.FromSeconds(5);
            );

            services.AddHttpClient<ServiceB>(c =>
            
                c.BaseAddress = new Uri("api-url-for-B");
                c.Timeout = TimeSpan.FromSeconds(5);
            );

            return services;
        
    

问题:如何使用 Serializer("option 1") 为 ServiceA 和 Serializer("option 2") 为 ServiceB 配置 ISerializer 依赖关系?但是,因为我使用的是 AddHttpClient 并且它自己负责构建 ServiceA 和 ServiceB,所以我无法按照我的意愿进行配置。

注意:正如建议的那样,创建多个实现可能适用于简单的情况,但想象一下 Serializer 接收一个 Options 对象,并且为所有选项组合创建不同的实现是不可能的。这就是我想在 DI 设置期间配置选项的原因。

任何帮助将不胜感激。

-------- 更新 1 --------

我不打算注入两个 ISerializer,而是能够配置 Serializer 并在构造 ServiceA 和 ServiceB 的同时利用 .AddHttpClient。

例如(下面的代码是不可能的,但很理想):

services.AddHttpClient<ServiceA>(c =>

    c.BaseAddress = new Uri("api-url-for-A");
    c.Timeout = TimeSpan.FromSeconds(5);
);
services.AddTransient<ServiceA>(p => 
    return new ServiceA(p.GetHttpClientFor<ServiceA>(), new Serializer("option 1"));
);

services.AddHttpClient<ServiceB>(c =>

    c.BaseAddress = new Uri("api-url-for-B");
    c.Timeout = TimeSpan.FromSeconds(5);
);
services.AddTransient<ServiceB>(p => 
    return new ServiceB(p.GetHttpClientFor<ServiceB>(), new Serializer("option 2"));
);

// neither IServiceProvider.GetHttpClient method exists, nor I can use .AddHttpClient with AddTransient for same dependency.

【问题讨论】:

ISerializer引入不同的实现类型,让不同的服务可以使用不同的选项。 这行不通。您不能向管道中注入多个 ISerializer。否则框架如何知道将哪个注入到需要它的服务中。 @Fabio 我的例子就是一个例子,但想象一下 Serializer 接收不同的序列化选项(缩进、不缩进、使用驼峰式大小写、使用偷偷摸摸的大小写),创建多个实现将是致命的。 @Andy,我添加了更新 1 以澄清您的观点。 【参考方案1】:

如果序列化器有多个选项,请引入一个类,该类将基于所需的选项构建新的序列化器。

public class SerializerBuilder

    private string _option;

    public SerializerBuilder With(string option)
    
        _option = option;
    

    public ISerializer Create()
    
        return new Serializer(_option);
    


public class ServiceA

    public ServiceA(HttpClient client, SerializerBuilder builder)
    
        _serializer = builder.With("option A").Create();
    

【讨论】:

好的,我喜欢这个。它需要是瞬态的才能线程安全,但是很好,谢谢 如果您可以将所有必需的选项传递给 Create 方法,则可以将其设为单例,那么它将比构建器更“工厂”;) 这是迄今为止最好的选择,但是,它带来了构造问题和与 ServiceA 的耦合,这是我试图避免的(请记住,我将 ISerializer 作为依赖项)

以上是关于使用 AddHttpClient 时如何配置其他依赖项的主要内容,如果未能解决你的问题,请参考以下文章

若依如何区分不同环境下配置文件?

.net services.AddHttpClient 自动访问令牌处理

如何看若依管理后台的版本号

services.AddHttpClient

对 AddHttpClient 的重复调用会相互覆盖吗?

若依框架详细使用