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

时间:2018-03-05 22:45:12

标签: asp.net-core .net-core asp.net-core-2.0 asp.net-core-webapi coreclr

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

所以我没有直接注入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");
    }
 }

然后在服务中使用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:无法解析类型的服务   尝试激活时'System.Net.Http.HttpClient'   'XXXXXXXXX.ClientA'。在   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(类型   serviceType,Type implementationType,ISet 1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, ISet 1 callSiteChain)

为简洁起见,

删除了剩余的异常

请注意,在注册期间,我是HttpClient的新实例并将其传递给ClientA类,因为我必须设置用户ID和密码。

为了消除上述错误,我可以使用DI框架注册带有UserID和密码的HttpClient,我猜这样可行。

但是,在这种情况下,如果有另一个客户端ClientB,它接受HttpClient,那么DI框架将注入具有用户ID和密码的相同httpclient。这将产生安全问题,因为ClientB会在请求标头中看到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);
        }
   }

2 个答案:

答案 0 :(得分:3)

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

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

这将a)规模很好b)每个应用程序实例/启动每个客户只运行一次,c)强制执行访问检查。

此外,此答案已与您的标题要求相关联且相关 - HttpClient single instance with different authentication headers

答案 1 :(得分:-1)

解决了我的问题

   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);
    });