refactor csproj and add XBase Identity Http provider tools and also add supports for streaming ...
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using xCommons.Extensions;
|
||||
|
||||
namespace xIdentityService.Extensions
|
||||
{
|
||||
public static partial class StreamExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an Stream to Async Enumerable ...
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="bufferSize"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async IAsyncEnumerable<byte[]> ToAsyncEnumerable(
|
||||
this Stream stream,
|
||||
int bufferSize = 8192,
|
||||
[System.Runtime.CompilerServices.EnumeratorCancellation]
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
//
|
||||
var buffer = new byte[bufferSize];
|
||||
while (true)
|
||||
{
|
||||
//
|
||||
int read = await stream
|
||||
.ReadAsync(
|
||||
buffer,
|
||||
0,
|
||||
buffer.Length,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
//
|
||||
if (read == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
//
|
||||
yield return buffer.Take(read).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an Stream to Async Enumerable ...
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="bufferSize"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async IAsyncEnumerable<T> ToAsyncEnumerable<T>(
|
||||
this Stream stream,
|
||||
int bufferSize = 8192,
|
||||
[System.Runtime.CompilerServices.EnumeratorCancellation]
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
//
|
||||
var buffer = new byte[bufferSize];
|
||||
while (true)
|
||||
{
|
||||
//
|
||||
int read = await stream
|
||||
.ReadAsync(
|
||||
buffer,
|
||||
0,
|
||||
buffer.Length,
|
||||
cancellationToken
|
||||
);
|
||||
if (read == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
//
|
||||
T chunk = default(T);
|
||||
var readedBuffer = buffer.Take(read).ToArray();
|
||||
var str = readedBuffer.FromBytes();
|
||||
if (str.IsNullOrEmpty())
|
||||
{
|
||||
yield return chunk;
|
||||
}
|
||||
try
|
||||
{
|
||||
chunk = str.FromJSON<T>();
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
|
||||
//
|
||||
yield return chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RestSharp;
|
||||
using xCommons.Providers;
|
||||
using xHttpService.Constants;
|
||||
using xIdentityModels.Constants;
|
||||
using xIdentityModels.Models;
|
||||
using xIdentityService.Models;
|
||||
|
||||
namespace xIdentityService.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// an interface for Identity Provided Http Based Services ...
|
||||
/// </summary>
|
||||
public interface IXBaseIdentityHttpProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Client Base Url ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
string BaseUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Logger ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
ILogger Logger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Powered Value ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
string XPoweredValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Default Client Timeout ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
int DefaultClientTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Default Buffering Chunk Size ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
int DefaultBufferingChunckSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Validation Provider ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
XValidationProvider ValidationProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get an Instance of Http Client
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
HttpClient GetHttpClient(XTokenResponse tokens = null);
|
||||
HttpClient GetHttpClient(XUserClaimsInfoDto userInfo = null);
|
||||
|
||||
/// <summary>
|
||||
/// Get an Instance of RestClient
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
RestClient GetRestClient(XTokenResponse tokens = null);
|
||||
RestClient GetRestClient(XUserClaimsInfoDto userInfo = null);
|
||||
|
||||
/// <summary>
|
||||
/// Get an Instance of RestRequest
|
||||
/// </summary>
|
||||
/// <param name="endpoint"></param>
|
||||
/// <param name="addXPoweredValue"></param>
|
||||
/// <param name=""></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
RestRequest GetRestRequet(
|
||||
Enum endpoint,
|
||||
bool addXPoweredValue = true,
|
||||
IDictionary<string, string> @params = null,
|
||||
IDictionary<string, string> headers = null,
|
||||
IDictionary<string, string> queryStrings = null
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Handle Call Specific Request and retrieve Response
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="supportRefreshingTokens"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
Task<T> RunRestRequest<T>(
|
||||
RestClient client,
|
||||
RestRequest request,
|
||||
XHttpMethod method,
|
||||
bool supportRefreshingTokens = true,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Access a Get Request Stream as AsyncEnumerable ...
|
||||
/// </summary>
|
||||
/// <param name="endpoint"></param>
|
||||
/// <param name="addXPoweredValue"></param>
|
||||
/// <param name="tokens"></param>
|
||||
/// <param name="@params"></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
IAsyncEnumerable<T> StreamData<T>(
|
||||
Enum endpoint,
|
||||
bool addXPoweredValue = true,
|
||||
XTokenResponse tokens = null,
|
||||
IDictionary<string, string> @params = null,
|
||||
IDictionary<string, string> headers = null,
|
||||
IDictionary<string, string> queryStrings = null,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Handle Call Specified Request and Retrieve XBaseRestResponse<T> ...
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="supportRefreshingTokens"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>an instance of XBaseRestResponse<T></returns>
|
||||
Task<XBaseRestResponse<T>> RunRestRequestByResponse<T>(
|
||||
RestClient client,
|
||||
RestRequest request,
|
||||
XHttpMethod method,
|
||||
bool supportRefreshingTokens = true,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Get User SelectBy Param
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="forceNotNull"></param>
|
||||
/// <param name="excludes"></param>
|
||||
/// <returns></returns>
|
||||
string GetUserSelectByParam(
|
||||
XActionRequest model,
|
||||
bool forceNotNull = true,
|
||||
ICollection<XUserSelectBy> excludes = null
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Converts UserInfo to TokenResponse model ...
|
||||
/// </summary>
|
||||
/// <param name="userInfo"></param>
|
||||
/// <returns></returns>
|
||||
XTokenResponse ToTokenResponse(XUserClaimsInfoDto userInfo = null);
|
||||
|
||||
/// <summary>
|
||||
/// Prepare Url Address of Specified Enum by Filling Params
|
||||
/// and Query Strings Attach ...
|
||||
/// </summary>
|
||||
/// <param name="endpoint"></param>
|
||||
/// <param name="@params"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
string PrepareUrl(
|
||||
Enum endpoint,
|
||||
IDictionary<string, string> @params = null,
|
||||
IDictionary<string, string> queryStrings = null
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Prepare Url Address by Filling Params
|
||||
/// and Query Strings Attach ...
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="@params"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
string PrepareUrl(
|
||||
string url,
|
||||
IDictionary<string, string> @params = null,
|
||||
IDictionary<string, string> queryStrings = null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,871 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RestSharp;
|
||||
using RestSharp.Authenticators;
|
||||
using xCommons.Constants;
|
||||
using xCommons.Extensions;
|
||||
using xCommons.Providers;
|
||||
using xExceptions.Constants;
|
||||
using xHttpService.Constants;
|
||||
using xIdentityModels.Constants;
|
||||
using xIdentityModels.Extensions;
|
||||
using xIdentityModels.Models;
|
||||
using xIdentityService.Constants;
|
||||
using xIdentityService.Interfaces;
|
||||
using xIdentityService.Models;
|
||||
using xIdentityService.Extensions;
|
||||
|
||||
namespace xIdentityService.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// an abstraction Implementation for Identity Provided Http Based Services ...
|
||||
/// </summary>
|
||||
public abstract class XBaseIdentityHttpProvider : IXBaseIdentityHttpProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Client Base Url ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string BaseUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Logger ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public ILogger Logger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Powered Value ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string XPoweredValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Default Client Timeout ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public int DefaultClientTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Default Buffering Chunk Size ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public int DefaultBufferingChunckSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Validation Provider ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public XValidationProvider ValidationProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Token Provider ...
|
||||
/// </summary>
|
||||
private readonly IXTokenProvider tokenProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Identity Provider ...
|
||||
/// </summary>
|
||||
private readonly IXIdentityProvider identityProvider;
|
||||
|
||||
public XBaseIdentityHttpProvider(
|
||||
string baseUrl,
|
||||
ILogger logger,
|
||||
string poweredValue,
|
||||
int defaultClientTimeout,
|
||||
IXTokenProvider tokenProvider,
|
||||
IXIdentityProvider identityProvider,
|
||||
XValidationProvider validationProvider,
|
||||
int defaultBufferingChunckSize = 8192
|
||||
)
|
||||
{
|
||||
//
|
||||
Logger = logger;
|
||||
BaseUrl = baseUrl;
|
||||
XPoweredValue = poweredValue;
|
||||
this.tokenProvider = tokenProvider;
|
||||
ValidationProvider = validationProvider;
|
||||
this.identityProvider = identityProvider;
|
||||
DefaultClientTimeout = defaultClientTimeout;
|
||||
DefaultBufferingChunckSize = defaultBufferingChunckSize;
|
||||
}
|
||||
|
||||
//
|
||||
#region Tools ...
|
||||
/// <summary>
|
||||
/// Get an Instance of Http Client
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public HttpClient GetHttpClient(XTokenResponse tokens = null)
|
||||
{
|
||||
//
|
||||
HttpClient httpClient = null;
|
||||
|
||||
//
|
||||
// httpClient = new HttpClient ();
|
||||
var httpClientHandler = new HttpClientHandler()
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
|
||||
{
|
||||
//
|
||||
Logger.LogInformation(
|
||||
$"XSSL Handler: {Environment.NewLine}, sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}"
|
||||
);
|
||||
|
||||
//
|
||||
return true;
|
||||
},
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual,
|
||||
};
|
||||
httpClient = new HttpClient(httpClientHandler);
|
||||
|
||||
//
|
||||
// Set Authenticator ...
|
||||
if (!tokens.IsNull() &&
|
||||
!tokens.AccessToken.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
httpClient.DefaultRequestHeaders
|
||||
.Add(
|
||||
XAuthorization.AccessToken,
|
||||
$"{XAuthentication.BEARER} {tokens.AccessToken}");
|
||||
}
|
||||
|
||||
//
|
||||
// Refresh Token ...
|
||||
if (!tokens.IsNull() &&
|
||||
!tokens.RefreshToken.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
httpClient.DefaultRequestHeaders
|
||||
.Add(
|
||||
XAuthorization.RefreshToken,
|
||||
tokens.RefreshToken);
|
||||
}
|
||||
|
||||
//
|
||||
// ExpiresAt ...
|
||||
if (!tokens.IsNull() &&
|
||||
tokens.ExpiresAt > 0)
|
||||
{
|
||||
//
|
||||
httpClient.DefaultRequestHeaders
|
||||
.Add(
|
||||
XAuthorization.ExpiresAt,
|
||||
tokens.ExpiresAt.AsHttpParamString());
|
||||
}
|
||||
|
||||
//
|
||||
// Set Default Timeout ...
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(DefaultClientTimeout);
|
||||
|
||||
//
|
||||
return httpClient;
|
||||
}
|
||||
public HttpClient GetHttpClient(XUserClaimsInfoDto userInfo = null)
|
||||
{
|
||||
//
|
||||
XTokenResponse tokens = ToTokenResponse(userInfo);
|
||||
|
||||
//
|
||||
return GetHttpClient(tokens);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an Instance of RestClient
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public RestClient GetRestClient(XTokenResponse tokens = null)
|
||||
{
|
||||
//
|
||||
var client = new RestClient(BaseUrl);
|
||||
|
||||
//
|
||||
// Set Authenticator ...
|
||||
if (!tokens.IsNull() &&
|
||||
!tokens.AccessToken.IsNullOrEmpty())
|
||||
{
|
||||
client.Authenticator = new JwtAuthenticator(tokens.AccessToken);
|
||||
}
|
||||
|
||||
//
|
||||
// Refresh Token ...
|
||||
if (!tokens.IsNull() &&
|
||||
!tokens.RefreshToken.IsNullOrEmpty())
|
||||
{
|
||||
client.AddDefaultHeader(XAuthorization.RefreshToken, tokens.RefreshToken);
|
||||
}
|
||||
|
||||
//
|
||||
// ExpiresAt ...
|
||||
if (!tokens.IsNull() &&
|
||||
tokens.ExpiresAt > 0)
|
||||
{
|
||||
client.AddDefaultHeader(XAuthorization.ExpiresAt, tokens.ExpiresAt.AsHttpParamString());
|
||||
}
|
||||
|
||||
//
|
||||
// Set Default Timeout ...
|
||||
client.Timeout = DefaultClientTimeout;
|
||||
|
||||
//
|
||||
// SSL Validation Handler ...
|
||||
client.RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
|
||||
{
|
||||
//
|
||||
Logger.LogInformation(
|
||||
$"XSSL Handler: {Environment.NewLine}, sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}"
|
||||
);
|
||||
|
||||
//
|
||||
return true;
|
||||
};
|
||||
|
||||
//
|
||||
return client;
|
||||
}
|
||||
public RestClient GetRestClient(XUserClaimsInfoDto userInfo = null)
|
||||
{
|
||||
//
|
||||
XTokenResponse tokens = ToTokenResponse(userInfo);
|
||||
|
||||
//
|
||||
return GetRestClient(tokens);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an Instance of RestRequest
|
||||
/// </summary>
|
||||
/// <param name="endpoint"></param>
|
||||
/// <param name="addXPoweredValue"></param>
|
||||
/// <param name=""></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
public RestRequest GetRestRequet(
|
||||
Enum endpoint,
|
||||
bool addXPoweredValue = true,
|
||||
IDictionary<string, string> @params = null,
|
||||
IDictionary<string, string> headers = null,
|
||||
IDictionary<string, string> queryStrings = null
|
||||
)
|
||||
{
|
||||
//
|
||||
var url = PrepareUrl(
|
||||
endpoint,
|
||||
@params,
|
||||
queryStrings
|
||||
);
|
||||
|
||||
//
|
||||
// Create request ...
|
||||
var request = new RestRequest(url, DataFormat.Json);
|
||||
|
||||
//
|
||||
if (addXPoweredValue)
|
||||
{
|
||||
request.AddHeader(XAuthorization.XPoweredBy, XPoweredValue);
|
||||
}
|
||||
|
||||
//
|
||||
// Add Headers to request ...
|
||||
if (!headers.IsNull() && headers.Count > 0)
|
||||
{
|
||||
//
|
||||
var enumerator = headers.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
//
|
||||
var item = enumerator.Current;
|
||||
request.AddHeader(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return request;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle Call Specific Request and retrieve Response
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="supportRefreshingTokens"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public async Task<T> RunRestRequest<T>(
|
||||
RestClient client,
|
||||
RestRequest request,
|
||||
XHttpMethod method,
|
||||
bool supportRefreshingTokens = true,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
//
|
||||
var response = await Task.Run(async () =>
|
||||
{
|
||||
//
|
||||
IRestResponse resp = null;
|
||||
|
||||
//
|
||||
// Call Endpoint Service using Specified Method ...
|
||||
switch (method)
|
||||
{
|
||||
//
|
||||
// Get ...
|
||||
case XHttpMethod.GET:
|
||||
resp = client.Get(request);
|
||||
break;
|
||||
|
||||
//
|
||||
// Put ...
|
||||
case XHttpMethod.PUT:
|
||||
resp = client.Put(request);
|
||||
break;
|
||||
|
||||
//
|
||||
// Post ...
|
||||
case XHttpMethod.POST:
|
||||
resp = client.Post(request);
|
||||
break;
|
||||
|
||||
//
|
||||
// Delete ...
|
||||
case XHttpMethod.DELETE:
|
||||
resp = client.Delete(request);
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
if (!resp.IsSuccessful)
|
||||
{
|
||||
//
|
||||
// Log Exception ...
|
||||
Logger.LogError($"Error: {resp.ErrorMessage}");
|
||||
Logger.LogError($"Exception: {resp.ErrorException.ToJSON()}");
|
||||
if (!resp.ErrorMessage.IsNullOrEmpty() &&
|
||||
resp.ErrorMessage.Contains("timed out"))
|
||||
{
|
||||
XException.Timeout.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Check UnAuthorized Status here for
|
||||
// Refresh Tokens or Issue Correct Exception ...
|
||||
if (supportRefreshingTokens &&
|
||||
resp.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||
{
|
||||
//
|
||||
var isRefreshed = request.Parameters.Any(rp =>
|
||||
rp.Name == XParam.XIsRefreshed.GetStringValue() &&
|
||||
rp.Value.ToString().ToNormalString() == true.ToJSON());
|
||||
|
||||
//
|
||||
Logger.LogInformation($"isRefreshed: {isRefreshed}");
|
||||
|
||||
//
|
||||
// Get Access Token ...
|
||||
XTokenResponse tokens = null;
|
||||
var accessToken = request.Parameters.FirstOrDefault(rp =>
|
||||
rp.Name.ToNormalString() == XAuthorization.Header.ToNormalString()).Value.ToString();
|
||||
if (!accessToken.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
accessToken = accessToken.Replace(XAuthorization.TokenIdentifier, "");
|
||||
|
||||
//
|
||||
if (!accessToken.IsNullOrEmpty())
|
||||
{
|
||||
tokens = await tokenProvider.RetrieveToken(accessToken);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Refresh Token and then re do request ...
|
||||
if (!isRefreshed &&
|
||||
!accessToken.IsNullOrEmpty() &&
|
||||
!tokens.IsNull() &&
|
||||
tokens.IsRefreshable())
|
||||
{
|
||||
//
|
||||
request.AddHeader(XParam.XIsRefreshed.GetStringValue(), true.ToJSON());
|
||||
|
||||
//
|
||||
// Refreshe Tokens ...
|
||||
var refreshedTokens = await identityProvider
|
||||
.RefreshTokens(tokens);
|
||||
|
||||
//
|
||||
// Renew Rest Client with Refreshed Tokens ...
|
||||
var timeout = client.Timeout;
|
||||
client = GetRestClient(refreshedTokens);
|
||||
client.Timeout = Timeout.Infinite;
|
||||
|
||||
//
|
||||
// Do Request Again using renew Client ...
|
||||
return await RunRestRequest<T>(
|
||||
client: client,
|
||||
request: request,
|
||||
method: method);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
var ex = resp.Content.ToXError();
|
||||
if (!ex.IsNull())
|
||||
{
|
||||
throw ex.ToException();
|
||||
}
|
||||
|
||||
//
|
||||
// UnAuthorized ...
|
||||
if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||
{
|
||||
XException.NotAuthorized.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// NotFound ...
|
||||
if (resp.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
{
|
||||
XException.NotFound.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var result = resp.Content.FromJSON<T>();
|
||||
return result;
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
//
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Access a Get Request Stream as AsyncEnumerable ...
|
||||
/// </summary>
|
||||
/// <param name="endpoint"></param>
|
||||
/// <param name="addXPoweredValue"></param>
|
||||
/// <param name="tokens"></param>
|
||||
/// <param name="@params"></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public async IAsyncEnumerable<T> StreamData<T>(
|
||||
Enum endpoint,
|
||||
bool addXPoweredValue = true,
|
||||
XTokenResponse tokens = null,
|
||||
IDictionary<string, string> @params = null,
|
||||
IDictionary<string, string> headers = null,
|
||||
IDictionary<string, string> queryStrings = null,
|
||||
[System.Runtime.CompilerServices.EnumeratorCancellation]
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
//
|
||||
var url = PrepareUrl(
|
||||
endpoint,
|
||||
@params,
|
||||
queryStrings
|
||||
);
|
||||
var client = GetHttpClient(tokens);
|
||||
if (addXPoweredValue)
|
||||
{
|
||||
//
|
||||
client.DefaultRequestHeaders
|
||||
.Add(
|
||||
XAuthorization.XPoweredBy,
|
||||
XPoweredValue);
|
||||
}
|
||||
|
||||
//
|
||||
// For SSE, you might want to set the Accept header, though not strictly necessary for just streaming
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Accept
|
||||
.Add(
|
||||
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("text/event-stream"));
|
||||
|
||||
//
|
||||
using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
|
||||
{
|
||||
//
|
||||
response.EnsureSuccessStatusCode();
|
||||
var stream = await response.Content.ReadAsStreamAsync();
|
||||
await foreach (var item in stream.ToAsyncEnumerable<T>(cancellationToken: cancellationToken))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle Call Specified Request and Retrieve XBaseRestResponse<T> ...
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="supportRefreshingTokens"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>an instance of XBaseRestResponse<T></returns>
|
||||
public async Task<XBaseRestResponse<T>> RunRestRequestByResponse<T>(
|
||||
RestClient client,
|
||||
RestRequest request,
|
||||
XHttpMethod method,
|
||||
bool supportRefreshingTokens = true,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
//
|
||||
var response = await Task.Run(async () =>
|
||||
{
|
||||
//
|
||||
IRestResponse resp = null;
|
||||
|
||||
//
|
||||
// Call Endpoint Service using Specified Method ...
|
||||
switch (method)
|
||||
{
|
||||
//
|
||||
// Get ...
|
||||
case XHttpMethod.GET:
|
||||
resp = client.Get(request);
|
||||
break;
|
||||
|
||||
//
|
||||
// Put ...
|
||||
case XHttpMethod.PUT:
|
||||
resp = client.Put(request);
|
||||
break;
|
||||
|
||||
//
|
||||
// Post ...
|
||||
case XHttpMethod.POST:
|
||||
resp = client.Post(request);
|
||||
break;
|
||||
|
||||
//
|
||||
// Delete ...
|
||||
case XHttpMethod.DELETE:
|
||||
resp = client.Delete(request);
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
if (!resp.IsSuccessful)
|
||||
{
|
||||
//
|
||||
// Log Exception ...
|
||||
Logger.LogError($"Error: {resp.ErrorMessage}");
|
||||
Logger.LogError($"Exception: {resp.ErrorException.ToJSON()}");
|
||||
if (!resp.ErrorMessage.IsNullOrEmpty() &&
|
||||
resp.ErrorMessage.Contains("timed out"))
|
||||
{
|
||||
XException.Timeout.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Check UnAuthorized Status here for
|
||||
// Refresh Tokens or Issue Correct Exception ...
|
||||
if (supportRefreshingTokens &&
|
||||
resp.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||
{
|
||||
//
|
||||
var isRefreshed = request.Parameters.Any(rp =>
|
||||
rp.Name == XParam.XIsRefreshed.GetStringValue() &&
|
||||
rp.Value.ToString().ToNormalString() == true.ToJSON());
|
||||
|
||||
//
|
||||
Logger.LogInformation($"isRefreshed: {isRefreshed}");
|
||||
|
||||
//
|
||||
// Get Access Token ...
|
||||
XTokenResponse tokens = null;
|
||||
var accessToken = request.Parameters.FirstOrDefault(rp =>
|
||||
rp.Name.ToNormalString() == XAuthorization.Header.ToNormalString()).Value.ToString();
|
||||
if (!accessToken.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
accessToken = accessToken.Replace(XAuthorization.TokenIdentifier, "");
|
||||
|
||||
//
|
||||
if (!accessToken.IsNullOrEmpty())
|
||||
{
|
||||
tokens = await tokenProvider.RetrieveToken(accessToken);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Refresh Token and then re do request ...
|
||||
if (!isRefreshed &&
|
||||
!accessToken.IsNullOrEmpty() &&
|
||||
!tokens.IsNull() &&
|
||||
tokens.IsRefreshable())
|
||||
{
|
||||
//
|
||||
request.AddHeader(XParam.XIsRefreshed.GetStringValue(), true.ToJSON());
|
||||
|
||||
//
|
||||
// Refreshe Tokens ...
|
||||
var refreshedTokens = await identityProvider
|
||||
.RefreshTokens(tokens);
|
||||
|
||||
//
|
||||
// Renew Rest Client with Refreshed Tokens ...
|
||||
var timeout = client.Timeout;
|
||||
client = GetRestClient(refreshedTokens);
|
||||
client.Timeout = Timeout.Infinite;
|
||||
|
||||
//
|
||||
// Do Request Again using renew Client ...
|
||||
return await RunRestRequestByResponse<T>(
|
||||
client: client,
|
||||
request: request,
|
||||
method: method
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
var ex = resp.Content.ToXError();
|
||||
if (!ex.IsNull())
|
||||
{
|
||||
throw ex.ToException();
|
||||
}
|
||||
|
||||
//
|
||||
// UnAuthorized ...
|
||||
if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||
{
|
||||
XException.NotAuthorized.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// NotFound ...
|
||||
if (resp.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
{
|
||||
XException.NotFound.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var result = new XBaseRestResponse<T>
|
||||
{
|
||||
Response = resp,
|
||||
Model = resp.Content.FromJSON<T>()
|
||||
};
|
||||
|
||||
//
|
||||
return result;
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
//
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get User SelectBy Param
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="forceNotNull"></param>
|
||||
/// <param name="excludes"></param>
|
||||
/// <returns></returns>
|
||||
public string GetUserSelectByParam(
|
||||
XActionRequest model,
|
||||
bool forceNotNull = true,
|
||||
ICollection<XUserSelectBy> excludes = null
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull(model);
|
||||
|
||||
//
|
||||
var id = model.UserId;
|
||||
var userName = model.UserName;
|
||||
var email = model.Email;
|
||||
var phoneNumber = model.MobileNumber;
|
||||
|
||||
//
|
||||
if (excludes != null)
|
||||
{
|
||||
//
|
||||
if (excludes.Contains(XUserSelectBy.ID))
|
||||
{
|
||||
id = null;
|
||||
}
|
||||
if (excludes.Contains(XUserSelectBy.Email))
|
||||
{
|
||||
email = null;
|
||||
}
|
||||
if (excludes.Contains(XUserSelectBy.MobileNumber))
|
||||
{
|
||||
phoneNumber = null;
|
||||
}
|
||||
if (excludes.Contains(XUserSelectBy.Username))
|
||||
{
|
||||
userName = null;
|
||||
}
|
||||
}
|
||||
|
||||
var selectByParam =
|
||||
id.IsNullOrEmpty() ?
|
||||
userName.IsNullOrEmpty() ?
|
||||
email.IsNullOrEmpty() ?
|
||||
phoneNumber.IsNullOrEmpty() ? null : phoneNumber : email : userName : id;
|
||||
|
||||
//
|
||||
// Check result ...
|
||||
if (forceNotNull &&
|
||||
selectByParam.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
return selectByParam;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepare Url Address of Specified Enum by Filling Params
|
||||
/// and Query Strings Attach ...
|
||||
/// </summary>
|
||||
/// <param name="endpoint"></param>
|
||||
/// <param name="@params"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
public string PrepareUrl(
|
||||
Enum endpoint,
|
||||
IDictionary<string, string> @params = null,
|
||||
IDictionary<string, string> queryStrings = null
|
||||
)
|
||||
{
|
||||
//
|
||||
// Retrieving Url Template ...
|
||||
var url = endpoint.GetStringValue();
|
||||
|
||||
//
|
||||
// Filling Parmas and Query Strings of Url ...
|
||||
url = PrepareUrl(
|
||||
url,
|
||||
@params,
|
||||
queryStrings
|
||||
);
|
||||
|
||||
//
|
||||
return url;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepare Url Address by Filling Params
|
||||
/// and Query Strings Attach ...
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="@params"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
public string PrepareUrl(
|
||||
string url,
|
||||
IDictionary<string, string> @params = null,
|
||||
IDictionary<string, string> queryStrings = null
|
||||
)
|
||||
{
|
||||
//
|
||||
// Add Route Payloads ...
|
||||
if (!@params.IsNull() && @params.Count > 0)
|
||||
{
|
||||
//
|
||||
var enumerator = @params.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
//
|
||||
var item = enumerator.Current;
|
||||
|
||||
//
|
||||
url = url.Replace(item.Key, item.Value.ToUrlEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Add Query Strings ...
|
||||
if (!queryStrings.IsNull() && queryStrings.Count > 0)
|
||||
{
|
||||
//
|
||||
url = $"{url}?";
|
||||
var enumerator = queryStrings.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
//
|
||||
var item = enumerator.Current;
|
||||
|
||||
//
|
||||
url += $"{item.Key}={item.Value.ToUrlEncoded()}&";
|
||||
}
|
||||
|
||||
//
|
||||
// remove lates & ...
|
||||
if (url.EndsWith("&"))
|
||||
{
|
||||
url = url.Substring(0, url.Length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return url;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts UserInfo to TokenResponse model ...
|
||||
/// </summary>
|
||||
/// <param name="userInfo"></param>
|
||||
/// <returns></returns>
|
||||
public XTokenResponse ToTokenResponse(XUserClaimsInfoDto userInfo = null)
|
||||
{
|
||||
//
|
||||
XTokenResponse result = null;
|
||||
|
||||
//
|
||||
if (!userInfo.IsNull())
|
||||
{
|
||||
//
|
||||
result = new XTokenResponse
|
||||
{
|
||||
AccessToken = userInfo.AccessToken.IsNullOrEmpty()
|
||||
? ""
|
||||
: userInfo.AccessToken,
|
||||
RefreshToken = userInfo.RefreshToken.IsNullOrEmpty()
|
||||
? ""
|
||||
: userInfo.RefreshToken,
|
||||
ExpiresAt = userInfo.ExpiresAt,
|
||||
};
|
||||
|
||||
//
|
||||
if (userInfo.AccessToken.IsNullOrEmpty())
|
||||
{
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- Runtime Definition -->
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<PackageId>xDashboard.xIdentityService</PackageId>
|
||||
<Version>1.0.0</Version>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<Authors>Hadi Khazaee Asl</Authors>
|
||||
<Company>SaherElm IT Center</Company>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<PackageId>xDashboard.xIdentityService</PackageId>
|
||||
<Description>
|
||||
provide an interface to connect xDashboard IdentityServer and do OAuth Actions to xDashboard
|
||||
project.
|
||||
@@ -23,11 +23,12 @@
|
||||
|
||||
<!-- Local Modules -->
|
||||
<ItemGroup>
|
||||
<!-- <PackageReference Include="xDashboard.xHttpService" Version="1.0.0" /> -->
|
||||
<!-- <PackageReference Include="xDashboard.xDataService" Version="1.0.0" /> -->
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectReference Include="../xDataService/xDataService.csproj" />
|
||||
<ProjectReference Include="../xHttpService/xHttpService.csproj" />
|
||||
<!-- Local Dependencies -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../xDataService/xDataService.csproj" />
|
||||
<ProjectReference Include="../xHttpService/xHttpService.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Dependencies -->
|
||||
@@ -35,5 +36,4 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="IdentityModel.AspNetCore.OAuth2Introspection" Version="4.0.0-preview.6" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user