简体   繁体   English

.Net Core 1.1 HttpClient 与 DI

[英].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.

相关问题 不清楚通过.NET Core中的HttpClient传递参数 - unclear on passing parameters through HttpClient in .NET Core NET Core中如何使用httpClient发送可序列化的请求对象? - How to send serializable request object with httpClient in .net core? httpClient-&gt; HTTP / 1.1 500内部服务器错误 - httpClient -> HTTP/1.1 500 Internal Server Error 如何在 asp.net 核心中使用 web api 响应传递 HttpClient 响应 - How pass HttpClient response with web api response in asp.net core ASP.NET Core 2.x - HttpClient api 返回错误“对象引用未设置为对象的实例” - ASP.NET Core 2.x - HttpClient api returns error 'Object reference not set to an instance of an object' ASP.NET Core 使用 HTTPClient 使用 ViewModel 发布表单数据 IFormFile - ASP.NET Core Post form data IFormFile with ViewModel using HTTPClient 来自 httpClient 的 POST 错误(HTTP/1.1 500 内部服务器错误)但在 Postman 中有效 - Error (HTTP/1.1 500 Internal Server Error) for POST from httpClient but works in Postman 性能问题:.NET HttpClient 响应很慢 - Performance Issue: .NET HttpClient Response is slow Xamarin-在.Net标准库中使用httpClient - Xamarin - Using httpClient in .Net Standard Library 是否可以对System.Net.Http.HttpClient使用持久连接? - Is it possible to use persistent connections with System.Net.Http.HttpClient?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM