Rest 带有DI的.Net Core 1.1 HttpClient

Rest 带有DI的.Net Core 1.1 HttpClient,rest,singleton,asp.net-core-1.0,Rest,Singleton,Asp.net Core 1.0,以下是我正在使用的代码: 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.Configurat

以下是我正在使用的代码:

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

    }
}
名称空间MySite.Api
{
使用System.Collections.Specialized;
使用System.Linq;
Net系统;
使用System.Net.Http;
使用系统文本;
使用System.Threading.Tasks;
使用Microsoft.Extensions.Configuration;
使用Newtonsoft.Json;
使用制度;
使用System.Net.Http.Header;
使用Microsoft.Extensions.Logging;
/// 
///API查询执行助手
/// 
公共类ApiQuery:IApiQuery
{
/// 
///配置参考
/// 
私有IConfiguration配置;
私人HmacAuthenticationUtils hmacUtils;
专用只读ILogger记录器;
私有静态只读HttpClient HttpClient=新HttpClient();
私有静态只读HttpClient httpClientHMAC=新HttpClient();
/// 
///初始化类的新实例。
/// 
///注入配置
公共ApiQuery(图中的IConfig配置、HmacAuthenticationUtils hmacUtils、iLogger工厂和loggerFactory)
{
this.config=inConfig;
this.hmacUtils=hmacUtils;
this.logger=loggerFactory.CreateLogger(“perfLogger”);
}
/// 
///HTTP动词post
/// 
///API url
///请求数据
///HTTP响应消息
公共虚拟异步任务Post(字符串requestUrl,对象requestData,HttpClient=null)
{
返回等待缓存(requestUrl、requestData、客户端);
}
/// 
///HTTP谓词post,专门用于绕过缓存
/// 
///API url
///请求数据
///HTTP响应消息
公共异步任务PostBypassCache(字符串requestUrl,对象requestData,HttpClient客户端=null)
{
DateTime perfStart=DateTime.Now;
string customerJson=string.Empty;
if(请求数据为字符串)
{
customerJson=requestData.ToString();
}
其他的
{
customerJson=JsonConvert.SerializeObject(requestData);
}
////只是一些要测试的模板输出,我正在返回。
字符串resultJson=“{'status':'No Content'}”;
if(客户端==null)
{
client=httpClient;
}
var response=wait-httpClient.PostAsync(requestUrl,newstringcontent(customerJson,Encoding.UTF8,“application/json”);
if(response.StatusCode==HttpStatusCode.OK)
{
resultJson=await response.Content.ReadAsStringAsync();
}
logger.LogInformation(“响应时间:”+(DateTime.Now-perfStart).totalmillizes+“ms.Resource:”+requestUrl);
返回resultJson;
}
/// 
///HTTP动词post
/// 
///API url
///请求数据
///标题数据
///字符串数据
公共异步任务PostHmacAuth(字符串requestUrl,字符串requestData)
{
var httpRequest=新的HttpRequestMessage(HttpMethod.Post,requestUrl);
httpRequest.Content=newstringcontent(requestData,Encoding.UTF8,“application/json”);
var signature=wait Utils.GenerateAuthenticationString(httpRequest);
httpClientHMAC.DefaultRequestHeaders.Authorization=新的AuthenticationHeaderValue(HmacAuthenticationUtils.HmacHeader,签名);
return wait-PostBypassCache(requestUrl、requestData、httpClientHMAC);
}
}
}
在Startup.cs中,我注入它

services.AddTransient<IApiQuery, ApiQuery>();
services.AddTransient();
我最近做了这些更改,因为之前代码实际上是在每个方法中实例化httpClient,即。, var client=新的HttpClient()

在某些地方,它就像: 正在使用(var client=new HttpClient()){}

我认为由于这样的代码,appPool显示了我的IIS挂起的错误,只有重新启动appPool才能解决这个问题。我总结说这是一个问题,因为我阅读了很多其他文章。我无法得出的结论是,将ApiQuery服务作为singleton本身注入是否是一个好主意。 把它作为一种药物注射会更好吗


由于我现在将IApiQuery作为临时服务注入到每个业务服务中,这是个好主意吗?任何想法

HttpClient
都应该是单例范围的。您的计算机上可用的连接数量有限,而且由于
HttpClient
保留了它创建的连接,因此多个实例的浮动会很快耗尽您的连接池

从ASP.NET Core 2.1开始,就有了
IHttpClientFactory
,它提供了一种简单且可重用的方法来注入范围适当的
HttpClient
实例。但是,由于您使用的是1.1,因此无法使用。建议的路径是将项目升级到2.1。ASP.NET核心的1.X行显然是垃圾。它还没有准备好投入生产使用,尽管它是官方发布的

如果坚持使用1.1,则需要实现自己的重用
HttpClient
实例的方法。最直接的方法是使用“accessor”类,然后可以利用这些类将不同的
HttpClient
s注入到不同的对象中。例如:

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);        
}
然后,您可以将这个访问器类注册为单例,这意味着它只会创建一次(因此包含的
HttpClient
也只会创建一次)。然后,设置类以在其构造函数中接受此访问器:

public class ApiQuery : IApiQuery
{
    private readonly HttpClient _client;

    public ApiQuery(ApiHttpClientAccessor httpClientAccessor)
    {
        _client = (httpClientAccessor ?? throw new ArgumentNullException(nameof(httpClientAccessor))).HttpClient;
    }

    ...
}
Startup.cs
中:

services.AddSingleton<ApiHttpClientAccessor>();
services.AddTransient<IApiQuery, ApiQuery>();
services.AddSingleton();
services.AddTransient();

感谢您的回答。迁移正在进行中,但至少需要2个月才能投入生产。我想