Base.Net classes & Accounts module added
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
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.Net
|
||||
{
|
||||
//=======================================================================
|
||||
public class SacsHttpRequestData : SacsTaskData
|
||||
{
|
||||
public SacsHttpRequestData (CancellationToken? cancellationToken) : base(cancellationToken) { }
|
||||
/// <summary>URL to send request to.</summary>
|
||||
public String Url { get; set; }
|
||||
/// <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; }
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
public class SacsHttpResponseData
|
||||
{
|
||||
public DateTime RequestStarted { get; set; }
|
||||
public DateTime RequestCompleted { get; set; }
|
||||
/// <summary>Response headers.</summary>
|
||||
public Dictionary<String, IEnumerable<String>> ResponseHeaders { get; } = new Dictionary<String, IEnumerable<String>>();
|
||||
/// <summary>Response content headers.</summary>
|
||||
public Dictionary<String, IEnumerable<String>> ContentHeaders { get; } = new Dictionary<String, IEnumerable<String>>();
|
||||
/// <summary>Indicates whether Status Code represents success.</summary>
|
||||
public bool Success { get; set; }
|
||||
/// <summary>HTTP Status Code.</summary>
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
/// <summary>HTTP Reason Code.</summary>
|
||||
public String ReasonPhrase { get; set; } = "";
|
||||
/// <summary>Response data.</summary>
|
||||
public String Data { get; set; } = "";
|
||||
/// <summary>Elapsed time for request.</summary>
|
||||
public TimeSpan Elapsed { get; set; }
|
||||
/// <summary>Exception thrown during handling of request.</summary>
|
||||
public Exception Exception { get; set; }
|
||||
/// <summary>Error state message - generally exception type and message.</summary>
|
||||
public String ErrorStateMessage { get; set; }
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
public static class SacsHttpClient
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
private static readonly HttpClient client;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Static initialisation.
|
||||
static SacsHttpClient ()
|
||||
{
|
||||
// Configure client.
|
||||
client = new HttpClient();
|
||||
client.Timeout = new TimeSpan(0, 0, RequestTimeoutSecs_Default);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public static int RequestTimeoutSecs_Default { get; } = 5;
|
||||
public static String HeaderName_ContentType { get; } = "Content-Type";
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public static async Task<SacsHttpResponseData> DoRequest (SacsHttpRequestData requestData)
|
||||
{
|
||||
String fn = "SacsHttpClient.DoRequest";
|
||||
SacsHttpResponseData responseData = new SacsHttpResponseData();
|
||||
requestData.ThrowIfCancellationRequested();
|
||||
|
||||
// Construct content to post.
|
||||
HttpContent postContent = null;
|
||||
if (requestData.FormData.Count > 0)
|
||||
{
|
||||
postContent = new FormUrlEncodedContent(requestData.FormData);
|
||||
if (!requestData.ContentHeaders.ContainsKey(HeaderName_ContentType))
|
||||
requestData.ContentHeaders.Add(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(HeaderName_ContentType))
|
||||
requestData.ContentHeaders.Add(HeaderName_ContentType,"text/plain; charset=utf-8");
|
||||
}
|
||||
requestData.ThrowIfCancellationRequested();
|
||||
|
||||
// Make request.
|
||||
DateTime startedAt = DateTime.Now;
|
||||
try
|
||||
{
|
||||
// Request.
|
||||
using (HttpRequestMessage request = new HttpRequestMessage())
|
||||
{
|
||||
request.RequestUri = new Uri(requestData.Url);
|
||||
|
||||
// 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 (client.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 client.SendAsync(request, requestData.CancellationToken);
|
||||
responseData.RequestCompleted = DateTime.Now;
|
||||
}
|
||||
else
|
||||
{
|
||||
// GET.
|
||||
request.Method = HttpMethod.Get;
|
||||
responseData.RequestStarted = DateTime.Now;
|
||||
response = await client.SendAsync(request, requestData.CancellationToken);
|
||||
responseData.RequestCompleted = DateTime.Now;
|
||||
}
|
||||
|
||||
// Extract resopnse data.
|
||||
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 = exc.GetType().Name + ": " + exc.Message;
|
||||
SaLogger.Warn(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)
|
||||
{
|
||||
// Ref. https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
switch (httpStatus)
|
||||
{
|
||||
// 1xx: Informational - Request received, continuing process.
|
||||
case HttpStatusCode.Continue: return "Continue";
|
||||
// 2xx: Success - The action was successfully received, understood, and accepted.
|
||||
case HttpStatusCode.OK: return "OK";
|
||||
case HttpStatusCode.Created: return "Created";
|
||||
case HttpStatusCode.Accepted: return "Accepted";
|
||||
// 3xx: Redirection - Further action must be taken in order to complete the request.
|
||||
case HttpStatusCode.MultipleChoices: return "Multiple choices";
|
||||
case HttpStatusCode.MovedPermanently: return "Moved permanently";
|
||||
case HttpStatusCode.Found: return "Found";
|
||||
// 4xx: Client Error -The request contains bad syntax or cannot be fulfilled.
|
||||
case HttpStatusCode.BadRequest: return "Bad request";
|
||||
case HttpStatusCode.Unauthorized: return "Unauthorized";
|
||||
case HttpStatusCode.PaymentRequired: return "Payment required";
|
||||
case HttpStatusCode.Forbidden: return "Forbidden";
|
||||
case HttpStatusCode.NotFound: return "Not found";
|
||||
case HttpStatusCode.MethodNotAllowed: return "Method not allowed";
|
||||
case HttpStatusCode.NotAcceptable: return "Not acceptable";
|
||||
// 5xx: Server Error -The server failed to fulfill an apparently valid request.
|
||||
case HttpStatusCode.InternalServerError: return "Internal server error";
|
||||
}
|
||||
return httpStatus.ToString();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace Sarand.Net.Standard.Base.Net
|
||||
{
|
||||
public static class WebUtil
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// H T M L E n c o d i n g
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Converts a string to an HTML-encoded string.</summary>
|
||||
public static String HtmlEncode (this String value)
|
||||
{
|
||||
if (StringUtil.IsEmpty(value)) return value;
|
||||
return WebUtility.HtmlEncode(value);
|
||||
}
|
||||
/// <summary>Converts a string that has been HTML-encoded for HTTP transmission into a decoded string.</summary>
|
||||
public static String HtmlDecode (this String value)
|
||||
{
|
||||
if (StringUtil.IsEmpty(value)) return value;
|
||||
return WebUtility.HtmlDecode(value);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// U R L E n c o d i n g
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Converts a string to an HTML-encoded string.</summary>
|
||||
public static String UrlEncode (this String value)
|
||||
{
|
||||
if (StringUtil.IsEmpty(value)) return value;
|
||||
return WebUtility.UrlEncode(value);
|
||||
}
|
||||
/// <summary>Converts a string that has been HTML-encoded for HTTP transmission into a decoded string.</summary>
|
||||
public static String UrlDecode (this String value)
|
||||
{
|
||||
if (StringUtil.IsEmpty(value)) return value;
|
||||
return WebUtility.UrlDecode(value);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user