using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; namespace EVCB_OCPP.WSServer.Service { public class HttpClientService { /// /// 要求逾時前等候的時間長度 /// #網域名稱系統(DNS)查詢最多可能需要15秒的時間才會傳回或超時 /// 預設60秒 /// public int Timeout { get => _timeout; set => _timeout = value; } /// /// 取得或設定使用 HttpClient 物件提出要求時,所允許的同時連線 數目上限 (每個伺服器端點)。 /// 請注意,此限制是按照每個伺服器端點計算,例如值 256 允許 http://www.adatum.com/ 使用 256 個同時連線, /// 而 http://www.adventure-works.com/ 另有 256 個同時連線。 /// 預設100個 /// public int MaxConnectionsPerServer { get => _maxConnectionsPerServer; set => _maxConnectionsPerServer = value; } /// /// 設定HttpMessageHandler的生命週期,如果其存留期間尚未過期,HttpMessageHandler 執行個體可從集區重複使用(建立新的 HttpClient 執行個體時) /// 因為處理常式通常會管理自己專屬的底層 HTTP 連線。 建立比所需數目更多的處理常式,可能會導致連線延遲。 有些處理常式也會保持連線無限期地開啟,這可能導致處理常式無法回應 DNS (網域名稱系統)變更。 /// 預設處理常式存留時間為120秒。 /// public int HandlerLifetime { get => _handlerLifetime; set => _handlerLifetime = value; } private IHttpClientFactory _clientFactory = null; private IServiceCollection _services = new ServiceCollection(); private int _handlerLifetime = 2; private int _maxConnectionsPerServer = 300; private int _timeout = 60; public HttpClientService(string baseAddress = "") { _services.AddHttpClient("Default", c => { if (!string.IsNullOrEmpty(baseAddress)) { c.BaseAddress = new Uri(baseAddress); } c.Timeout = TimeSpan.FromSeconds(_timeout); c.DefaultRequestHeaders.Add("Cache-Control", "no-cache"); }) .AddTypedClient().SetHandlerLifetime(TimeSpan.FromMinutes(_handlerLifetime)).ConfigurePrimaryHttpMessageHandler((h => { return new HttpClientHandler { MaxConnectionsPerServer = _maxConnectionsPerServer }; })); Init(); } private void Init() { _clientFactory = _services.BuildServiceProvider() .GetRequiredService(); } public virtual async Task PostJsonAsync(string Url, string bodyData, Dictionary headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null) { HttpResponse result = new HttpResponse() { IsError = false }; try { var client = _clientFactory.CreateClient(clientName); if (!string.IsNullOrEmpty(authorizationToken)) { client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken); } if (headers != null) { for (int idx = 0; idx < headers.Count; idx++) { client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value); } } HttpContent content = new StringContent(bodyData); content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json"); content.Headers.ContentType.CharSet = "UTF-8"; var response = await client.PostAsync(Url, content).ConfigureAwait(false); result.IsSuccessStatusCode = response.IsSuccessStatusCode; result.Headers = response.Headers; result.RequestMessage = response.RequestMessage; result.StatusCode = response.StatusCode; result.Response = await response.Content.ReadAsStringAsync(); } catch (Exception ex) { result.IsError = true; result.Exception = ex; } return result; } public virtual async Task GetJsonAsync(string Url, Dictionary headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null) { HttpResponse result = new HttpResponse() { IsError = false }; try { var client = _clientFactory.CreateClient(clientName); if (!string.IsNullOrEmpty(authorizationToken)) { client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken); } if (headers != null) { for (int idx = 0; idx < headers.Count; idx++) { client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value); } } // client.DefaultRequestHeaders.Add("Content-Type", "application/json"); var response = await client.GetAsync(Url).ConfigureAwait(false); result.IsSuccessStatusCode = response.IsSuccessStatusCode; result.Headers = response.Headers; result.RequestMessage = response.RequestMessage; result.StatusCode = response.StatusCode; result.Response = await response.Content.ReadAsStringAsync(); } catch (Exception ex) { result.IsError = true; result.Exception = ex; } return result; } public virtual async Task PutJsonAsync(string Url, string bodyData, Dictionary headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null) { HttpResponse result = new HttpResponse() { IsError = false }; try { var client = _clientFactory.CreateClient(clientName); if (!string.IsNullOrEmpty(authorizationToken)) { client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken); } if (headers != null) { for (int idx = 0; idx < headers.Count; idx++) { client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value); } } HttpContent content = new StringContent(bodyData); content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json"); content.Headers.ContentType.CharSet = "UTF-8"; var response = await client.PutAsync(Url, content).ConfigureAwait(false); result.IsSuccessStatusCode = response.IsSuccessStatusCode; result.Headers = response.Headers; result.RequestMessage = response.RequestMessage; result.StatusCode = response.StatusCode; result.Response = await response.Content.ReadAsStringAsync(); } catch (Exception ex) { result.IsError = true; result.Exception = ex; } return result; } public virtual async Task DeleteJsonAsync(string Url, Dictionary headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null) { HttpResponse result = new HttpResponse() { IsError = false }; try { var client = _clientFactory.CreateClient(clientName); if (!string.IsNullOrEmpty(authorizationToken)) { client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken); } if (headers != null) { for (int idx = 0; idx < headers.Count; idx++) { client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value); } } // client.DefaultRequestHeaders.Add("Content-Type", "application/json"); var response = await client.DeleteAsync(Url).ConfigureAwait(false); result.IsSuccessStatusCode = response.IsSuccessStatusCode; result.Headers = response.Headers; result.RequestMessage = response.RequestMessage; result.StatusCode = response.StatusCode; result.Response = await response.Content.ReadAsStringAsync(); } catch (Exception ex) { result.IsError = true; result.Exception = ex; } return result; } public virtual async Task PostFormDataAsync(string Url, Dictionary bodyData, Dictionary headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null) { HttpResponse result = new HttpResponse() { IsError = false }; try { var client = _clientFactory.CreateClient(clientName); ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; if (!string.IsNullOrEmpty(authorizationToken)) { client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken); } if (headers != null) { for (int idx = 0; idx < headers.Count; idx++) { client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value); } } var content = new MultipartFormDataContent(); foreach (var keyValuePair in bodyData) { content.Add(new StringContent(keyValuePair.Value), "\"" + keyValuePair.Key + "\""); } var response = await client.PostAsync(Url, content).ConfigureAwait(false); result.IsSuccessStatusCode = response.IsSuccessStatusCode; result.Headers = response.Headers; result.RequestMessage = response.RequestMessage; result.StatusCode = response.StatusCode; result.Response = await response.Content.ReadAsStringAsync(); } catch (Exception ex) { result.IsError = true; result.Exception = ex; } return result; } } public class HttpResponse { public bool IsError { internal set; get; } public Exception Exception { internal set; get; } public string Response { internal set; get; } /// /// 摘要: /// 取得或設定 HTTP 回應的狀態碼。 /// 傳回: /// HTTP 回應的狀態碼。 /// public HttpStatusCode StatusCode { get; internal set; } /// /// 摘要: /// 取得 HTTP 回應標頭的集合。 /// 傳回: /// HTTP 回應標頭的集合。 /// public HttpResponseHeaders Headers { get; internal set; } /// /// 摘要: /// 取得或設定導致此回應訊息的要求訊息。 /// 傳回: /// 導致此回應訊息的要求訊息。 /// public HttpRequestMessage RequestMessage { get; internal set; } /// /// 摘要: /// 取得指示 HTTP 回應是否成功的值。 /// 傳回: /// 指示 HTTP 回應是否成功的值。 如果 System.Net.Http.HttpResponseMessage.StatusCode 在 200-299 /// 的範圍內,則為 true;否則為 false。 /// public bool IsSuccessStatusCode { get; internal set; } } }