203 lines
7.9 KiB
C#
203 lines
7.9 KiB
C#
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.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
|
|
{
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Request configuration</summary>
|
|
public class RequestData : SacsTaskData
|
|
{
|
|
// Construction.
|
|
public RequestData(CancellationToken? cancellationToken) : base(cancellationToken) { }
|
|
/// <summary>URL to send request to.</summary>
|
|
public UriBuilder UriBuilder { get; } = new UriBuilder();
|
|
/// <summary>
|
|
/// Request timeout. Default: 5 secs.
|
|
/// <para>NB Not currently supported as can only be changed when static HTTP Client not in use.</para>
|
|
/// </summary>
|
|
public int TimeoutSecs { get; set; } = SacsHttpClient.RequestTimeoutSecs_Default;
|
|
/// <summary>
|
|
/// Headers to send with request.
|
|
/// <para>Accept, Accept-Charset, Accept-Encoding, Accept-Language, User-Agent, Transfer-Encoding, Pragma, Expect etc.</para>
|
|
/// </summary>
|
|
public Dictionary<String, List<String>> RequestHeaders { get; } = new Dictionary<String, List<String>>();
|
|
/// <summary>
|
|
/// Content Headers to send with request.
|
|
/// <para>Content-Length (bytes), Content-Type (media type), Content-Encoding (compression algorithm), Content-Language etc.</para>
|
|
/// <para>Content-Type header is defaulted for POST request if not specified.</para>
|
|
/// </summary>
|
|
public Dictionary<String, List<String>> ContentHeaders { get; } = new Dictionary<String, List<String>>();
|
|
/// <summary>
|
|
/// Form values to post as 'URL Encoded'.
|
|
/// <para>Content-Type defaults to 'application/x-www-form-urlencoded; charset=utf-8'.</para>
|
|
/// </summary>
|
|
public List<KeyValuePair<string, string>> FormData { get; } = new List<KeyValuePair<string, string>>();
|
|
/// <summary>
|
|
/// Data to POST. Ignored if FormData populated.
|
|
/// <para>Content-Type defaults to 'text/plain; charset=utf-8'.</para>
|
|
/// </summary>
|
|
public String PostData { get; set; }
|
|
/// <summary>Use POST request rather than GET even if no data to post.</summary>
|
|
public bool ForcePost { get; set; }
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Response data</summary>
|
|
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<ResponseData> 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<String>(header.Value);
|
|
}
|
|
if (response.Content != null && response.Content.Headers != null)
|
|
{
|
|
foreach (var header in response.Content.Headers)
|
|
responseData.ContentHeaders[header.Key] = new List<String>(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 = ExceptionUtil.GetErrorStateMessage(exc);
|
|
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);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
}
|