[英].Net Core 1.1 HttpClient with DI
Following is the code I am using:以下是我正在使用的代码:
namespace MySite.Api
{
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Net.Http.Headers;
using Microsoft.Extensions.Logging;
/// <summary>
/// API query execution helper
/// </summary>
public class ApiQuery : IApiQuery
{
/// <summary>
/// configuration reference
/// </summary>
private IConfiguration config;
private HmacAuthenticationUtils hmacUtils;
private readonly ILogger logger;
private static readonly HttpClient httpClient = new HttpClient();
private static readonly HttpClient httpClientHMAC = new HttpClient();
/// <summary>
/// Initializes a new instance of the <see cref="ApiQuery"/> class.
/// </summary>
/// <param name="inConfig">injected configuration</param>
public ApiQuery(IConfiguration inConfig, HmacAuthenticationUtils hmacUtils, ILoggerFactory loggerFactory)
{
this.config = inConfig;
this.hmacUtils = hmacUtils;
this.logger = loggerFactory.CreateLogger("perfLogger");
}
/// <summary>
/// HTTP verb post
/// </summary>
/// <param name="requestUrl">API url</param>
/// <param name="requestData">request data</param>
/// <returns>HTTP response message</returns>
public virtual async Task<string> Post(string requestUrl, object requestData, HttpClient client = null)
{
return await PostBypassCache(requestUrl, requestData, client);
}
/// <summary>
/// HTTP verb post, specifically to bypass cache
/// </summary>
/// <param name="requestUrl">API url</param>
/// <param name="requestData">request data</param>
/// <returns>HTTP response message</returns>
public async Task<string> PostBypassCache(string requestUrl, object requestData, HttpClient client = null)
{
DateTime perfStart = DateTime.Now;
string customerJson = string.Empty;
if (requestData is string)
{
customerJson = requestData.ToString();
}
else
{
customerJson = JsonConvert.SerializeObject(requestData);
}
////just some template output to test which I'm getting back.
string resultJson = "{ 'status':'No Content'}";
if (client == null)
{
client = httpClient;
}
var response = await httpClient.PostAsync(requestUrl, new StringContent(customerJson, Encoding.UTF8, "application/json"));
if (response.StatusCode == HttpStatusCode.OK)
{
resultJson = await response.Content.ReadAsStringAsync();
}
logger.LogInformation("response time: " + (DateTime.Now - perfStart).TotalMilliseconds + "ms. Resource:" + requestUrl);
return resultJson;
}
/// <summary>
/// HTTP verb post
/// </summary>
/// <param name="requestUrl">API url</param>
/// <param name="requestData">request data</param>
/// <param name="headerset">header data</param>
/// <returns>string data</returns>
public async Task<string> PostHmacAuth(string requestUrl, string requestData)
{
var httpRequest = new HttpRequestMessage(HttpMethod.Post, requestUrl);
httpRequest.Content = new StringContent(requestData, Encoding.UTF8, "application/json");
var signature = await Utils.GenerateAuthenticationString(httpRequest);
httpClientHMAC.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(HmacAuthenticationUtils.HmacHeader, signature);
return await PostBypassCache(requestUrl, requestData, httpClientHMAC);
}
}
}
In the Startup.cs, I inject it在 Startup.cs 中,我注入它
services.AddTransient<IApiQuery, ApiQuery>();
I have recently made these changes as previously the code was actually instantiating the httpClient in each of the method, ie, var client = new HttpClient();我最近进行了这些更改,因为之前的代码实际上是在每个方法中实例化 httpClient,即 var client = new HttpClient();
At some places it was like: using(var client = new HttpClient()){}在某些地方,它是这样的: using(var client = new HttpClient()){}
I Think due to such code, the the appPool is showing error due to which my IIS hangs, and the problem only get solved by restarting the appPool.我认为由于这样的代码,appPool 显示错误,因为我的 IIS 挂起,只有通过重新启动 appPool 才能解决问题。 I conclude this to be problem as I went through a lot of other articles.当我浏览了很多其他文章时,我得出结论这是有问题的。 What I was not able to conclude is on if it should be a good idea to inject the ApiQuery Service as singleton itself.我无法得出结论的是,将 ApiQuery 服务作为单例本身注入是否是一个好主意。 Will it be better to inject it as ?注入它会更好吗?
As I am injecting IApiQuery to every business service right now as a transient service, will it be good idea ?由于我现在将 IApiQuery 作为临时服务注入到每个业务服务中,这是个好主意吗? Any thoughts有什么想法吗
HttpClient
should be singleton-scoped. HttpClient
应该是单例范围的。 There's a finite number of connections available on your machine, and since HttpClient
holds on to connections it creates, having multiple instances floating around can quickly exhaust your connection pool.您的机器上可用的连接数是有限的,并且由于HttpClient
保留它创建的连接,因此多个实例浮动会很快耗尽您的连接池。
Beginning with ASP.NET Core 2.1, there exists IHttpClientFactory
which provides a simple and reusable way of injecting properly scoped HttpClient
instances.从 ASP.NET Core 2.1 开始,存在IHttpClientFactory
,它提供了一种注入适当范围的HttpClient
实例的简单且可重用的方法。 However, since you're using 1.1, that's not available to you.但是,由于您使用的是 1.1,因此您无法使用它。 The recommended path would be to upgrade your project to 2.1.推荐的路径是将您的项目升级到 2.1。 The 1.X line of ASP.NET Core is frankly trash. ASP.NET Core 的 1.X 线坦率地说是垃圾。 It wasn't ready for production use, despite being an official release.尽管已正式发布,但它尚未准备好用于生产。
If you insist on sticking with 1.1, then you'll need to implement your own method of reusing HttpClient
instances.如果您坚持使用 1.1,那么您将需要实现自己的重用HttpClient
实例的方法。 The most straightforward way is to use "accessor" classes, which you can then utilize to inject different HttpClient
s into different objects.最直接的方法是使用“访问器”类,然后您可以利用这些类将不同的HttpClient
注入到不同的对象中。 For example:例如:
public class ApiHttpClientAccessor : IDisposable
{
public ApiHttpClientAccessor()
{
HttpClient = new HttpClient
{
BaseAddress = new Uri("https://foo.com")
};
}
public HttpClient HttpClient { get; }
private bool _disposed;
public virtual void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
HttpClient.Dispose();
}
_disposed = true;
}
public bool Dispose() =>
Dispose(true);
}
Then, you can register this accessor class as a singleton, meaning it will only be created once (so the contained HttpClient
will also only be created once).然后,您可以将此访问器类注册为单例,这意味着它只会被创建一次(因此包含的HttpClient
也只会被创建一次)。 Then, set up your class to accept this accessor in its constructor:然后,将您的类设置为在其构造函数中接受此访问器:
public class ApiQuery : IApiQuery
{
private readonly HttpClient _client;
public ApiQuery(ApiHttpClientAccessor httpClientAccessor)
{
_client = (httpClientAccessor ?? throw new ArgumentNullException(nameof(httpClientAccessor))).HttpClient;
}
...
}
And in Startup.cs
:在Startup.cs
:
services.AddSingleton<ApiHttpClientAccessor>();
services.AddTransient<IApiQuery, ApiQuery>();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.