我正在尝试弄清楚如何在ASP.Net Core中最好地使用HttpClient类。
根据文档和几篇文章的说法,最好在应用程序的生命周期内实例化一次该类,并共享多个请求。不幸的是,在Core中我找不到如何正确执行此操作的示例,因此我想出了以下解决方案。
我的特定需求需要使用2个不同的端点(我有一个业务逻辑的APIServer和一个API驱动的ImageServer),因此我的想法是拥有2个HttpClient单例,我可以在应用程序中使用。
我已经在appsettings.json中配置了我的服务点,如下所示:
"ServicePoints": {
"APIServer": "http://localhost:5001",
"ImageServer": "http://localhost:5002",
}
接下来,我创建了一个HttpClientsFactory,它将实例化我的2个 HttpClient 并将它们保存在静态 Dictionary 中。
public class HttpClientsFactory : IHttpClientsFactory
{
public static Dictionary<string, HttpClient> HttpClients { get; set; }
private readonly ILogger _logger;
private readonly IOptions<ServerOptions> _serverOptionsAccessor;
public HttpClientsFactory(ILoggerFactory loggerFactory, IOptions<ServerOptions> serverOptionsAccessor) {
_logger = loggerFactory.CreateLogger<HttpClientsFactory>();
_serverOptionsAccessor = serverOptionsAccessor;
HttpClients = new Dictionary<string, HttpClient>();
Initialize();
}
private void Initialize()
{
HttpClient client = new HttpClient();
// ADD imageServer
var imageServer = _serverOptionsAccessor.Value.ImageServer;
client.BaseAddress = new Uri(imageServer);
HttpClients.Add("imageServer", client);
// ADD apiServer
var apiServer = _serverOptionsAccessor.Value.APIServer;
client.BaseAddress = new Uri(apiServer);
HttpClients.Add("apiServer", client);
}
public Dictionary<string, HttpClient> Clients()
{
return HttpClients;
}
public HttpClient Client(string key)
{
return Clients()[key];
}
}
然后,我创建了一个接口,以后在定义我的DI时可以使用它。注意,HttpClientsFactory类继承自这个接口。
public interface IHttpClientsFactory
{
Dictionary<string, HttpClient> Clients();
HttpClient Client(string key);
}
现在我已准备好将此内容注入到我的依赖容器中,在“ConfigureServices”方法下的“Startup”类中按照以下方式进行。
// Add httpClient service
services.AddSingleton<IHttpClientsFactory, HttpClientsFactory>();
现在已经准备好在我的控制器中开始使用它了。
首先,我引入依赖关系。为此,我创建了一个私有类属性来保存它,然后将其添加到构造函数签名中,并最后通过将传入的对象分配给本地类属性来完成。
private IHttpClientsFactory _httpClientsFactory;
public AppUsersAdminController(IHttpClientsFactory httpClientsFactory)
{
_httpClientsFactory = httpClientsFactory;
}
最后,我们现在可以使用工厂来请求 htppclient 并执行调用。以下是一个示例,在此示例中我使用 httpclientsfactory 请求图像服务器上的图像:
[HttpGet]
public async Task<ActionResult> GetUserPicture(string imgName)
{
// get imageserver uri
var imageServer = _optionsAccessor.Value.ImageServer;
// create path to requested image
var path = imageServer + "/imageuploads/" + imgName;
var client = _httpClientsFactory.Client("imageServer");
byte[] image = await client.GetByteArrayAsync(path);
return base.File(image, "image/jpeg");
}
完成了!
我已经在我的开发环境中进行了测试,效果很好。但是,我不确定这是否是实现此功能的最佳方法。我仍然有以下问题:
- 这种解决方案是否线程安全?(根据 Microsoft 文档:“此类型的任何公共静态成员(在 Visual Basic 中为 Shared)都是线程安全的。”)
- 这种设置能否处理大量负载而不打开许多单独的连接?
- 在 ASP.Net Core 中如何处理“Singleton HttpClient?Beware of this serious behaviour and how to fix.”中描述的 DNS 问题,该问题位于http://byterot.blogspot.be/2016/07/singleton-httpclient-dns.html
- 有其他改进或建议吗?
HttpClient
实例。只需注册一个单例并使用它即可。DNS 问题仍然存在。顺便说一下,Factory
类 创建 对象实例。在此处阅读有关工厂模式的更多信息:https://msdn.microsoft.com/en-us/library/ee817667.aspx。 - galdin