在.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界面调用接口测试结果