在 .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, ISet
1 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
我有 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, ISet
1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, ISet
1 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