在 .NET Core 2 中注册基于范围的 HttpClient

Register scoped based HttpClient in .NET Core 2

我有 NET Core 2 Web API 应用程序。在此过程中,我必须调用客户端 A 的 API 来获取一些数据。所以我正在使用 HttpClient 来调用它。客户端 A 还要求我在 header.

中传递用户名和密码

因此,我没有直接注入 HttpClient,而是围绕 HttpClient 进行了包装,如下所示

public class ClientA : IClientA
{
    private readonly HttpClient _httpClient;

    public ClientA(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<string> GetData()
    {
        return await _httpClient.HttpGetAsync("someurl");
    }
 }

然后在Service中使用ClientA

  public class MyService :IMyService
  {
      private readonly IClientA _clientA

      public MyService(IClientA clientA)
      {
           _clientA= clientA
      }

      public void DoSomethig()
      {
          _clientA.GetData();
      }           
  }

然后我在Startup.cs

中注册所有内容
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IMyService, MyService>();           

        services.AddScoped(factory =>
        {
            Func<Task<IClientA>> provider = async () =>
            {
                using (var dbContext = factory.GetService<MyDBContext>())
                {
                    // get userid and password from database here

                    var httpClient = new HttpClient();
                    httpClient.DefaultRequestHeaders.Add("UserId",userid);
                    httpClient.DefaultRequestHeaders.Add("Password",password);
                    return new ClientA(httpClient);
                }
            };

            return provider;
        });
    }

但是我收到错误

System.InvalidOperationException: Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate 'XXXXXXXXX.ClientA'. at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, ISet1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, ISet1 callSiteChain)

为简洁起见删除了剩余的异常

请注意,在注册过程中,我是 HttpClient 的 newing-up 实例并将其传递给 ClientA class,因为我必须设置用户 ID 和密码。

为了消除上述错误,我可以使用 DI 框架使用用户 ID 和密码注册 HttpClient,我想这会起作用。

但是,在这种情况下,如果有另一个客户端 ClientB 采用 HttpClient,那么 DI 框架将注入具有用户 ID 和密码的相同 httpclient。这会造成安全问题,因为 ClientB 会在请求 headers.

中看到 ClientA 的凭据
   public class ClientB(HttpClient client)
   {
        private readonly _httpClient;
        public class ClientB(HttpClient client)
        {
           _httpClient = client;
        }

        public string CallClientB(string url)
        {
            // here ClientB will receive ClientA's credentials
            return await _httpClient.HttpGetAsync(url);
        }
   }

解决了我的问题

   services.AddScoped<IClientA>(factory =>
    {            
            var dbContext = factory.GetService<MyDBContext>();                
                // get userid and password from database here

                var httpClient = new HttpClient();
                httpClient.DefaultRequestHeaders.Add("UserId",userid);
                httpClient.DefaultRequestHeaders.Add("Password",password);
                return new ClientA(httpClient);
    });

您不希望在范围上下文中实例化 httpclient,即为每个请求创建一个 httpclient 实例,这不是 class 的推荐使用模式。 (不会很好地扩展)。 https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

为每个客户创建一个单独接口的单例(假设客户数量很少)- 可能在其实现中有代码访问安全需求,具体取决于您的设置(启用身份模拟?)

这将 a) 很好地扩展 b) 每个客户每个应用程序仅 运行 一次 instance/startup 和 c) 对使用情况强制执行访问检查。

此外,此答案与您的 header 要求相关 - HttpClient single instance with different authentication headers