XFEExtension.NetCore.ServerInteractive
[DLL] Server interaction extension, including user identity verification and querying in conjunction with AutoConfig
关注
0
Fork
0
Star
0
using System.Net;
using System.Text.Json;
using System.Text.RegularExpressions;
using XFEExtension.NetCore.AutoImplement;
using XFEExtension.NetCore.DelegateExtension;
using XFEExtension.NetCore.ServerInteractive.Exceptions;
using XFEExtension.NetCore.ServerInteractive.Interfaces.Requester;
using XFEExtension.NetCore.ServerInteractive.Models.RequesterModels;
using XFEExtension.NetCore.ServerInteractive.Utilities.Helpers;
using XFEExtension.NetCore.ServerInteractive.Utilities.JsonConverter;
namespace XFEExtension.NetCore.ServerInteractive.Utilities.Requester;
/// <summary>
/// 客户端请求器
/// </summary>
[CreateImpl]
public abstract class ClientRequester : IRequesterBase
{
private readonly JsonSerializerOptions _jsonSerializerOptions = new();
internal Dictionary<string, Func<IRequestService>> RequestServiceDictionary = [];
internal Dictionary<string, Func<IStandardRequestService>> StandardRequestServiceDictionary = [];
internal List<(string Pattern, Func<IStandardRequestService> Factory)> WildcardStandardRequestServiceList = [];
internal Dictionary<string, StandardClientInstanceRequest> StandardClientInstanceRequestDictionary = [];
private string _session = string.Empty;
private TableRequester? _tableRequester;
/// <inheritdoc/>
public string RequestAddress { get; set; } = string.Empty;
/// <inheritdoc/>
public string Session
{
get => _session;
set
{
_session = value;
if (TableRequester is not null)
TableRequester.Session = value;
}
}
/// <inheritdoc/>
public string DeviceInfo { get; set; } = string.Empty;
/// <summary>
/// 表格请求器(如需使用表格相关功能,请通过构建器添加WithTableRequester实例并赋值此属性)
/// </summary>
public TableRequester? TableRequester
{
get => _tableRequester;
set
{
_tableRequester = value;
if (_tableRequester is null) return;
if (_tableRequester.RequestAddress.Length == 0)
_tableRequester.RequestAddress = RequestAddress;
if (_tableRequester.DeviceInfo.Length == 0)
_tableRequester.DeviceInfo = DeviceInfo;
if (_tableRequester.Session.Length == 0)
_tableRequester.Session = Session;
}
}
/// <inheritdoc/>
public event XFEEventHandler<object?, ServerInteractiveEventArgs>? MessageReceived;
/// <summary>
/// XFE客户端请求器
/// </summary>
protected ClientRequester()
{
_jsonSerializerOptions.Converters.Add(new JsonDateTimeConverter());
}
/// <summary>
/// 请求
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="serviceName"></param>
/// <param name="parameters"></param>
/// <returns></returns>
/// <exception cref="XFERequesterException"></exception>
public async Task<ClientRequestResult<T>> Request<T>(string serviceName, params object[] parameters)
=> (await RequestAsync(serviceName, CancellationToken.None, parameters).ConfigureAwait(false)).TryConvertTo<T>();
/// <summary>发起支持取消的类型化请求。</summary>
public async Task<ClientRequestResult<T>> RequestAsync<T>(string serviceName, CancellationToken cancellationToken, params object[] parameters)
=> (await RequestAsync(serviceName, cancellationToken, parameters).ConfigureAwait(false)).TryConvertTo<T>();
/// <summary>
/// 请求
/// </summary>
/// <param name="serviceName"></param>
/// <param name="parameters"></param>
/// <returns></returns>
/// <exception cref="XFERequesterException"></exception>
public Task<ClientRequestResult<object>> Request(string serviceName, params object[] parameters)
=> RequestAsync(serviceName, CancellationToken.None, parameters);
/// <summary>发起支持取消的请求。</summary>
public async Task<ClientRequestResult<object>> RequestAsync(string serviceName, CancellationToken cancellationToken, params object[] parameters)
{
var result = new ClientRequestResult<object>();
try
{
if (RequestServiceDictionary.TryGetValue(serviceName, out var serviceFactory))
{
var service = serviceFactory();
service.XFEClientRequester = this;
result = await service.Request<object>(parameters);
}
else if (StandardRequestServiceDictionary.TryGetValue(serviceName, out var xFEServiceFactory))
{
var xFEService = xFEServiceFactory();
xFEService.XFEClientRequester = this;
xFEService.DeviceInfo = DeviceInfo;
xFEService.Parameters = parameters;
// 通过RequestRouteMap解析实际路由路径
var actualRoute = xFEService.RequestRouteMap.GetValueOrDefault(serviceName, serviceName);
xFEService.Route = actualRoute;
if (!xFEService.RequestPoints.TryGetValue(serviceName, out var requestHandler))
throw new XFERequesterException($"未找到'{serviceName}'对应的请求处理方法([Request]标记的方法)");
var (response, code) = await InteractiveHelper.GetServerResponse(InteractiveHelper.BuildRequestUri(RequestAddress, actualRoute), requestHandler(), _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
result.StatusCode = code;
if ((int)code is >= 200 and < 300)
{
xFEService.Response = response;
xFEService.UnescapedResponse = response;
if (xFEService.ResponsePoints.TryGetValue(serviceName, out var responseHandler))
{
result.Result = responseHandler();
}
else
{
result.Result = response;
}
NotifyMessageReceived(new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
}
else
{
result.FailureKind = ClientRequestFailureKind.Protocol;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(response, code));
result.Message = response;
}
}
else if (StandardClientInstanceRequestDictionary.TryGetValue(serviceName, out var instance))
{
var (response, code) = await InteractiveHelper.GetServerResponse(InteractiveHelper.BuildRequestUri(RequestAddress, instance.Route ?? serviceName), instance.ConstructBody(Session, DeviceInfo, parameters), _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
result.StatusCode = code;
if ((int)code is >= 200 and < 300)
{
var requestResult = instance.ProcessResponse?.Invoke(response);
NotifyMessageReceived(new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
result.Result = requestResult ?? new();
}
else
{
result.FailureKind = ClientRequestFailureKind.Protocol;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(response, code));
result.Message = response;
}
}
else if (TryMatchWildcardStandardService(serviceName, out var wildcardFactory, out var matchedPattern))
{
var xFEService = wildcardFactory!();
xFEService.XFEClientRequester = this;
xFEService.DeviceInfo = DeviceInfo;
xFEService.Parameters = parameters;
// 通配符匹配:Route设为实际请求路径,以便服务方法中可通过Route属性查看具体路径
xFEService.Route = serviceName;
if (!xFEService.RequestPoints.TryGetValue(matchedPattern!, out var requestHandler))
throw new XFERequesterException($"未找到'{serviceName}'(通配符模式'{matchedPattern}')对应的请求处理方法([Request]标记的方法)");
var (response, code) = await InteractiveHelper.GetServerResponse(InteractiveHelper.BuildRequestUri(RequestAddress, serviceName), requestHandler(), _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
result.StatusCode = code;
if ((int)code is >= 200 and < 300)
{
xFEService.Response = response;
xFEService.UnescapedResponse = response;
if (xFEService.ResponsePoints.TryGetValue(matchedPattern!, out var responseHandler))
{
result.Result = responseHandler();
}
else
{
result.Result = response;
}
NotifyMessageReceived(new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
}
else
{
result.FailureKind = ClientRequestFailureKind.Protocol;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(response, code));
result.Message = response;
}
}
else
{
throw new InvalidOperationException($"未找到包含指定请求名称:{serviceName} 对应的请求处理服务");
}
return result;
}
catch (OperationCanceledException ex) when (cancellationToken.IsCancellationRequested)
{
result.FailureKind = ClientRequestFailureKind.Cancelled;
result.Exception = ex;
result.Message = "请求已取消";
result.StatusCode = HttpStatusCode.RequestTimeout;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(result.Message, result.StatusCode));
return result;
}
catch (OperationCanceledException ex)
{
result.FailureKind = ClientRequestFailureKind.Timeout;
result.Exception = ex;
result.Message = "请求超时";
result.StatusCode = HttpStatusCode.RequestTimeout;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(result.Message, result.StatusCode));
return result;
}
catch (HttpRequestException ex)
{
result.FailureKind = ClientRequestFailureKind.Transport;
result.Exception = ex;
result.Message = ex.Message;
result.StatusCode = HttpStatusCode.ServiceUnavailable;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(result.Message, result.StatusCode));
return result;
}
catch (JsonException ex)
{
result.FailureKind = ClientRequestFailureKind.Serialization;
result.Exception = ex;
result.Message = ex.Message;
result.StatusCode = HttpStatusCode.UnprocessableEntity;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(result.Message, result.StatusCode));
return result;
}
catch (Exception ex)
{
result.FailureKind = ClientRequestFailureKind.Application;
result.Exception = ex;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(ex.Message, HttpStatusCode.InternalServerError));
result.Message = ex.Message;
result.StatusCode = HttpStatusCode.InternalServerError;
return result;
}
}
private void NotifyMessageReceived(ServerInteractiveEventArgs args)
{
try { MessageReceived?.Invoke(this, args); } catch (Exception) { }
}
/// <summary>
/// 尝试通过通配符模式匹配标准请求服务(选择最具体的匹配模式,与服务端行为一致)
/// </summary>
/// <param name="serviceName">请求路径</param>
/// <param name="factory">匹配到的服务工厂</param>
/// <param name="matchedPattern">匹配到的通配符模式</param>
/// <returns>是否匹配成功</returns>
private bool TryMatchWildcardStandardService(string serviceName, out Func<IStandardRequestService>? factory, out string? matchedPattern)
{
// 在所有命中的候选中选择最具体的模式(字面量段越多越优先),避免结果依赖注册顺序
static int GetWildcardPatternPriority(string pattern)
{
var segments = pattern.Split('/');
var literalSegmentCount = 0;
var wildcardSegmentCount = 0;
foreach (var segment in segments)
{
if (segment == "*")
wildcardSegmentCount++;
else
literalSegmentCount++;
}
return (literalSegmentCount * 1000) - (wildcardSegmentCount * 10) + pattern.Length;
}
var bestPriority = int.MinValue;
factory = null;
matchedPattern = null;
foreach (var (pattern, serviceFactory) in WildcardStandardRequestServiceList)
{
if (!RouteMatchHelper.MatchWildcardRoute(pattern, serviceName)) continue;
var currentPriority = GetWildcardPatternPriority(pattern);
if (currentPriority <= bestPriority) continue;
bestPriority = currentPriority;
factory = serviceFactory;
matchedPattern = pattern;
}
return factory is not null;
}
}
using System.Net;
using System.Text.Json;
using System.Text.RegularExpressions;
using XFEExtension.NetCore.AutoImplement;
using XFEExtension.NetCore.DelegateExtension;
using XFEExtension.NetCore.ServerInteractive.Exceptions;
using XFEExtension.NetCore.ServerInteractive.Interfaces.Requester;
using XFEExtension.NetCore.ServerInteractive.Models.RequesterModels;
using XFEExtension.NetCore.ServerInteractive.Utilities.Helpers;
using XFEExtension.NetCore.ServerInteractive.Utilities.JsonConverter;
namespace XFEExtension.NetCore.ServerInteractive.Utilities.Requester;
/// <summary>
/// 客户端请求器
/// </summary>
[CreateImpl]
public abstract class ClientRequester : IRequesterBase
{
private readonly JsonSerializerOptions _jsonSerializerOptions = new();
internal Dictionary<string, Func<IRequestService>> RequestServiceDictionary = [];
internal Dictionary<string, Func<IStandardRequestService>> StandardRequestServiceDictionary = [];
internal List<(string Pattern, Func<IStandardRequestService> Factory)> WildcardStandardRequestServiceList = [];
internal Dictionary<string, StandardClientInstanceRequest> StandardClientInstanceRequestDictionary = [];
private string _session = string.Empty;
private TableRequester? _tableRequester;
/// <inheritdoc/>
public string RequestAddress { get; set; } = string.Empty;
/// <inheritdoc/>
public string Session
{
get => _session;
set
{
_session = value;
if (TableRequester is not null)
TableRequester.Session = value;
}
}
/// <inheritdoc/>
public string DeviceInfo { get; set; } = string.Empty;
/// <summary>
/// 表格请求器(如需使用表格相关功能,请通过构建器添加WithTableRequester实例并赋值此属性)
/// </summary>
public TableRequester? TableRequester
{
get => _tableRequester;
set
{
_tableRequester = value;
if (_tableRequester is null) return;
if (_tableRequester.RequestAddress.Length == 0)
_tableRequester.RequestAddress = RequestAddress;
if (_tableRequester.DeviceInfo.Length == 0)
_tableRequester.DeviceInfo = DeviceInfo;
if (_tableRequester.Session.Length == 0)
_tableRequester.Session = Session;
}
}
/// <inheritdoc/>
public event XFEEventHandler<object?, ServerInteractiveEventArgs>? MessageReceived;
/// <summary>
/// XFE客户端请求器
/// </summary>
protected ClientRequester()
{
_jsonSerializerOptions.Converters.Add(new JsonDateTimeConverter());
}
/// <summary>
/// 请求
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="serviceName"></param>
/// <param name="parameters"></param>
/// <returns></returns>
/// <exception cref="XFERequesterException"></exception>
public async Task<ClientRequestResult<T>> Request<T>(string serviceName, params object[] parameters)
=> (await RequestAsync(serviceName, CancellationToken.None, parameters).ConfigureAwait(false)).TryConvertTo<T>();
/// <summary>发起支持取消的类型化请求。</summary>
public async Task<ClientRequestResult<T>> RequestAsync<T>(string serviceName, CancellationToken cancellationToken, params object[] parameters)
=> (await RequestAsync(serviceName, cancellationToken, parameters).ConfigureAwait(false)).TryConvertTo<T>();
/// <summary>
/// 请求
/// </summary>
/// <param name="serviceName"></param>
/// <param name="parameters"></param>
/// <returns></returns>
/// <exception cref="XFERequesterException"></exception>
public Task<ClientRequestResult<object>> Request(string serviceName, params object[] parameters)
=> RequestAsync(serviceName, CancellationToken.None, parameters);
/// <summary>发起支持取消的请求。</summary>
public async Task<ClientRequestResult<object>> RequestAsync(string serviceName, CancellationToken cancellationToken, params object[] parameters)
{
var result = new ClientRequestResult<object>();
try
{
if (RequestServiceDictionary.TryGetValue(serviceName, out var serviceFactory))
{
var service = serviceFactory();
service.XFEClientRequester = this;
result = await service.Request<object>(parameters);
}
else if (StandardRequestServiceDictionary.TryGetValue(serviceName, out var xFEServiceFactory))
{
var xFEService = xFEServiceFactory();
xFEService.XFEClientRequester = this;
xFEService.DeviceInfo = DeviceInfo;
xFEService.Parameters = parameters;
// 通过RequestRouteMap解析实际路由路径
var actualRoute = xFEService.RequestRouteMap.GetValueOrDefault(serviceName, serviceName);
xFEService.Route = actualRoute;
if (!xFEService.RequestPoints.TryGetValue(serviceName, out var requestHandler))
throw new XFERequesterException($"未找到'{serviceName}'对应的请求处理方法([Request]标记的方法)");
var (response, code) = await InteractiveHelper.GetServerResponse(InteractiveHelper.BuildRequestUri(RequestAddress, actualRoute), requestHandler(), _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
result.StatusCode = code;
if ((int)code is >= 200 and < 300)
{
xFEService.Response = response;
xFEService.UnescapedResponse = response;
if (xFEService.ResponsePoints.TryGetValue(serviceName, out var responseHandler))
{
result.Result = responseHandler();
}
else
{
result.Result = response;
}
NotifyMessageReceived(new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
}
else
{
result.FailureKind = ClientRequestFailureKind.Protocol;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(response, code));
result.Message = response;
}
}
else if (StandardClientInstanceRequestDictionary.TryGetValue(serviceName, out var instance))
{
var (response, code) = await InteractiveHelper.GetServerResponse(InteractiveHelper.BuildRequestUri(RequestAddress, instance.Route ?? serviceName), instance.ConstructBody(Session, DeviceInfo, parameters), _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
result.StatusCode = code;
if ((int)code is >= 200 and < 300)
{
var requestResult = instance.ProcessResponse?.Invoke(response);
NotifyMessageReceived(new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
result.Result = requestResult ?? new();
}
else
{
result.FailureKind = ClientRequestFailureKind.Protocol;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(response, code));
result.Message = response;
}
}
else if (TryMatchWildcardStandardService(serviceName, out var wildcardFactory, out var matchedPattern))
{
var xFEService = wildcardFactory!();
xFEService.XFEClientRequester = this;
xFEService.DeviceInfo = DeviceInfo;
xFEService.Parameters = parameters;
// 通配符匹配:Route设为实际请求路径,以便服务方法中可通过Route属性查看具体路径
xFEService.Route = serviceName;
if (!xFEService.RequestPoints.TryGetValue(matchedPattern!, out var requestHandler))
throw new XFERequesterException($"未找到'{serviceName}'(通配符模式'{matchedPattern}')对应的请求处理方法([Request]标记的方法)");
var (response, code) = await InteractiveHelper.GetServerResponse(InteractiveHelper.BuildRequestUri(RequestAddress, serviceName), requestHandler(), _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
result.StatusCode = code;
if ((int)code is >= 200 and < 300)
{
xFEService.Response = response;
xFEService.UnescapedResponse = response;
if (xFEService.ResponsePoints.TryGetValue(matchedPattern!, out var responseHandler))
{
result.Result = responseHandler();
}
else
{
result.Result = response;
}
NotifyMessageReceived(new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
}
else
{
result.FailureKind = ClientRequestFailureKind.Protocol;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(response, code));
result.Message = response;
}
}
else
{
throw new InvalidOperationException($"未找到包含指定请求名称:{serviceName} 对应的请求处理服务");
}
return result;
}
catch (OperationCanceledException ex) when (cancellationToken.IsCancellationRequested)
{
result.FailureKind = ClientRequestFailureKind.Cancelled;
result.Exception = ex;
result.Message = "请求已取消";
result.StatusCode = HttpStatusCode.RequestTimeout;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(result.Message, result.StatusCode));
return result;
}
catch (OperationCanceledException ex)
{
result.FailureKind = ClientRequestFailureKind.Timeout;
result.Exception = ex;
result.Message = "请求超时";
result.StatusCode = HttpStatusCode.RequestTimeout;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(result.Message, result.StatusCode));
return result;
}
catch (HttpRequestException ex)
{
result.FailureKind = ClientRequestFailureKind.Transport;
result.Exception = ex;
result.Message = ex.Message;
result.StatusCode = HttpStatusCode.ServiceUnavailable;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(result.Message, result.StatusCode));
return result;
}
catch (JsonException ex)
{
result.FailureKind = ClientRequestFailureKind.Serialization;
result.Exception = ex;
result.Message = ex.Message;
result.StatusCode = HttpStatusCode.UnprocessableEntity;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(result.Message, result.StatusCode));
return result;
}
catch (Exception ex)
{
result.FailureKind = ClientRequestFailureKind.Application;
result.Exception = ex;
NotifyMessageReceived(new ServerInteractiveEventArgsImpl(ex.Message, HttpStatusCode.InternalServerError));
result.Message = ex.Message;
result.StatusCode = HttpStatusCode.InternalServerError;
return result;
}
}
private void NotifyMessageReceived(ServerInteractiveEventArgs args)
{
try { MessageReceived?.Invoke(this, args); } catch (Exception) { }
}
/// <summary>
/// 尝试通过通配符模式匹配标准请求服务(选择最具体的匹配模式,与服务端行为一致)
/// </summary>
/// <param name="serviceName">请求路径</param>
/// <param name="factory">匹配到的服务工厂</param>
/// <param name="matchedPattern">匹配到的通配符模式</param>
/// <returns>是否匹配成功</returns>
private bool TryMatchWildcardStandardService(string serviceName, out Func<IStandardRequestService>? factory, out string? matchedPattern)
{
// 在所有命中的候选中选择最具体的模式(字面量段越多越优先),避免结果依赖注册顺序
static int GetWildcardPatternPriority(string pattern)
{
var segments = pattern.Split('/');
var literalSegmentCount = 0;
var wildcardSegmentCount = 0;
foreach (var segment in segments)
{
if (segment == "*")
wildcardSegmentCount++;
else
literalSegmentCount++;
}
return (literalSegmentCount * 1000) - (wildcardSegmentCount * 10) + pattern.Length;
}
var bestPriority = int.MinValue;
factory = null;
matchedPattern = null;
foreach (var (pattern, serviceFactory) in WildcardStandardRequestServiceList)
{
if (!RouteMatchHelper.MatchWildcardRoute(pattern, serviceName)) continue;
var currentPriority = GetWildcardPatternPriority(pattern);
if (currentPriority <= bestPriority) continue;
bestPriority = currentPriority;
factory = serviceFactory;
matchedPattern = pattern;
}
return factory is not null;
}
}