.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  阅读(3320)  评论(1编辑  收藏  举报

导航