在 .NET Core 2 中注册基于作用域的 HttpClient



I have NET Core 2 Web API application.在此过程中,我必须调用客户端 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");
}
}

然后在服务中使用客户端 A

public class MyService :IMyService
{
private readonly IClientA _clientA
public MyService(IClientA clientA)
{
_clientA= clientA
}
public void DoSomethig()
{
_clientA.GetData();
}           
}

然后我在启动中注册所有内容.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.Net.Http.HttpClient" 'XXXXXXXXX.ClientA'。 在 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实例并将其传递给 ClientA 类,因为我必须设置 userid 和密码。

为了摆脱上述错误,我可以在DI框架中注册带有UserID和密码的HttpClient,我想这会起作用。

但是,在这种情况下,如果有另一个客户端 ClientB,它采用 HttpClient,那么 DI 框架将注入具有用户标识和密码的同一 httpclient。 这将产生安全问题,因为客户端 B 会在请求标头中看到客户端 A 的凭据。

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

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

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

这将 a) 扩展良好 b) 每个客户每个应用程序实例/启动仅运行一次,以及 c) 强制访问检查使用情况。

此外,此答案与您的标头要求相关 - 具有不同身份验证标头的 HttpClient 单个实例

解决了我的问题

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

最新更新