ASP.NET Core使用IHttpClientFactory发起HTTP请求

  在.NET项目开发中,我们常用于发起HTTP请求HttpClient类由于先天缺陷,调用HttpClient的Dispose方法后并不能立即释放套接字(Sokect)资源。

在频繁的发起HTTP请求的系统中将会存在大量的处于TIME_WAIT状态的套接字资源,最终导致套接字资源被耗尽。为了解决这个问题,微软推出了IHttpClientFactory方案,大概意思就是将链接对象池化。、

具体使用案列如下,(本案列只封装了部分参数,基本已经可以满足绝大部分的需求,如有特殊要求,请自行扩展参数。代码中使用了Newtonsoft.Json。

1.在Startup中的ConfigureServices方法中注册IHttpClientFactory.和实现类 

public void ConfigureServices(IServiceCollection services)
{
   //注册IHttpClientFactory
   services.AddHttpClient();
   //注册IHttpClientFactory的实现到DI容器
   services.AddTransient<HttpClientHelper>();
}

 2.基于IHttpClientFactory的Helper类封装。

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Research.Web.Common
{
    /// <summary>
    /// HTTP帮助类
    /// </summary>
    public class HttpClientHelper
    {
        private IHttpClientFactory _httpClientFactory;
        public HttpClientHelper(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        /// <summary>
        /// 发起GET异步请求
        /// </summary>
        /// <typeparam name="T">返回类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="headers">请求头信息</param>
        /// <param name="timeOut">请求超时时间,单位秒</param>
        /// <returns>返回string</returns>
        public async Task<string> GetAsync(string url, Dictionary<string, string> headers = null, int timeOut = 30)
        {
            var hostName = GetHostName(url);
            using (HttpClient client = _httpClientFactory.CreateClient(hostName))
            {
                client.Timeout = TimeSpan.FromSeconds(timeOut);
                if (headers?.Count > 0)
                {
                    foreach (string key in headers.Keys)
                    {
                        client.DefaultRequestHeaders.Add(key, headers[key]);
                    }
                }
                using (HttpResponseMessage response = await client.GetAsync(url))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        string responseString = await response.Content.ReadAsStringAsync();
                        return responseString;
                    }
                    else
                    {
                        return string.Empty;
                    }
                }
            }
        }


        /// <summary>
        /// 发起POST异步请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="body">POST提交的内容</param>
        /// <param name="bodyMediaType">POST内容的媒体类型,如:application/xml、application/json</param>
        /// <param name="responseContentType">HTTP响应上的content-type内容头的值,如:application/xml、application/json、application/text、application/x-www-form-urlencoded等</param>
        /// <param name="headers">请求头信息</param>
        /// <param name="timeOut">请求超时时间,单位秒</param>
        /// <returns>返回string</returns>
        public async Task<string> PostAsync(string url, string body,
            string bodyMediaType = null,
            string responseContentType = null,
            Dictionary<string, string> headers = null,
            int timeOut = 30)
        {
            var hostName = GetHostName(url);
            using (HttpClient client = _httpClientFactory.CreateClient(hostName))
            {
                client.Timeout = TimeSpan.FromSeconds(timeOut);
                if (headers?.Count > 0)
                {
                    foreach (string key in headers.Keys)
                    {
                        client.DefaultRequestHeaders.Add(key, headers[key]);
                    }
                }
                StringContent content = new StringContent(body, System.Text.Encoding.UTF8, mediaType: bodyMediaType);
                if (!string.IsNullOrWhiteSpace(responseContentType))
                {
                    content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(responseContentType);
                }
                using (HttpResponseMessage response = await client.PostAsync(url, content))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        string responseString = await response.Content.ReadAsStringAsync();
                        return responseString;
                    }
                    else
                    {
                        return string.Empty;
                    }
                }
            }
        }

        /// <summary>
        /// 发起POST异步请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="body">POST提交的内容</param>
        /// <param name="bodyMediaType">POST内容的媒体类型,如:application/xml、application/json</param>
        /// <param name="responseContentType">HTTP响应上的content-type内容头的值,如:application/xml、application/json、application/text、application/x-www-form-urlencoded等</param>
        /// <param name="headers">请求头信息</param>
        /// <param name="timeOut">请求超时时间,单位秒</param>
        /// <returns>返回string</returns>
        public async Task<string> PutAsync(string url, string body,
            string bodyMediaType = null,
            string responseContentType = null,
            Dictionary<string, string> headers = null,
            int timeOut = 30)
        {
            var hostName = GetHostName(url);
            using (HttpClient client = _httpClientFactory.CreateClient(hostName))
            {
                client.Timeout = TimeSpan.FromSeconds(timeOut);
                if (headers?.Count > 0)
                {
                    foreach (string key in headers.Keys)
                    {
                        client.DefaultRequestHeaders.Add(key, headers[key]);
                    }
                }
                StringContent content = new StringContent(body, System.Text.Encoding.UTF8, mediaType: bodyMediaType);
                if (!string.IsNullOrWhiteSpace(responseContentType))
                {
                    content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(responseContentType);
                }
                using (HttpResponseMessage response = await client.PutAsync(url, content))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        string responseString = await response.Content.ReadAsStringAsync();
                        return responseString;
                    }
                    else
                    {
                        return string.Empty;
                    }
                }
            }
        }

        /// <summary>
        /// 发起GET异步请求
        /// </summary>
        /// <typeparam name="T">返回类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="headers">请求头信息</param>
        /// <param name="timeOut">请求超时时间,单位秒</param>
        /// <returns>返回string</returns>
        public async Task<string> DeleteAsync(string url, Dictionary<string, string> headers = null, int timeOut = 30)
        {
            var hostName = GetHostName(url);
            using (HttpClient client = _httpClientFactory.CreateClient(hostName))
            {
                client.Timeout = TimeSpan.FromSeconds(timeOut);
                if (headers?.Count > 0)
                {
                    foreach (string key in headers.Keys)
                    {
                        client.DefaultRequestHeaders.Add(key, headers[key]);
                    }
                }
                using (HttpResponseMessage response = await client.DeleteAsync(url))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        string responseString = await response.Content.ReadAsStringAsync();
                        return responseString;
                    }
                    else
                    {
                        return string.Empty;
                    }
                }
            }
        }

        /// <summary>
        /// 发起GET异步请求
        /// </summary>
        /// <typeparam name="T">返回类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="headers">请求头信息</param>
        /// <param name="timeOut">请求超时时间,单位秒</param>
        /// <returns>返回T</returns>
        public async Task<T> GetAsync<T>(string url, Dictionary<string, string> headers = null, int timeOut = 30) where T : new()
        {
            string responseString = await GetAsync(url, headers, timeOut);
            if (!string.IsNullOrWhiteSpace(responseString))
            {
                return JsonConvert.DeserializeObject<T>(responseString);
            }
            else
            {
                return default(T);
            }
        }


        /// <summary>
        /// 发起POST异步请求
        /// </summary>
        /// <typeparam name="T">返回类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="body">POST提交的内容</param>
        /// <param name="bodyMediaType">POST内容的媒体类型,如:application/xml、application/json</param>
        /// <param name="responseContentType">HTTP响应上的content-type内容头的值,如:application/xml、application/json、application/text、application/x-www-form-urlencoded等</param>
        /// <param name="headers">请求头信息</param>
        /// <param name="timeOut">请求超时时间,单位秒</param>
        /// <returns>返回T</returns>
        public async Task<T> PostAsync<T>(string url, string body,
            string bodyMediaType = null,
            string responseContentType = null,
            Dictionary<string, string> headers = null,
            int timeOut = 30) where T : new()
        {
            string responseString = await PostAsync(url, body, bodyMediaType, responseContentType, headers, timeOut);
            if (!string.IsNullOrWhiteSpace(responseString))
            {
                return JsonConvert.DeserializeObject<T>(responseString);
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// 发起PUT异步请求
        /// </summary>
        /// <typeparam name="T">返回类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="body">POST提交的内容</param>
        /// <param name="bodyMediaType">POST内容的媒体类型,如:application/xml、application/json</param>
        /// <param name="responseContentType">HTTP响应上的content-type内容头的值,如:application/xml、application/json、application/text、application/x-www-form-urlencoded等</param>
        /// <param name="headers">请求头信息</param>
        /// <param name="timeOut">请求超时时间,单位秒</param>
        /// <returns>返回T</returns>
        public async Task<T> PutAsync<T>(string url, string body,
            string bodyMediaType = null,
            string responseContentType = null,
            Dictionary<string, string> headers = null,
            int timeOut = 30) where T : new()
        {
            string responseString = await PutAsync(url, body, bodyMediaType, responseContentType, headers, timeOut);
            if (!string.IsNullOrWhiteSpace(responseString))
            {
                return JsonConvert.DeserializeObject<T>(responseString);
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// 发起DELETE异步请求
        /// </summary>
        /// <typeparam name="T">返回类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="headers">请求头信息</param>
        /// <param name="timeOut">请求超时时间,单位秒</param>
        /// <returns>返回T</returns>
        public async Task<T> DeleteAsync<T>(string url, Dictionary<string, string> headers = null, int timeOut = 30) where T : new()
        {
            string responseString = await DeleteAsync(url, headers, timeOut);
            if (!string.IsNullOrWhiteSpace(responseString))
            {
                return JsonConvert.DeserializeObject<T>(responseString);
            }
            else
            {
                return default(T);
            }
        }
        #region 私有函数

        /// <summary>
        /// 获取请求的主机名
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private static string GetHostName(string url)
        {
            if (!string.IsNullOrWhiteSpace(url))
            {
                return url.Replace("https://", "").Replace("http://", "").Split('/')[0];
            }
            else
            {
                return "AnyHost";
            }
        }

        #endregion
    }
}

 3.调用基于IHttpClientFactory的Helper类发起Http请求。

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Research.Web.Common;

namespace Research.Web.Controllers
{
    [ApiExplorerSettings(GroupName = "Web")]
    [ApiController]
    [Route("Web")]
    public class WebeController : ControllerBase
    {
        private HttpClientHelper _httpClientHelper;

        /// <summary>
        /// 从DI容器中获取HttpClientHelper实列对象
        /// </summary>
        /// <param name="httpClientHelper"></param>
        public WebeController(HttpClientHelper httpClientHelper)
        {
            this._httpClientHelper = httpClientHelper;
        }

        /// <summary>
        /// 测试异步HttpClient
        /// </summary>
        /// <returns></returns>
        [HttpGet("testhttpasync")]
        public async Task<JsonResult> TestAsyncHttpClientFactory()
        {
            var model = await _httpClientHelper.GetAsync<ResponseModel>("https://www.juhe.cn/_t");
            return new JsonResult(model);
        }
    }
}

4.通过SwaggerUI界面调用接口测试结果

原文地址:https://www.cnblogs.com/pudefu/p/14715146.html