mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-02 07:40:48 +08:00
refactor(flow) : 重新实现了UIContextOperation的注入逻辑,为后续开发Avalonia版本的编辑器做准备。
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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>);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -332,7 +332,7 @@ public static class NodeMethodDetailsHelper
|
||||
{
|
||||
isExplicitData = nodeParmsAttribute.IsExplicit; // 设置是否是显式参数
|
||||
}
|
||||
if (string.IsNullOrEmpty(nodeParmsAttribute.Name))
|
||||
if (!string.IsNullOrEmpty(nodeParmsAttribute.Name))
|
||||
{
|
||||
description = nodeParmsAttribute.Name; // 设置显示的名称
|
||||
}
|
||||
|
||||
@@ -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
|
||||
提供技术交流与支持,欢迎加入。
|
||||
因为个人是社畜,所以可能不会及时回复,请谅解。
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
54
Serein.Proto.HttpApi/HandleState.cs
Normal file
54
Serein.Proto.HttpApi/HandleState.cs
Normal 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,
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
56
Serein.Proto.HttpApi/InvokeResult.cs
Normal file
56
Serein.Proto.HttpApi/InvokeResult.cs
Normal 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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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) // 如果包含查询字符串
|
||||
{
|
||||
|
||||
42
Serein.Proto.HttpApi/RouterInfo.cs
Normal file
42
Serein.Proto.HttpApi/RouterInfo.cs
Normal 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
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@ namespace Serein.Workbench.Services
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
void KeyUp(Key key);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user