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

6
.gitignore vendored
View File

@@ -3,6 +3,9 @@
*.user
*.suo
# 排除 Rider 相关文件夹
.idea
# 排除编译输出文件夹
bin/
obj/
@@ -18,11 +21,14 @@ obj/
*.exe
# 排除发布文件夹
.Output/
/.git1
doc
WorkBench.ControlLibrary.Core
WorkBench.Remote
Serein.FlowStartTool
Serein.CloudWorkbench
Serein.CollaborationSync
Serein.Workbench.Avalonia

View File

@@ -12,7 +12,7 @@ namespace Serein.Library.Utils
/// <summary>
/// Json门户类需要你提供实现
/// </summary>
private static IJsonProvider provider;
public static IJsonProvider Provider { get; private set; }
/// <summary>
/// 使用第三方包进行解析
@@ -20,7 +20,7 @@ namespace Serein.Library.Utils
/// <param name="jsonPortal"></param>
public static void UseJsonProvider(IJsonProvider jsonPortal)
{
JsonHelper.provider = jsonPortal;
JsonHelper.Provider = jsonPortal;
}
/// <summary>
@@ -32,7 +32,7 @@ namespace Serein.Library.Utils
public static T Deserialize<T>(string jsonText)
{
return provider.Deserialize<T>(jsonText);
return Provider.Deserialize<T>(jsonText);
}
/// <summary>
@@ -43,7 +43,7 @@ namespace Serein.Library.Utils
/// <returns></returns>
public static object Deserialize(string jsonText, Type type)
{
return provider.Deserialize(jsonText, type);
return Provider.Deserialize(jsonText, type);
}
@@ -55,7 +55,7 @@ namespace Serein.Library.Utils
public static IJsonToken Parse(string json)
{
return provider.Parse(json);
return Provider.Parse(json);
}
/// <summary>
@@ -66,7 +66,7 @@ namespace Serein.Library.Utils
/// <returns></returns>
public static bool TryParse(string json, out IJsonToken jsonToken)
{
return provider.TryParse(json, out jsonToken);
return Provider.TryParse(json, out jsonToken);
}
@@ -78,7 +78,7 @@ namespace Serein.Library.Utils
/// <returns></returns>
public static string Serialize(object obj)
{
return provider.Serialize(obj);
return Provider.Serialize(obj);
}
/// <summary>
@@ -90,7 +90,7 @@ namespace Serein.Library.Utils
{
var dict = new Dictionary<string, object>();
init(dict);
return provider.CreateObject(dict);
return Provider.CreateObject(dict);
}
/// <summary>
@@ -100,7 +100,7 @@ namespace Serein.Library.Utils
/// <returns></returns>
public static IJsonToken Array(IEnumerable<object> values)
{
return provider.CreateArray(values);
return Provider.CreateArray(values);
}
/// <summary>
@@ -111,8 +111,8 @@ namespace Serein.Library.Utils
public static IJsonToken FromObject(object obj)
{
if (obj is System.Collections.IEnumerable && !(obj is string))
return provider.CreateObject(obj as IDictionary<string, object>);
return provider.CreateArray(obj as IEnumerable<object>);
return Provider.CreateObject(obj as IDictionary<string, object>);
return Provider.CreateArray(obj as IEnumerable<object>);
}
}

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
@@ -9,7 +10,18 @@ using System.Text;
namespace Serein.Library.Utils
{
/// <summary>
/// 指示IOC扫描构造函数时的行为
/// </summary>
[AttributeUsage(AttributeTargets.Constructor)]
public sealed class SereinIOCCtorAttribute : Attribute
{
/// <summary>
/// 忽略该构造函数
/// </summary>
public bool IsIgnore = false;
}
/// <summary>
/// 一个轻量级的单例IOC容器
/// </summary>
@@ -380,7 +392,19 @@ namespace Serein.Library.Utils
/// <returns></returns>
private ConstructorInfo[] GetConstructor(Type type)
{
return type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Length).ToArray();
return type.GetConstructors().Where(ctor =>
{
var attribute = ctor.GetCustomAttribute<SereinIOCCtorAttribute>();
if (attribute is null)
{
return true;
}
else
{
return !attribute.IsIgnore;
}
})
.OrderByDescending(ctor => ctor.GetParameters().Length).ToArray();
}
/// <summary>
@@ -556,6 +580,11 @@ namespace Serein.Library.Utils
if (typeName.Equals("Serein.Library.LightweightFlowEnvironment"))
{
}
if (_registerCallback.TryGetValue(typeName, out var registerCallback))
{
}
if (_dependencies.ContainsKey(typeName))
{

View File

@@ -34,11 +34,15 @@ namespace Serein.Library.Utils
}
}
/// <summary>
/// 传入UI线程上下文
/// </summary>
/// <param name="synchronizationContext">线程上下文</param>
[SereinIOCCtor(IsIgnore = true)]
public UIContextOperation(SynchronizationContext synchronizationContext)
{
this.context = synchronizationContext;
@@ -48,6 +52,7 @@ namespace Serein.Library.Utils
/// 传入获取UI线程上下文的闭包创建
/// </summary>
/// <param name="getUiContext">获取线程上下文的闭包函数</param>
[SereinIOCCtor(IsIgnore = true)]
public UIContextOperation(Func<SynchronizationContext> getUiContext)
{
this.getUiContext = getUiContext;

View File

@@ -24,21 +24,21 @@ namespace Serein.NodeFlow.Env
private readonly FlowLibraryService flowLibraryService;
private readonly FlowOperationService flowOperationService;
private readonly FlowModelService flowModelService;
private readonly UIContextOperation UIContextOperation;
private readonly UIContextOperation uiContextOperation;
public FlowControl(IFlowEnvironment flowEnvironment,
IFlowEnvironmentEvent flowEnvironmentEvent,
FlowLibraryService flowLibraryService,
FlowOperationService flowOperationService,
FlowModelService flowModelService,
UIContextOperation UIContextOperation)
UIContextOperation uiContextOperation)
{
this.flowEnvironment = flowEnvironment;
this.flowEnvironmentEvent = flowEnvironmentEvent;
this.flowLibraryService = flowLibraryService;
this.flowOperationService = flowOperationService;
this.flowModelService = flowModelService;
this.UIContextOperation = UIContextOperation;
this.uiContextOperation = uiContextOperation;
contexts = new ObjectPool<IFlowContext>(() => new FlowContext(flowEnvironment), context => context.Reset());
flowTaskOptions = new FlowWorkOptions
@@ -340,7 +340,7 @@ namespace Serein.NodeFlow.Env
{
flowWorkManagement.Exit();
}
UIContextOperation?.Invoke(() => flowEnvironmentEvent.OnFlowRunComplete(new FlowEventArgs()));
uiContextOperation?.Invoke(() => flowEnvironmentEvent.OnFlowRunComplete(new FlowEventArgs()));
IOC.Reset();
GC.Collect();
return Task.FromResult(true);

View File

@@ -63,7 +63,44 @@ namespace Serein.NodeFlow.Env
.Build();
// 设置JSON解析器
JsonHelper.UseJsonProvider(new NewtonsoftJsonProvider());
if (JsonHelper.Provider is null)
{
JsonHelper.UseJsonProvider(new NewtonsoftJsonProvider());
}
// 默认使用本地环境
currentFlowEnvironment = ioc.Get<LocalFlowEnvironment>();
currentFlowEnvironmentEvent = ioc.Get<IFlowEnvironmentEvent>();
SereinEnv.SetEnv(currentFlowEnvironment);
}
/// <summary>
/// 提供上下文操作进行调用
/// </summary>
/// <param name="operation"></param>
public FlowEnvironment(UIContextOperation operation)
{
ISereinIOC ioc = new SereinIOC();
ioc.Register<ISereinIOC>(()=> ioc) // IOC容器接口
.Register<UIContextOperation>(() => operation) // 流程环境接口
.Register<IFlowEnvironment>(() => this) // 流程环境接口
.Register<IFlowEnvironmentEvent, FlowEnvironmentEvent>() // 流程环境事件接口
.Register<IFlowEdit, FlowEdit>() // 流程编辑接口
.Register<IFlowControl, FlowControl>() // 流程控制接口
.Register<LocalFlowEnvironment>() // 本地环境
.Register<FlowModelService>() // 节点/画布模型服务
.Register<FlowLibraryService>() // 流程库服务
.Register<FlowCoreGenerateService>() // 代码生成
.Register<FlowOperationService>() // 流程操作
.Register<NodeMVVMService>() // 节点MVVM服务
.Build();
// 设置JSON解析器
if (JsonHelper.Provider is null)
{
JsonHelper.UseJsonProvider(new NewtonsoftJsonProvider());
}
// 默认使用本地环境
currentFlowEnvironment = ioc.Get<LocalFlowEnvironment>();
currentFlowEnvironmentEvent = ioc.Get<IFlowEnvironmentEvent>();

View File

@@ -245,12 +245,14 @@ namespace Serein.NodeFlow.Env
/// <param name="filePath"></param>
public void LoadProject(string filePath)
{
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
_ = Task.Run(async () =>
{
await this.LoadProjetAsync((filePath));
});
/*string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
var FlowProjectData = JsonHelper.Deserialize<SereinProjectData>(content);
var FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
this.ProjectFileLocation = filePath;
var projectData = FlowProjectData;
@@ -285,30 +287,50 @@ namespace Serein.NodeFlow.Env
}
Event.OnProjectLoaded(new ProjectLoadedEventArgs());
});
});*/
}
public async Task LoadProjetAsync(string filePath)
{
string content = await System.IO.File.ReadAllTextAsync(filePath); // 读取整个文件内容
var FlowProjectData = JsonHelper.Deserialize<SereinProjectData>(content);
var FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
if(FlowProjectData is null)
var content = await System.IO.File.ReadAllTextAsync(filePath); // 读取整个文件内容
var flowProjectData = JsonHelper.Deserialize<SereinProjectData>(content);
var fileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
if(flowProjectData is null)
{
return;
}
var projectData = flowProjectData ?? throw new ArgumentNullException(nameof(flowProjectData));
if (!_flowLibraryService.IsLoadedBaseLibrary)
{
var baseLibrary = _flowLibraryService.LoadBaseLibrary();
if (baseLibrary.MethodInfos.Count > 0 && UIContextOperation is not null)
{
await UIContextOperation.InvokeAsync(() => Event.OnDllLoad(new LoadDllEventArgs(baseLibrary))); // 通知UI创建dll面板显示
}
if (_flowModelService.GetAllCanvasModel().Count == 0)
{
// 创建第一个画布
FlowEdit.CreateCanvas("Default", 1920, 1080);
}
}
this.ProjectFileLocation = filePath;
var projectData = FlowProjectData;
// 加载项目配置文件
var dllPaths = projectData.Librarys.Select(it => it.FilePath).ToList();
List<MethodDetails> methodDetailss = [];
// 遍历依赖项中的特性注解,生成方法详情
foreach (var dllPath in dllPaths)
{
string cleanedRelativePath = dllPath.TrimStart('.', '\\');
var cleanedRelativePath = dllPath.TrimStart('.', '\\');
var tmpPath = Path.Combine(filePath, cleanedRelativePath);
var dllFilePath = Path.GetFullPath(tmpPath);
LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集
@@ -526,7 +548,8 @@ namespace Serein.NodeFlow.Env
return;
}
this.UIContextOperation = uiContextOperation;
IOC.Register<UIContextOperation>(() => uiContextOperation).Build();
IOC.Register<UIContextOperation>(() => uiContextOperation);
IOC.Build();
OnUIContextOperationSet();
}
@@ -656,18 +679,11 @@ namespace Serein.NodeFlow.Env
#endregion
/// <summary>
/// 设置了 UIContextOperation 需要立刻执行的方法,用于加载基础库,创建第一个画布。
/// 设置了 UIContextOperation 需要立刻执行的方法
/// </summary>
private void OnUIContextOperationSet()
{
var baseLibrary = _flowLibraryService.LoadBaseLibrary();
if (baseLibrary is not null && baseLibrary.MethodInfos.Count > 0)
{
UIContextOperation?.Invoke(() => Event.OnDllLoad(new LoadDllEventArgs(baseLibrary))); // 通知UI创建dll面板显示
}
// 创建第一个画布
FlowEdit.CreateCanvas("Default", 1920, 1080);
}
}

View File

@@ -1,5 +1,6 @@
using Serein.Library;
using Serein.NodeFlow.Tool;
using System.Globalization;
using System.Reflection;
namespace Serein.NodeFlow.Model.Library
@@ -229,14 +230,17 @@ namespace Serein.NodeFlow.Model.Library
public FlowLibraryInfo ToInfo()
{
var assemblyName = Assembly.GetName().Name;
var mdInfos = MethodDetailss.Values.Select(x => x.ToInfo()).ToList();
mdInfos.Sort((a, b) => string.Compare(a.MethodName, b.MethodName, StringComparison.OrdinalIgnoreCase));
var mdInfos = MethodDetailss.Values.Select(x => x.ToInfo())
.OrderBy(d => d.AssemblyName)
.ThenBy(s => s.MethodAnotherName, StringComparer.Create(CultureInfo.GetCultureInfo("zh-cn"), true))
.ToList();
return new FlowLibraryInfo
{
AssemblyName = assemblyName,
FileName = FullName,
FilePath = FilePath,
MethodInfos = mdInfos.ToList(),
MethodInfos = mdInfos,
};
}

View File

@@ -14,6 +14,11 @@ namespace Serein.NodeFlow.Services
/// </summary>
public class FlowLibraryService
{
/// <summary>
/// 是否加载过基础依赖
/// </summary>
public bool IsLoadedBaseLibrary { get; private set; } = false;
/// <summary>
/// 构造函数,初始化流程依赖
/// </summary>
@@ -60,7 +65,7 @@ namespace Serein.NodeFlow.Services
/// </summary>
public FlowLibraryInfo LoadBaseLibrary()
{
Assembly baseAssmbly = typeof(FlowBaseLibrary).Assembly;
var baseAssmbly = typeof(FlowBaseLibrary).Assembly;
var flowLibrary = new FlowLibraryCache(baseAssmbly);
flowLibrary.LoadFlowMethod();
var assemblyName = baseAssmbly.GetName().Name;
@@ -69,7 +74,9 @@ namespace Serein.NodeFlow.Services
throw new Exception($"程序集\"{baseAssmbly}\"返回 Name 为 null");
}
_flowLibraryCaches.TryAdd(assemblyName, flowLibrary);
return flowLibrary.ToInfo();
var infos = flowLibrary.ToInfo();
IsLoadedBaseLibrary = true;
return infos;
}
/// <summary>

View File

@@ -332,7 +332,7 @@ public static class NodeMethodDetailsHelper
{
isExplicitData = nodeParmsAttribute.IsExplicit; // 设置是否是显式参数
}
if (string.IsNullOrEmpty(nodeParmsAttribute.Name))
if (!string.IsNullOrEmpty(nodeParmsAttribute.Name))
{
description = nodeParmsAttribute.Name; // 设置显示的名称
}

View File

@@ -29,4 +29,10 @@ This project supports both **English** and **中文** documentation.
## 🔗 External Links
- Bilibili: https://space.bilibili.com/33526379
- Bilibili: https://space.bilibili.com/33526379
# 社群
QQ群 955830545
提供技术交流与支持,欢迎加入。
因为个人是社畜,所以可能不会及时回复,请谅解。

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

View File

@@ -30,6 +30,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Proto.Modbus", "Sere
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Proto.HttpApi", "Serein.Proto.HttpApi\Serein.Proto.HttpApi.csproj", "{281B8E55-B9CD-4FE5-A72F-59CBB968D844}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Workbench.Avalonia", "Serein.Workbench.Avalonia\Serein.Workbench.Avalonia.csproj", "{3115002B-4CDA-4793-803A-5C1BA95EC6C5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -80,6 +82,10 @@ Global
{281B8E55-B9CD-4FE5-A72F-59CBB968D844}.Debug|Any CPU.Build.0 = Debug|Any CPU
{281B8E55-B9CD-4FE5-A72F-59CBB968D844}.Release|Any CPU.ActiveCfg = Release|Any CPU
{281B8E55-B9CD-4FE5-A72F-59CBB968D844}.Release|Any CPU.Build.0 = Release|Any CPU
{3115002B-4CDA-4793-803A-5C1BA95EC6C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3115002B-4CDA-4793-803A-5C1BA95EC6C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3115002B-4CDA-4793-803A-5C1BA95EC6C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3115002B-4CDA-4793-803A-5C1BA95EC6C5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -81,7 +81,7 @@
<ItemGroup>
<PackageReference Include="AvalonEdit" Version="6.3.0.90" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" />
<!--<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />-->
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

View File

@@ -55,7 +55,6 @@ namespace Serein.Workbench.Services
/// </summary>
/// <param name="key"></param>
void KeyUp(Key key);
}
/// <summary>