refactor(flow) : 重新实现了UIContextOperation的注入逻辑,为后续开发Avalonia版本的编辑器做准备。

This commit is contained in:
fengjiayi
2025-09-19 23:58:52 +08:00
parent 0161c64e2c
commit 630008198d
21 changed files with 415 additions and 200 deletions

View File

@@ -154,7 +154,15 @@ namespace Serein.Proto.HttpApi
}
else if (jsonObject != null && PostArgTypes[i] == PostArgType.IsBobyData)
{
args[i] = jsonObject.ToObject(type);
if (type.IsEnum)
{
args[i] = jsonObject.ToObject(type);
}
else
{
args[i] = jsonObject.ToObject(type);
}
}
else if (jsonObject != null)
{

View File

@@ -0,0 +1,54 @@
namespace Serein.Proto.HttpApi
{
public enum HandleState
{
/// <summary>
/// 默认值
/// </summary>
None ,
/// <summary>
/// 没有异常
/// </summary>
Ok,
/// <summary>
/// 没有对应的控制器
/// </summary>
NotHanleController,
/// <summary>
/// 没有对应的Http请求类型
/// </summary>
NotHttpApiRequestType,
/// <summary>
/// 没有处理配置
/// </summary>
NotHandleConfig,
/// <summary>
/// 无法实例化控制器
/// </summary>
HanleControllerIsNull,
/// <summary>
/// 调用参数获取错误
/// </summary>
InvokeArgError,
/// <summary>
/// 调用发生异常
/// </summary>
InvokeErrored,
/// <summary>
/// 请求被阻止
/// </summary>
RequestBlocked,
}
}

View File

@@ -0,0 +1,56 @@
namespace Serein.Proto.HttpApi
{
/// <summary>
/// 调用结果
/// </summary>
public class InvokeResult
{
/// <summary>
/// 处理工具记录的请求Id用于匹配请求与响应
/// </summary>
public long RequestId { get; set; }
/// <summary>
/// 调用状态
/// </summary>
public HandleState State { get; set; }
/// <summary>
/// 调用正常时这里会有数据
/// </summary>
public object? Data{ get; set; }
/// <summary>
/// 调用失败时这里可能会有异常数据
/// </summary>
public Exception? Exception { get; set; }
/// <summary>
/// 调用成功
/// </summary>
/// <param name="requestId"></param>
/// <param name="data"></param>
/// <returns></returns>
public static InvokeResult Ok(long requestId, object? data) => new InvokeResult
{
RequestId = requestId,
Data = data,
State = HandleState.Ok,
};
/// <summary>
/// 调用失败
/// </summary>
/// <param name="requestId"></param>
/// <param name="state"></param>
/// <param name="ex"></param>
/// <returns></returns>
public static InvokeResult Fail(long requestId, HandleState state, Exception? ex = null) => new InvokeResult
{
RequestId = requestId,
State = state,
Exception = ex,
};
}
}

View File

@@ -17,140 +17,18 @@ using Type = System.Type;
namespace Serein.Proto.HttpApi
{
/// <summary>
/// 路由接口
/// </summary>
public interface IPathRouter
{
/// <summary>
/// 添加处理模块
/// </summary>
/// <param name="controllerType"></param>
void AddHandle(Type controllerType);
/// <summary>
/// 获取路由信息
/// </summary>
/// <returns></returns>
List<RouterInfo> GetRouterInfos();
/// <summary>
/// 路由解析开始处理
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task<InvokeResult> HandleAsync(HttpListenerContext context);
}
public enum HandleState
{
/// <summary>
/// 默认值
/// </summary>
None ,
/// <summary>
/// 没有异常
/// </summary>
Ok,
/// <summary>
/// 没有对应的控制器
/// </summary>
NotHanleController,
/// <summary>
/// 没有对应的Http请求类型
/// </summary>
NotHttpApiRequestType,
/// <summary>
/// 没有处理配置
/// </summary>
NotHandleConfig,
/// <summary>
/// 无法实例化控制器
/// </summary>
HanleControllerIsNull,
/// <summary>
/// 调用参数获取错误
/// </summary>
InvokeArgError,
/// <summary>
/// 调用发生异常
/// </summary>
InvokeErrored,
}
/// <summary>
/// 调用结果
/// </summary>
public class InvokeResult
{
/// <summary>
/// 调用状态
/// </summary>
public HandleState State { get; set; }
/// <summary>
/// 调用正常时这里会有数据
/// </summary>
public object? Data{ get; set; }
/// <summary>
/// 调用失败时这里可能会有异常数据
/// </summary>
public Exception? Exception { get; set; }
public static InvokeResult Ok(object? data) => new InvokeResult
{
Data = data,
State = HandleState.Ok,
};
public static InvokeResult Fail(HandleState state, Exception? ex = null) => new InvokeResult
{
State = state,
Exception = ex,
};
}
/// <summary>
/// 路由信息
/// </summary>
public class RouterInfo
{
#if NET6_0_OR_GREATER
/// <summary>
/// 接口类型
/// </summary>
public ApiType ApiType { get; set; }
/// <summary>
/// 接口URL
/// </summary>
public required string Url { get; set; }
/// <summary>
/// 对应的处理方法
/// </summary>
public required MethodInfo MethodInfo { get; set; }
#else
/// <summary>
/// 接口类型
/// </summary>
public ApiType ApiType { get; set; }
/// <summary>
/// 接口URL
/// </summary>
public string Url { get; set; }
/// <summary>
/// 对应的处理方法
/// </summary>
public MethodInfo MethodInfo { get; set; }
#endif
}
/// <summary>
/// 路由注册与解析
/// </summary>
public class PathRouter : IPathRouter
internal class PathRouter
{
private readonly ISereinIOC SereinIOC; // 用于存储路由信息
/// <summary>
/// IOC容器
/// </summary>
private readonly ISereinIOC SereinIOC;
private long _requestId = 0;
/// <summary>
/// 控制器实例对象的类型,每次调用都会重新实例化,[Url - ControllerType]
@@ -162,6 +40,12 @@ namespace Serein.Proto.HttpApi
/// </summary>
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, ApiHandleConfig>> HandleModels = new ConcurrentDictionary<string, ConcurrentDictionary<string, ApiHandleConfig>>();
/// <summary>
/// 请求时的处理函数传入API类型、URL、Body
/// </summary>
internal Func<ApiRequestInfo, bool>? OnBeforRequest;
public PathRouter(ISereinIOC SereinIOC)
{
this.SereinIOC = SereinIOC;
@@ -238,19 +122,35 @@ namespace Serein.Proto.HttpApi
public async Task<InvokeResult> HandleAsync(HttpListenerContext context)
{
var request = context.Request; // 获取请求对象
var uri = request.Url; // 获取请求的完整URL
var httpMethod = request.HttpMethod.ToUpper(); // 获取请求的 HTTP 方法
var fullUrl = uri.ToString(); // 获取完整URL字符串
var routeValues = GetUrlData(fullUrl); // 解析 URL 获取路由参数
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
var requestId = System.Threading.Interlocked.Increment(ref _requestId);
var requestInfo = new ApiRequestInfo
{
RequestId = requestId,
ApiType = httpMethod,
Url = fullUrl,
Body = requestBody,
};
if (OnBeforRequest?.Invoke(requestInfo) == false)
{
return InvokeResult.Fail(requestId, HandleState.RequestBlocked); // 请求被阻止
}
if (!HandleModels.TryGetValue(httpMethod, out var modules) || request.Url is null)
{
return InvokeResult.Fail(HandleState.NotHttpApiRequestType); // 没有对应HTTP请求方法的处理
return InvokeResult.Fail(requestId, HandleState.NotHttpApiRequestType); // 没有对应HTTP请求方法的处理
}
var template = request.Url.AbsolutePath.ToLower() ;
if (!_controllerTypes.TryGetValue(template, out var controllerType))
{
return InvokeResult.Fail(HandleState.NotHanleController); // 没有对应的处理器
return InvokeResult.Fail(requestId, HandleState.NotHanleController); // 没有对应的处理器
}
if (!modules.TryGetValue(template, out var config))
{
return InvokeResult.Fail(HandleState.NotHandleConfig); // 没有对应的处理配置
return InvokeResult.Fail(requestId, HandleState.NotHandleConfig); // 没有对应的处理配置
}
ControllerBase controllerInstance;
@@ -260,13 +160,13 @@ namespace Serein.Proto.HttpApi
}
catch
{
return InvokeResult.Fail(HandleState.HanleControllerIsNull); // 未找到控制器实例
return InvokeResult.Fail(requestId, HandleState.HanleControllerIsNull); // 未找到控制器实例
}
var url = request.Url; // 获取请求的完整URL
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
controllerInstance.Url = url.AbsolutePath;
controllerInstance.Url = uri.AbsolutePath; // 设置控制器实例的 URL 属性
object?[] args;
switch (httpMethod)
@@ -275,32 +175,32 @@ namespace Serein.Proto.HttpApi
args = config.GetArgsOfGet(routeValues); // Get请求
break;
case "POST":
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
controllerInstance.Body = requestBody;
if (!JsonHelper.TryParse(requestBody, out var requestJObject))
{
var exTips = $"body 无法转换为 json 数据, body: {requestBody}";
return InvokeResult.Fail(HandleState.InvokeArgError, new Exception(exTips));
return InvokeResult.Fail(requestId, HandleState.InvokeArgError, new Exception(exTips));
}
(var isPass, var index, var type, var ex) = config.TryGetArgsOfPost(routeValues, requestJObject, out args);
if (!isPass)
{
var exTips = $"尝试转换第{index}个入参参数时,类型 {type.FullName} 参数获取失败:{ex?.Message}";
return InvokeResult.Fail(HandleState.InvokeArgError, new Exception(exTips));
return InvokeResult.Fail(requestId, HandleState.InvokeArgError, new Exception(exTips));
}
break;
default:
return InvokeResult.Fail(HandleState.NotHttpApiRequestType);
return InvokeResult.Fail(requestId, HandleState.NotHttpApiRequestType);
}
try
{
var invokeResult = await config.HandleAsync(controllerInstance, args);
return InvokeResult.Ok(invokeResult);
return InvokeResult.Ok(requestId, invokeResult);
}
catch (Exception ex)
{
return InvokeResult.Fail(HandleState.InvokeErrored, ex);
return InvokeResult.Fail(requestId, HandleState.InvokeErrored, ex);
}
}
@@ -403,11 +303,11 @@ namespace Serein.Proto.HttpApi
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
private Dictionary<string, string> GetUrlData(Uri uri)
private Dictionary<string, string> GetUrlData(string uri)
{
Dictionary<string, string> routeValues = new Dictionary<string, string>();
var pathParts = uri.ToString().Split('?'); // 拆分 URL获取路径部分
var pathParts = uri.Split('?'); // 拆分 URL获取路径部分
if (pathParts.Length > 1) // 如果包含查询字符串
{

View File

@@ -0,0 +1,42 @@
using System.Reflection;
namespace Serein.Proto.HttpApi
{
/// <summary>
/// 路由信息
/// </summary>
public class RouterInfo
{
#if NET6_0_OR_GREATER
/// <summary>
/// 接口类型
/// </summary>
public ApiType ApiType { get; set; }
/// <summary>
/// 接口URL
/// </summary>
public required string Url { get; set; }
/// <summary>
/// 对应的处理方法
/// </summary>
public required MethodInfo MethodInfo { get; set; }
#else
/// <summary>
/// 接口类型
/// </summary>
public ApiType ApiType { get; set; }
/// <summary>
/// 接口URL
/// </summary>
public string Url { get; set; }
/// <summary>
/// 对应的处理方法
/// </summary>
public MethodInfo MethodInfo { get; set; }
#endif
}
}

View File

@@ -8,7 +8,7 @@
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<Title>基于Json数据载体的HttpApi交互工具包</Title>
<Version>0.0.9</Version>
<Version>0.0.11</Version>
<Description>基于Json数据载体的HttpApi交互工具包</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>

View File

@@ -1,26 +1,27 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using System.Net;
using System.Text;
namespace Serein.Proto.HttpApi
{
/// <summary>
/// 对于 HttpListenerContext 的拓展服务
/// </summary>
public class SereinWebApiService
{
private readonly IPathRouter _pathRouter;
private readonly PathRouter _pathRouter;
//private RequestLimiter? requestLimiter;
/// <summary>
/// 初始化处理器
/// </summary>
/// <param name="pathRouter"></param>
public SereinWebApiService(IPathRouter pathRouter)
public SereinWebApiService(ISereinIOC sereinIOC)
{
_pathRouter = pathRouter;
_pathRouter = new PathRouter(sereinIOC);
}
/// <summary>
@@ -50,7 +51,10 @@ namespace Serein.Proto.HttpApi
return _pathRouter.GetRouterInfos();
}
private Func<InvokeResult, (object, HttpStatusCode)> OnBeforeReplying;
/// <summary>
/// 传入方法调用结果,返回最终回复的内容和状态码
/// </summary>
private Func<InvokeResult, (object, HttpStatusCode)>? OnBeforeReplying;
/// <summary>
/// 设置回复前的处理函数
@@ -61,6 +65,16 @@ namespace Serein.Proto.HttpApi
OnBeforeReplying = func;
}
/// <summary>
/// 请求时的处理函数传入API类型、URL、Body
/// </summary>
/// <param name="func"></param>
public void SetOnBeforeRequest(Func<ApiRequestInfo, bool> func)
{
_pathRouter.OnBeforRequest = func;
}
/// <summary>
/// 处理请求
/// </summary>
@@ -118,4 +132,30 @@ namespace Serein.Proto.HttpApi
}
}
/// <summary>
/// 外部请求的信息
/// </summary>
public class ApiRequestInfo
{
/// <summary>
/// 请求编号
/// </summary>
public long RequestId { get; set; }
/// <summary>
/// API类型 GET/POST
/// </summary>
public string ApiType { get; set; }
/// <summary>
/// 请求的URL
/// </summary>
public string Url { get; set; }
/// <summary>
/// 请求的Body
/// </summary>
public string? Body { get; set; }
}
}