using Microsoft.Net.Http.Headers; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Net; using Sarand.Net.Standard.Base.Tasks; using Sarand.Net.Standard.Base.Utils; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Sarand.Net.Standard.Base.Helpers { //======================================================================= public static class SacsHttpClientHelper { //------------------------------------------------------------------- /// Request configuration public class RequestData : SacsTaskData { // Construction. public RequestData(CancellationToken? cancellationToken) : base(cancellationToken) { } /// URL to send request to. public UriBuilder UriBuilder { get; } = new UriBuilder(); /// /// Request timeout. Default: 5 secs. /// NB Not currently supported as can only be changed when static HTTP Client not in use. /// public int TimeoutSecs { get; set; } = SacsHttpClient.RequestTimeoutSecs_Default; /// /// Headers to send with request. /// Accept, Accept-Charset, Accept-Encoding, Accept-Language, User-Agent, Transfer-Encoding, Pragma, Expect etc. /// public Dictionary> RequestHeaders { get; } = new Dictionary>(); /// /// Content Headers to send with request. /// Content-Length (bytes), Content-Type (media type), Content-Encoding (compression algorithm), Content-Language etc. /// Content-Type header is defaulted for POST request if not specified. /// public Dictionary> ContentHeaders { get; } = new Dictionary>(); /// /// Form values to post as 'URL Encoded'. /// Content-Type defaults to 'application/x-www-form-urlencoded; charset=utf-8'. /// public List> FormData { get; } = new List>(); /// /// Data to POST. Ignored if FormData populated. /// Content-Type defaults to 'text/plain; charset=utf-8'. /// public String PostData { get; set; } /// Use POST request rather than GET even if no data to post. public bool ForcePost { get; set; } } //------------------------------------------------------------------- /// Response data public class ResponseData : SacsHttpResponseData { } //------------------------------------------------------------------- // Data members. //------------------------------------------------------------------- // Static initialisation. static SacsHttpClientHelper() { } //------------------------------------------------------------------- // Properties. public static HttpClient HttpClient { get { return SacsHttpClient.HttpClient; } } public static int RequestTimeoutSecs_Default { get { return SacsHttpClient.RequestTimeoutSecs_Default; } } public static String HeaderName_ContentType { get {; return SacsHttpClient.HeaderName_ContentType; } } //------------------------------------------------------------------- public static async Task DoRequest(RequestData requestData) { requestData.ThrowIfCancellationRequested(); ResponseData responseData = new ResponseData(); // Construct content to post. HttpContent postContent = null; if (requestData.FormData.Count > 0) { postContent = new FormUrlEncodedContent(requestData.FormData); if (!requestData.ContentHeaders.ContainsKey(SacsHttpClient.HeaderName_ContentType)) requestData.ContentHeaders.Add(SacsHttpClient.HeaderName_ContentType, "application/x-www-form-urlencoded; charset=utf-8"); } else if (!StringUtil.IsEmpty(requestData.PostData) || requestData.ForcePost) { postContent = new StringContent(StringUtil.EmptyOnNull(requestData.PostData), Encoding.UTF8); if (!requestData.ContentHeaders.ContainsKey(SacsHttpClient.HeaderName_ContentType)) requestData.ContentHeaders.Add(SacsHttpClient.HeaderName_ContentType, "text/plain; charset=utf-8"); } requestData.ThrowIfCancellationRequested(); // Make request. DateTime startedAt = DateTime.Now; try { // Request. using (HttpRequestMessage request = new HttpRequestMessage()) { request.RequestUri = requestData.UriBuilder.Uri; // Request headers. foreach (String headerName in requestData.RequestHeaders.Keys) { if (request.Headers.Contains(headerName)) request.Headers.Remove(headerName); request.Headers.Add(headerName, requestData.RequestHeaders[headerName]); } // Default request headers. if (SacsHttpClient.HttpClient.DefaultRequestHeaders.Accept.Count == 0 && request.Headers.Accept.Count == 0) request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/*")); // Configure request. HttpResponseMessage response = null; // Send request. if (postContent != null) { // POST. request.Method = HttpMethod.Post; request.Content = postContent; // Content headers. foreach (String headerName in requestData.ContentHeaders.Keys) { if (request.Content.Headers.Contains(headerName)) request.Content.Headers.Remove(headerName); request.Content.Headers.Add(headerName, requestData.ContentHeaders[headerName]); } // Send request. responseData.RequestStarted = DateTime.Now; response = await SacsHttpClient.HttpClient.SendAsync(request, requestData.CancellationToken); responseData.RequestCompleted = DateTime.Now; } else { // GET. request.Method = HttpMethod.Get; responseData.RequestStarted = DateTime.Now; response = await SacsHttpClient.HttpClient.SendAsync(request, requestData.CancellationToken); responseData.RequestCompleted = DateTime.Now; } // Extract response data. responseData.Version = response.Version; responseData.Success = response.IsSuccessStatusCode; responseData.StatusCode = response.StatusCode; responseData.ReasonPhrase = response.ReasonPhrase; if (response.Headers != null) { foreach (var header in response.Headers) responseData.ResponseHeaders[header.Key] = new List(header.Value); } if (response.Content != null && response.Content.Headers != null) { foreach (var header in response.Content.Headers) responseData.ContentHeaders[header.Key] = new List(header.Value); } responseData.Data = await response.Content.ReadAsStringAsync(); if (response != null) response.Dispose(); } } catch (Exception exc) { // Ignore if task has been cancelled. if (requestData.IsCancellationRequested) throw exc; // Record exception. responseData.Exception = exc; if (exc.InnerException != null) { exc = exc.InnerException; } String msg = exc.GetType().Name + ": " + exc.Message; SaLogger.Warn(SysUtil.Fn + " " + msg + ExceptionUtil.GetStackEntries(exc, 6).Concat("", "\n\t")); responseData.ErrorStateMessage = msg; } requestData.ThrowIfCancellationRequested(); // Complete response data. responseData.Elapsed = DateTime.Now - startedAt; return responseData; } //------------------------------------------------------------------- // R e s p o n s e S t a t u s D e s c r i p t i o n //------------------------------------------------------------------- public static String GetResponseStatusDescription(HttpStatusCode httpStatus) { return SacsHttpClient.GetResponseStatusDescription(httpStatus); } //------------------------------------------------------------------- } //======================================================================= }