.NetCore简单封装基于IHttpClientFactory的HttpClient请求
IHttpClientFactory是什么?为什么出现了IHttpClientFactory
一、IHttpClientFactory是什么?
IHttpClientFactory是.netcore2.1才开始引入的,是HttpClient的工厂接口,它为我们提供了获取HttpClient的接口,它帮助我们维护HttpClient的生命周期。当我们需要HttpClient访问网络时,它会自动帮我们获取或者是创建HttpClient(存在空闲的HttpClient时,直接提供;当不存在可用的HttpClient时自动创建)。它相当于HttpClient池。
二、为什么出现IHttpClientFactory?
传统的HttpClient创建后,其占用了Socket资源并且其不会是及时的回收。我们每次new一个HttpClient时是一个全新的对象,所以在高并发下又是会导致socket资源耗尽(Unable to connect to the remote serverSystem.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.)。而如果采用单例或者静态的HttpClient,却达不到高效的使用网络请求,而且当访问不同的url时,单例或者静态的HttpClient往往会导致访问混乱而出现错误。
.NetCore简单封装基于IHttpClientFactory的HttpClient请求
1 public class HttpWebClient 2 { 3 4 private IHttpClientFactory _httpClientFactory; 5 private readonly ILogger<HttpWebClient> _logger; 6 public HttpWebClient(IHttpClientFactory httpClientFactory, ILogger<HttpWebClient> logger) 7 { 8 this._httpClientFactory = httpClientFactory; 9 this._logger = logger; 10 } 11 12 /// <summary> 13 /// Get 14 /// </summary> 15 /// <param name="url"></param> 16 /// <param name="dicHeaders"></param> 17 /// <param name="timeoutSecond"></param> 18 /// <returns></returns> 19 public async Task<T> GetAsync<T>(string url, Dictionary<string, string> dicHeaders, int timeoutSecond = 180) 20 { 21 try 22 { 23 var client = BuildHttpClient(dicHeaders, timeoutSecond); 24 var response = await client.GetAsync(url); 25 var responseContent = await response.Content.ReadAsStringAsync(); 26 if (response.IsSuccessStatusCode) 27 { 28 return JsonUtil.Deserialize<T>(responseContent); 29 } 30 else 31 { 32 throw new CustomerHttpException(response.StatusCode.ToString(), responseContent); 33 } 34 } 35 catch (Exception ex) 36 { 37 _logger.LogError($"HttpGet:{url} Error:{ex.ToString()}"); 38 throw new Exception($"HttpGet:{url} Error", ex); 39 } 40 } 41 /// <summary> 42 /// Post 43 /// </summary> 44 /// <param name="url"></param> 45 /// <param name="requestBody"></param> 46 /// <param name="dicHeaders"></param> 47 /// <param name="timeoutSecond"></param> 48 /// <returns></returns> 49 public async Task<T> PostAsync<T>(string url, string requestBody, Dictionary<string, string> dicHeaders, int timeoutSecond = 180) 50 { 51 try 52 { 53 var client = BuildHttpClient(null, timeoutSecond); 54 var requestContent = GenerateStringContent(requestBody, dicHeaders); 55 var response = await client.PostAsync(url, requestContent); 56 var responseContent = await response.Content.ReadAsStringAsync(); 57 if (response.IsSuccessStatusCode) 58 { 59 var result = JsonUtil.Deserialize<T>(responseContent); 60 return result; 61 } 62 else 63 { 64 throw new CustomerHttpException(response.StatusCode.ToString(), responseContent); 65 } 66 } 67 catch (Exception ex) 68 { 69 _logger.LogError($"HttpPost:{url},body:{requestBody} Error:{ex.ToString()}"); 70 throw new Exception($"HttpPost:{url} Error", ex); 71 } 72 } 73 /// <summary> 74 /// Put 75 /// </summary> 76 /// <param name="url"></param> 77 /// <param name="requestBody"></param> 78 /// <param name="dicHeaders"></param> 79 /// <param name="timeoutSecond"></param> 80 /// <returns></returns> 81 public async Task<T> PutAsync<T>(string url, string requestBody, Dictionary<string, string> dicHeaders, int timeoutSecond = 180) 82 { 83 try 84 { 85 var client = BuildHttpClient(null, timeoutSecond); 86 var requestContent = GenerateStringContent(requestBody, dicHeaders); 87 var response = await client.PutAsync(url, requestContent); 88 var responseContent = await response.Content.ReadAsStringAsync(); 89 if (response.IsSuccessStatusCode) 90 { 91 var result = JsonUtil.Deserialize<T>(responseContent); 92 return result; 93 } 94 else 95 { 96 throw new CustomerHttpException(response.StatusCode.ToString(), responseContent); 97 } 98 } 99 catch (Exception ex) 100 { 101 _logger.LogError($"HttpPut:{url},Body:{requestBody}, Error:{ex.ToString()}"); 102 throw new Exception($"HttpPut:{url} Error", ex); 103 } 104 } 105 106 /// <summary> 107 /// Patch 108 /// </summary> 109 /// <param name="url"></param> 110 /// <param name="requestString"></param> 111 /// <param name="dicHeaders"></param> 112 /// <param name="timeoutSecond"></param> 113 /// <returns></returns> 114 public async Task<T> PatchAsync<T>(string url, string requestBody, Dictionary<string, string> dicHeaders, int timeoutSecond = 180) 115 { 116 try 117 { 118 var client = BuildHttpClient(null, timeoutSecond); 119 var requestContent = GenerateStringContent(requestBody, dicHeaders); 120 var response = await client.PatchAsync(url, requestContent); 121 var responseContent = await response.Content.ReadAsStringAsync(); 122 if (response.IsSuccessStatusCode) 123 { 124 var result = JsonUtil.Deserialize<T>(responseContent); 125 return result; 126 } 127 else 128 { 129 throw new CustomerHttpException(response.StatusCode.ToString(), responseContent); 130 } 131 } 132 catch (Exception ex) 133 { 134 _logger.LogError($"HttpPatch:{url},body:{requestBody}, Error:{ex.ToString()}"); 135 throw new Exception($"HttpPatch:{url} Error", ex); 136 } 137 } 138 /// <summary> 139 /// Delete 140 /// </summary> 141 /// <param name="url"></param> 142 /// <param name="dicHeaders"></param> 143 /// <param name="timeoutSecond"></param> 144 /// <returns></returns> 145 public async Task<T> DeleteAsync<T>(string url, Dictionary<string, string> dicHeaders, int timeoutSecond = 180) 146 { 147 try 148 { 149 var client = BuildHttpClient(dicHeaders, timeoutSecond); 150 var response = await client.DeleteAsync(url); 151 var responseContent = await response.Content.ReadAsStringAsync(); 152 if (response.IsSuccessStatusCode) 153 { 154 var result = JsonUtil.Deserialize<T>(responseContent); 155 return result; 156 } 157 else 158 { 159 throw new CustomerHttpException(response.StatusCode.ToString(), responseContent); 160 } 161 } 162 catch (Exception ex) 163 { 164 _logger.LogError($"HttpDelete:{url}, Error:{ex.ToString()}"); 165 throw new Exception($"HttpDelete:{url} Error", ex); 166 } 167 } 168 /// <summary> 169 /// common request 170 /// </summary> 171 /// <param name="url"></param> 172 /// <param name="method"></param> 173 /// <param name="requestBody"></param> 174 /// <param name="dicHeaders"></param> 175 /// <param name="timeoutSecond"></param> 176 /// <returns></returns> 177 public async Task<T> ExecuteAsync<T>(string url, HttpMethod method, string requestBody, Dictionary<string, string> dicHeaders, int timeoutSecond = 180) 178 { 179 try 180 { 181 var client = BuildHttpClient(null, timeoutSecond); 182 var request = GenerateHttpRequestMessage(url, requestBody, method, dicHeaders); 183 var response = await client.SendAsync(request); 184 var responseContent = await response.Content.ReadAsStringAsync(); 185 if (response.IsSuccessStatusCode) 186 { 187 var type = typeof(T); 188 if (type.IsPrimitive || type == typeof(string)) 189 { 190 return (T)Convert.ChangeType(responseContent, typeof(T)); 191 } 192 else 193 { 194 return JsonUtil.Deserialize<T>(responseContent); 195 } 196 } 197 else 198 { 199 throw new CustomerHttpException(response.StatusCode.ToString(), responseContent); 200 } 201 } 202 catch (Exception ex) 203 { 204 _logger.LogError($"{method.ToString()}:{url},body:{requestBody}, Error:{ex.ToString()}"); 205 throw new Exception($"{method.ToString()}:{url} Error", ex); 206 } 207 208 } 209 /// <summary> 210 /// Build HttpClient 211 /// </summary> 212 /// <param name="timeoutSecond"></param> 213 /// <returns></returns> 214 private HttpClient BuildHttpClient(Dictionary<string, string> dicDefaultHeaders, int? timeoutSecond) 215 { 216 var httpClient = _httpClientFactory.CreateClient(); 217 httpClient.DefaultRequestHeaders.Clear(); //in order that the client is not affected by the last request,it need to clear DefaultRequestHeaders 218 httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 219 if (dicDefaultHeaders != null) 220 { 221 foreach (var headerItem in dicDefaultHeaders) 222 { 223 if (!httpClient.DefaultRequestHeaders.Contains(headerItem.Key)) 224 { 225 httpClient.DefaultRequestHeaders.Add(headerItem.Key, headerItem.Value); 226 } 227 } 228 } 229 if (timeoutSecond != (int?)null) 230 { 231 httpClient.Timeout = TimeSpan.FromSeconds(timeoutSecond.Value); 232 } 233 return httpClient; 234 } 235 236 /// <summary> 237 /// Generate HttpRequestMessage 238 /// </summary> 239 /// <param name="url"></param> 240 /// <param name="requestBody"></param> 241 /// <param name="method"></param> 242 /// <param name="dicHeaders"></param> 243 /// <returns></returns> 244 private HttpRequestMessage GenerateHttpRequestMessage(string url, string requestBody, HttpMethod method, Dictionary<string, string> dicHeaders) 245 { 246 var request = new HttpRequestMessage(method, url); 247 if (!string.IsNullOrEmpty(requestBody)) 248 { 249 request.Content = new StringContent(requestBody); 250 } 251 if (dicHeaders != null) 252 { 253 foreach (var header in dicHeaders) 254 { 255 request.Headers.Add(header.Key, header.Value); 256 } 257 } 258 return request; 259 } 260 /// <summary> 261 /// Generate StringContent 262 /// </summary> 263 /// <param name="requestBody"></param> 264 /// <param name="dicHeaders"></param> 265 /// <returns></returns> 266 private StringContent GenerateStringContent(string requestBody, Dictionary<string, string> dicHeaders) 267 { 268 var content = new StringContent(requestBody); 269 if (dicHeaders != null) 270 { 271 foreach (var headerItem in dicHeaders) 272 { 273 content.Headers.Add(headerItem.Key, headerItem.Value); 274 } 275 } 276 return content; 277 } 278 }
CustomerHttpException类的简单定义
1 public class CustomerHttpException : Exception 2 { 3 public string ErrorCode { get; set; } 4 public string ErrorMessage { get; set; } 5 public CustomerHttpException() : base() 6 { } 7 public CustomerHttpException(string errorCode, string errorMessage) : base() 8 { 9 this.ErrorCode = errorCode; 10 this.ErrorMessage = ErrorMessage; 11 } 12 }
以上是简单的Http请求封装。其中一些值得注意的点
1、创建的HttpClient是由HttpClientFactory统一管理的,所以当指定了DefaultRequestHeaders时,下次再次从HttpClientFactory中获取时可能带着上次的Header信息。所以需要对其进行Clear。
2、封装了统一的调用接口ExecuteAsync,建议采用该接口进行http请求,此时就不会去配置DefaultRequestHeaders,而是将Header信息配置在请求信息中。
3、自定义了CustomerHttpException,主要是为了支持ResultFul风格的api。通过外层捕获CustomerHttpException,从而解析出具体的错误信息。
posted on 2020-07-14 00:23 john_yong 阅读(3582) 评论(1) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律