XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension.NetCore.ServerInteractive

[DLL] Server interaction extension, including user identity verification and querying in conjunction with AutoConfig

公开
关注 0 Fork 0 Star 0
UTF-8
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 Request(serviceName, parameters)).TryConvertTo<T>();

    /// <summary>
    /// 请求
    /// </summary>
    /// <param name="serviceName"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    /// <exception cref="XFERequesterException"></exception>
    public async Task<ClientRequestResult<object>> Request(string serviceName, 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(RequestAddress + $"/{actualRoute}", requestHandler(), _jsonSerializerOptions);
                result.StatusCode = code;
                if (code == HttpStatusCode.OK)
                {
                    xFEService.Response = response;
                    xFEService.UnescapedResponse = Regex.Unescape(response);
                    if (xFEService.ResponsePoints.TryGetValue(serviceName, out var responseHandler))
                    {
                        result.Result = responseHandler();
                    }
                    else
                    {
                        result.Result = response;
                    }
                    MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl("Success", code));
                    result.Message = "Success";
                }
                else
                {
                    MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl(response, code));
                    result.Message = response;
                }
            }
            else if (StandardClientInstanceRequestDictionary.TryGetValue(serviceName, out var instance))
            {
                var (response, code) = await InteractiveHelper.GetServerResponse(RequestAddress + $"/{serviceName}", instance.ConstructBody(Session, DeviceInfo, parameters), _jsonSerializerOptions);
                result.StatusCode = code;
                if (code == HttpStatusCode.OK)
                {
                    var requestResult = instance.ProcessResponse?.Invoke(response);
                    MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl("Success", code));
                    result.Message = "Success";
                    result.Result = requestResult ?? new();
                }
                else
                {
                    MessageReceived?.Invoke(this, 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(RequestAddress + $"/{serviceName}", requestHandler(), _jsonSerializerOptions);
                result.StatusCode = code;
                if (code == HttpStatusCode.OK)
                {
                    xFEService.Response = response;
                    xFEService.UnescapedResponse = Regex.Unescape(response);
                    if (xFEService.ResponsePoints.TryGetValue(matchedPattern!, out var responseHandler))
                    {
                        result.Result = responseHandler();
                    }
                    else
                    {
                        result.Result = response;
                    }
                    MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl("Success", code));
                    result.Message = "Success";
                }
                else
                {
                    MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl(response, code));
                    result.Message = response;
                }
            }
            else
            {
                throw new InvalidOperationException($"未找到包含指定请求名称:{serviceName} 对应的请求处理服务");
            }
            return result;
        }
        catch (Exception ex)
        {
            MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl(ex.Message, HttpStatusCode.InternalServerError));
            result.Message = ex.Message;
            result.StatusCode = HttpStatusCode.InternalServerError;
            return result;
        }
    }

    /// <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;
    }
}