mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-01 22:16:34 +08:00
移除了文件
This commit is contained in:
@@ -1,225 +0,0 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Serein.NodeFlow.Base
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
||||
/// </summary>
|
||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||
{
|
||||
|
||||
public NodeModelBase()
|
||||
{
|
||||
PreviousNodes = [];
|
||||
SuccessorNodes = [];
|
||||
foreach (ConnectionType ctType in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
PreviousNodes[ctType] = new List<NodeModelBase>();
|
||||
SuccessorNodes[ctType] = new List<NodeModelBase>();
|
||||
}
|
||||
DebugSetting = new NodeDebugSetting();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 调试功能
|
||||
/// </summary>
|
||||
public NodeDebugSetting DebugSetting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点对应的控件类型
|
||||
/// </summary>
|
||||
public NodeControlType ControlType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 方法描述,对应DLL的方法
|
||||
/// </summary>
|
||||
public MethodDetails MethodDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点guid
|
||||
/// </summary>
|
||||
public string Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 显示名称
|
||||
/// </summary>
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为起点控件
|
||||
/// </summary>
|
||||
public bool IsStart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 运行时的上一节点
|
||||
/// </summary>
|
||||
public NodeModelBase PreviousNode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 不同分支的父节点
|
||||
/// </summary>
|
||||
public Dictionary<ConnectionType,List<NodeModelBase>> PreviousNodes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 不同分支的子节点
|
||||
/// </summary>
|
||||
public Dictionary<ConnectionType,List<NodeModelBase>> SuccessorNodes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前节点执行完毕后需要执行的下一个分支的类别
|
||||
/// </summary>
|
||||
public ConnectionType NextOrientation { get; set; } = ConnectionType.None;
|
||||
|
||||
/// <summary>
|
||||
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
||||
/// </summary>
|
||||
public Exception RuningException { get; set; } = null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 控制FlowData在同一时间只会被同一个线程更改。
|
||||
/// </summary>
|
||||
private readonly ReaderWriterLockSlim _flowDataLock = new ReaderWriterLockSlim();
|
||||
private object _flowData;
|
||||
/// <summary>
|
||||
/// 当前传递数据(执行了节点对应的方法,才会存在值)。
|
||||
/// </summary>
|
||||
protected object FlowData
|
||||
{
|
||||
get
|
||||
{
|
||||
_flowDataLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
return _flowData;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_flowDataLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
_flowDataLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
_flowData = value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_flowDataLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
||||
/// </summary>
|
||||
//public class NodeModelBaseBuilder
|
||||
//{
|
||||
// public NodeModelBaseBuilder(NodeModelBase builder)
|
||||
// {
|
||||
// this.ControlType = builder.ControlType;
|
||||
// this.MethodDetails = builder.MethodDetails;
|
||||
// this.Guid = builder.Guid;
|
||||
// this.DisplayName = builder.DisplayName;
|
||||
// this.IsStart = builder.IsStart;
|
||||
// this.PreviousNode = builder.PreviousNode;
|
||||
// this.PreviousNodes = builder.PreviousNodes;
|
||||
// this.SucceedBranch = builder.SucceedBranch;
|
||||
// this.FailBranch = builder.FailBranch;
|
||||
// this.ErrorBranch = builder.ErrorBranch;
|
||||
// this.UpstreamBranch = builder.UpstreamBranch;
|
||||
// this.FlowState = builder.FlowState;
|
||||
// this.RuningException = builder.RuningException;
|
||||
// this.FlowData = builder.FlowData;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// 节点对应的控件类型
|
||||
// /// </summary>
|
||||
// public NodeControlType ControlType { get; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 方法描述,对应DLL的方法
|
||||
// /// </summary>
|
||||
// public MethodDetails MethodDetails { get; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 节点guid
|
||||
// /// </summary>
|
||||
// public string Guid { get; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 显示名称
|
||||
// /// </summary>
|
||||
// public string DisplayName { get;}
|
||||
|
||||
// /// <summary>
|
||||
// /// 是否为起点控件
|
||||
// /// </summary>
|
||||
// public bool IsStart { get; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 运行时的上一节点
|
||||
// /// </summary>
|
||||
// public NodeModelBase? PreviousNode { get; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 上一节点集合
|
||||
// /// </summary>
|
||||
// public List<NodeModelBase> PreviousNodes { get; } = [];
|
||||
|
||||
// /// <summary>
|
||||
// /// 下一节点集合(真分支)
|
||||
// /// </summary>
|
||||
// public List<NodeModelBase> SucceedBranch { get; } = [];
|
||||
|
||||
// /// <summary>
|
||||
// /// 下一节点集合(假分支)
|
||||
// /// </summary>
|
||||
// public List<NodeModelBase> FailBranch { get; } = [];
|
||||
|
||||
// /// <summary>
|
||||
// /// 异常分支
|
||||
// /// </summary>
|
||||
// public List<NodeModelBase> ErrorBranch { get; } = [];
|
||||
|
||||
// /// <summary>
|
||||
// /// 上游分支
|
||||
// /// </summary>
|
||||
// public List<NodeModelBase> UpstreamBranch { get; } = [];
|
||||
|
||||
// /// <summary>
|
||||
// /// 当前执行状态(进入真分支还是假分支,异常分支在异常中确定)
|
||||
// /// </summary>
|
||||
// public FlowStateType FlowState { get; set; } = FlowStateType.None;
|
||||
|
||||
// /// <summary>
|
||||
// /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
||||
// /// </summary>
|
||||
// public Exception RuningException { get; set; } = null;
|
||||
|
||||
// /// <summary>
|
||||
// /// 当前传递数据(执行了节点对应的方法,才会存在值)
|
||||
// /// </summary>
|
||||
// public object? FlowData { get; set; } = null;
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,457 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.Library.Ex;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using Serein.NodeFlow.Tool.SereinExpression;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
||||
|
||||
namespace Serein.NodeFlow.Base
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
||||
/// </summary>
|
||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||
{
|
||||
|
||||
|
||||
#region 调试中断
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 不再中断
|
||||
/// </summary>
|
||||
public void CancelInterrupt()
|
||||
{
|
||||
this.DebugSetting.InterruptClass = InterruptClass.None;
|
||||
DebugSetting.CancelInterruptCallback?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 导出/导入项目文件节点信息
|
||||
|
||||
internal abstract Parameterdata[] GetParameterdatas();
|
||||
public virtual NodeInfo ToInfo()
|
||||
{
|
||||
// if (MethodDetails == null) return null;
|
||||
|
||||
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
|
||||
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
|
||||
var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
|
||||
var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
|
||||
|
||||
// 生成参数列表
|
||||
Parameterdata[] parameterData = GetParameterdatas();
|
||||
|
||||
return new NodeInfo
|
||||
{
|
||||
Guid = Guid,
|
||||
MethodName = MethodDetails?.MethodName,
|
||||
Label = DisplayName ?? "",
|
||||
Type = this.GetType().ToString(),
|
||||
TrueNodes = trueNodes.ToArray(),
|
||||
FalseNodes = falseNodes.ToArray(),
|
||||
UpstreamNodes = upstreamNodes.ToArray(),
|
||||
ParameterData = parameterData.ToArray(),
|
||||
ErrorNodes = errorNodes.ToArray(),
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
|
||||
{
|
||||
this.Guid = nodeInfo.Guid;
|
||||
if (this.MethodDetails is not null)
|
||||
{
|
||||
for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
|
||||
{
|
||||
Parameterdata? pd = nodeInfo.ParameterData[i];
|
||||
this.MethodDetails.ParameterDetailss[i].IsExplicitData = pd.State;
|
||||
this.MethodDetails.ParameterDetailss[i].DataValue = pd.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 节点方法的执行
|
||||
|
||||
/// <summary>
|
||||
/// 是否应该退出执行
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="flowCts"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsBradk(IDynamicContext context, CancellationTokenSource? flowCts)
|
||||
{
|
||||
// 上下文不再执行
|
||||
if(context.RunState == RunState.Completion)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 不存在全局触发器时,流程运行状态被设置为完成,退出执行,用于打断无限循环分支。
|
||||
if (flowCts is null && context.Env.FlowState == RunState.Completion)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// 如果存在全局触发器,且触发器的执行任务已经被取消时,退出执行。
|
||||
if (flowCts is not null)
|
||||
{
|
||||
if (flowCts.IsCancellationRequested)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public async Task StartFlowAsync(IDynamicContext context)
|
||||
{
|
||||
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
||||
stack.Push(this);
|
||||
var flowCts = context.Env.IOC.Get<CancellationTokenSource>(FlowStarter.FlipFlopCtsName);
|
||||
bool hasFlipflow = flowCts != null;
|
||||
while (stack.Count > 0) // 循环中直到栈为空才会退出循环
|
||||
{
|
||||
await Task.Delay(0);
|
||||
// 从栈中弹出一个节点作为当前节点进行处理
|
||||
var currentNode = stack.Pop();
|
||||
|
||||
#region 执行相关
|
||||
|
||||
// 筛选出上游分支
|
||||
var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].ToArray();
|
||||
for (int index = 0; index < upstreamNodes.Length; index++)
|
||||
{
|
||||
NodeModelBase? upstreamNode = upstreamNodes[index];
|
||||
if (upstreamNode is not null && upstreamNode.DebugSetting.IsEnable)
|
||||
{
|
||||
if (upstreamNode.DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
|
||||
{
|
||||
var cancelType = await upstreamNode.DebugSetting.GetInterruptTask();
|
||||
await Console.Out.WriteLineAsync($"[{upstreamNode.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
|
||||
}
|
||||
upstreamNode.PreviousNode = currentNode;
|
||||
await upstreamNode.StartFlowAsync(context); // 执行流程节点的上游分支
|
||||
if (upstreamNode.NextOrientation == ConnectionType.IsError)
|
||||
{
|
||||
// 如果上游分支执行失败,不再继续执行
|
||||
// 使上游节点(仅上游节点本身,不包含上游节点的后继节点)
|
||||
// 具备通过抛出异常中断流程的能力
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (IsBradk(context, flowCts)) break; // 退出执行
|
||||
// 上游分支执行完成,才执行当前节点
|
||||
object? newFlowData = await currentNode.ExecutingAsync(context);
|
||||
if (IsBradk(context, flowCts)) break; // 退出执行
|
||||
|
||||
await RefreshFlowDataAndExpInterrupt(context, currentNode, newFlowData); // 执行当前节点后刷新数据
|
||||
#endregion
|
||||
|
||||
|
||||
#region 执行完成
|
||||
|
||||
// 选择后继分支
|
||||
var nextNodes = currentNode.SuccessorNodes[currentNode.NextOrientation];
|
||||
|
||||
// 将下一个节点集合中的所有节点逆序推入栈中
|
||||
for (int i = nextNodes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// 筛选出启用的节点的节点
|
||||
if (nextNodes[i].DebugSetting.IsEnable)
|
||||
{
|
||||
nextNodes[i].PreviousNode = currentNode;
|
||||
stack.Push(nextNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 执行节点对应的方法
|
||||
/// </summary>
|
||||
/// <param name="context">流程上下文</param>
|
||||
/// <returns>节点传回数据对象</returns>
|
||||
public virtual async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
{
|
||||
#region 调试中断
|
||||
|
||||
if (DebugSetting.InterruptClass != InterruptClass.None) // 执行触发检查是否需要中断
|
||||
{
|
||||
var cancelType = await this.DebugSetting.GetInterruptTask(); // 等待中断结束
|
||||
await Console.Out.WriteLineAsync($"[{this.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
MethodDetails? md = MethodDetails;
|
||||
//var del = md.MethodDelegate.Clone();
|
||||
if (md is null)
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
||||
}
|
||||
if (!context.Env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在对应委托");
|
||||
}
|
||||
md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
|
||||
object instance = md.ActingInstance;
|
||||
|
||||
|
||||
object? result = null;
|
||||
|
||||
try
|
||||
{
|
||||
object?[]? args = GetParameters(context, this, md);
|
||||
result = await dd.InvokeAsync(md.ActingInstance, args);
|
||||
NextOrientation = ConnectionType.IsSucceed;
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
||||
NextOrientation = ConnectionType.IsError;
|
||||
RuningException = ex;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取对应的参数数组
|
||||
/// </summary>
|
||||
public static object?[]? GetParameters(IDynamicContext context, NodeModelBase nodeModel, MethodDetails md)
|
||||
{
|
||||
// 用正确的大小初始化参数数组
|
||||
if (md.ParameterDetailss.Length == 0)
|
||||
{
|
||||
return null;// md.ActingInstance
|
||||
}
|
||||
|
||||
object?[]? parameters = new object[md.ParameterDetailss.Length];
|
||||
var flowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据
|
||||
var previousDataType = flowData?.GetType();
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
|
||||
object? inputParameter; // 存放解析的临时参数
|
||||
var ed = md.ParameterDetailss[i]; // 方法入参描述
|
||||
|
||||
|
||||
if (ed.IsExplicitData) // 判断是否使用显示的输入参数
|
||||
{
|
||||
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && flowData is not null)
|
||||
{
|
||||
// 执行表达式从上一节点获取对象
|
||||
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, flowData, out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 使用输入的固定值
|
||||
inputParameter = ed.DataValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inputParameter = flowData; // 使用上一节点的对象
|
||||
}
|
||||
|
||||
// 入参存在取值转换器
|
||||
if (ed.ExplicitType.IsEnum && ed.Convertor is not null)
|
||||
{
|
||||
if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum))
|
||||
{
|
||||
var value = ed.Convertor(resultEnum);
|
||||
if (value is not null)
|
||||
{
|
||||
parameters[i] = value;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("转换器调用失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 入参存在类型转换器,获取枚举转换器中记录的枚举
|
||||
if (ed.ExplicitType.IsEnum && ed.DataType != ed.ExplicitType)
|
||||
{
|
||||
if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum)) // 获取对应的枚举项
|
||||
{
|
||||
// 获取绑定的类型
|
||||
var type = EnumHelper.GetBoundValue(ed.ExplicitType, resultEnum, attr => attr.Value);
|
||||
if (type is Type enumBindType && enumBindType is not null)
|
||||
{
|
||||
var value = context.Env.IOC.Instantiate(enumBindType);
|
||||
if (value is not null)
|
||||
{
|
||||
parameters[i] = value;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (ed.DataType.IsValueType)
|
||||
{
|
||||
var valueStr = inputParameter?.ToString();
|
||||
parameters[i] = valueStr.ToValueData(ed.DataType);
|
||||
}
|
||||
else
|
||||
{
|
||||
var valueStr = inputParameter?.ToString();
|
||||
parameters[i] = ed.DataType switch
|
||||
{
|
||||
Type t when t == typeof(string) => valueStr,
|
||||
Type t when t == typeof(IDynamicContext) => context, // 上下文
|
||||
Type t when t == typeof(DateTime) => string.IsNullOrEmpty(valueStr) ? 0 : DateTime.Parse(valueStr),
|
||||
|
||||
Type t when t == typeof(MethodDetails) => md, // 节点方法描述
|
||||
Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类
|
||||
|
||||
Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
|
||||
Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
|
||||
_ => inputParameter,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新节点数据,并检查监视表达式是否生效
|
||||
/// </summary>
|
||||
/// <param name="context">上下文</param>
|
||||
/// <param name="nodeModel">节点Moel</param>
|
||||
/// <param name="newData">新的数据</param>
|
||||
/// <returns></returns>
|
||||
public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object? newData = null)
|
||||
{
|
||||
string guid = nodeModel.Guid;
|
||||
if (newData is not null)
|
||||
{
|
||||
await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象
|
||||
await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点
|
||||
nodeModel.FlowData = newData; // 替换数据
|
||||
context.AddOrUpdate(guid, nodeModel); // 上下文中更新数据
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object? data, int monitorType)
|
||||
{
|
||||
MonitorObjectEventArgs.ObjSourceType sourceType;
|
||||
string? key;
|
||||
if (monitorType == 0)
|
||||
{
|
||||
key = data?.GetType()?.FullName;
|
||||
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = nodeModel.Guid;
|
||||
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
|
||||
}
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Env.CheckObjMonitorState(key, out List<string> exps)) // 如果新的数据处于查看状态,通知UI进行更新?交给运行环境判断?
|
||||
{
|
||||
context.Env.MonitorObjectNotification(nodeModel.Guid, data, sourceType); // 对象处于监视状态,通知UI更新数据显示
|
||||
if (exps.Count > 0)
|
||||
{
|
||||
// 表达式环境下判断是否需要执行中断
|
||||
bool isExpInterrupt = false;
|
||||
string? exp = "";
|
||||
// 判断执行监视表达式,直到为 true 时退出
|
||||
for (int i = 0; i < exps.Count && !isExpInterrupt; i++)
|
||||
{
|
||||
exp = exps[i];
|
||||
if (string.IsNullOrEmpty(exp)) continue;
|
||||
isExpInterrupt = SereinConditionParser.To(data, exp);
|
||||
}
|
||||
|
||||
if (isExpInterrupt) // 触发中断
|
||||
{
|
||||
InterruptClass interruptClass = InterruptClass.Branch; // 分支中断
|
||||
if (context.Env.SetNodeInterrupt(nodeModel.Guid, interruptClass))
|
||||
{
|
||||
context.Env.TriggerInterrupt(nodeModel.Guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp);
|
||||
var cancelType = await nodeModel.DebugSetting.GetInterruptTask();
|
||||
await Console.Out.WriteLineAsync($"[{data}]中断已{cancelType},开始执行后继分支");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 释放对象
|
||||
/// </summary>
|
||||
public void ReleaseFlowData()
|
||||
{
|
||||
if (typeof(IDisposable).IsAssignableFrom(FlowData?.GetType()) && FlowData is IDisposable disposable)
|
||||
{
|
||||
disposable?.Dispose();
|
||||
}
|
||||
this.FlowData = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取节点数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object? GetFlowData()
|
||||
{
|
||||
return this.FlowData;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 标识一个类中的某些字段需要生成相应代码
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
|
||||
public sealed class AutoPropertyAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>属性路径</para>
|
||||
/// <para>CustomNode : 自定义节点</para>
|
||||
/// </summary>
|
||||
public string ValuePath = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动生成环境的属性
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = true)]
|
||||
public sealed class PropertyInfoAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否通知UI
|
||||
/// </summary>
|
||||
public bool IsNotification = false;
|
||||
/// <summary>
|
||||
/// 是否使用Console.WriteLine打印
|
||||
/// </summary>
|
||||
public bool IsPrint = false;
|
||||
/// <summary>
|
||||
/// 是否禁止参数进行修改(初始化后不能再通过setter修改)
|
||||
/// </summary>
|
||||
public bool IsProtection = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Serein.Library.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示参数为url中的数据(Get请求中不需要显式标注)
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class IsUrlDataAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示入参参数为整个boby的数据
|
||||
/// <para>
|
||||
/// 例如:User类型含有int id、string name字段</para>
|
||||
/// <para>
|
||||
/// ① Add(User user)</para>
|
||||
/// <para>请求需要传入的json为
|
||||
/// {"user":{
|
||||
/// "id":2,
|
||||
/// "name":"李志忠"}}</para>
|
||||
/// <para>
|
||||
/// ② Add([Boby]User user)</para>
|
||||
/// <para>请求需要传入的json为
|
||||
/// {"id":2,"name":"李志忠"}</para>
|
||||
///
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public sealed class IsBobyDataAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示该控制器会被自动注册(与程序集同一命名空间,暂时不支持运行时自动加载DLL,需要手动注册)
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class AutoHostingAttribute(string url = "") : Attribute
|
||||
{
|
||||
public string Url { get; } = url;
|
||||
}
|
||||
/// <summary>
|
||||
/// 表示该属性为自动注入依赖项
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class AutoInjectionAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 方法的接口类型与附加URL
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 假设UserController.Add()的WebAPI特性中
|
||||
/// http是HTTP.POST
|
||||
/// url被显示标明“temp”
|
||||
/// 那么请求的接口是POST,URL是
|
||||
/// [http://localhost:8080]/user/add/temp
|
||||
/// </remarks>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="url"></param>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
|
||||
public sealed class WebApiAttribute() : Attribute
|
||||
|
||||
{
|
||||
public API Type ;
|
||||
public string Url ;
|
||||
/// <summary>
|
||||
/// 方法名称不作为url的部分
|
||||
/// </summary>
|
||||
public bool IsUrl;
|
||||
}
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
|
||||
public sealed class ApiPostAttribute() : Attribute
|
||||
|
||||
{
|
||||
public string Url;
|
||||
/// <summary>
|
||||
/// 方法名称不作为url的部分
|
||||
/// </summary>
|
||||
public bool IsUrl = true;
|
||||
}
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
|
||||
public sealed class ApiGetAttribute() : Attribute
|
||||
|
||||
{
|
||||
public string Url;
|
||||
/// <summary>
|
||||
/// 方法名称不作为url的部分
|
||||
/// </summary>
|
||||
public bool IsUrl = true;
|
||||
}
|
||||
|
||||
/*public sealed class WebApiAttribute(API http, bool isUrl = true, string url = "") : Attribute
|
||||
{
|
||||
public API Http { get; } = http;
|
||||
public string Url { get; } = url;
|
||||
/// <summary>
|
||||
/// 方法名称不作为url的部分
|
||||
/// </summary>
|
||||
public bool IsUrl { get; } = isUrl;
|
||||
}*/
|
||||
public enum API
|
||||
{
|
||||
POST,
|
||||
GET,
|
||||
//PUT,
|
||||
//DELETE
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Serein.Library.Http
|
||||
{
|
||||
public class ControllerBase
|
||||
{
|
||||
|
||||
public string Url { get; set; }
|
||||
|
||||
public string BobyData { get; set; }
|
||||
|
||||
public string GetLog(Exception ex)
|
||||
{
|
||||
return "Url : " + Url + Environment.NewLine +
|
||||
"Ex : " + ex.Message + Environment.NewLine +
|
||||
"Data : " + BobyData + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,768 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Enum = System.Enum;
|
||||
using Type = System.Type;
|
||||
|
||||
namespace Serein.Library.Http
|
||||
{
|
||||
/*
|
||||
Router类负责解析请求的url,url参数,boby参数
|
||||
根据url
|
||||
|
||||
web server 监听类,监听外部的请求
|
||||
router 选择对应的控制器
|
||||
agent 负责传入对应的参数,注入依赖
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 路由注册与解析
|
||||
/// </summary>
|
||||
public class Router
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, bool> _controllerAutoHosting; // 存储是否实例化
|
||||
private readonly ConcurrentDictionary<string, Type> _controllerTypes; // 存储控制器类型
|
||||
private readonly ConcurrentDictionary<string, object> _controllerInstances; // 存储控制器实例对象
|
||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>> _routes; // 用于存储路由信息
|
||||
|
||||
private readonly SereinIOC serviceRegistry; // 用于存储路由信息
|
||||
|
||||
//private Type PostRequest;
|
||||
|
||||
public Router(ISereinIOC serviceRegistry) // 构造函数,初始化 Router 类的新实例
|
||||
{
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
|
||||
_routes = new ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>>(); // 初始化路由字典
|
||||
|
||||
_controllerAutoHosting = new ConcurrentDictionary<string, bool>(); // 初始化控制器实例对象字典
|
||||
_controllerTypes = new ConcurrentDictionary<string, Type>(); // 初始化控制器实例对象字典
|
||||
_controllerInstances = new ConcurrentDictionary<string, object>(); // 初始化控制器实例对象字典
|
||||
|
||||
foreach (API method in Enum.GetValues(typeof(API))) // 遍历 HTTP 枚举类型的所有值
|
||||
{
|
||||
_routes.TryAdd(method.ToString(), new ConcurrentDictionary<string, MethodInfo>()); // 初始化每种 HTTP 方法对应的路由字典
|
||||
}
|
||||
|
||||
// 获取当前程序集
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// 获取包含“Controller”名称的类型
|
||||
var controllerTypes = assembly.GetTypes()
|
||||
.Where(t => t.Name.Contains("Controller"));
|
||||
|
||||
Type baseAttribute = typeof(AutoHostingAttribute);
|
||||
Type baseController = typeof(ControllerBase);
|
||||
foreach (var controllerType in controllerTypes)
|
||||
{
|
||||
if (controllerType.IsSubclassOf(baseController) && controllerType.IsDefined(baseAttribute))
|
||||
{
|
||||
|
||||
// 如果属于控制器,并标记了AutoHosting特性,进行自动注册
|
||||
AutoRegisterAutoController(controllerType);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 自动注册 自动实例化控制器 类型
|
||||
/// </summary>
|
||||
/// <param name="controllerType"></param>
|
||||
public void AutoRegisterAutoController(Type controllerType) // 方法声明,用于注册并实例化控制器类型
|
||||
{
|
||||
if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回
|
||||
|
||||
var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
|
||||
if (autoHostingAttribute != null) {
|
||||
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
||||
{
|
||||
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
|
||||
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
|
||||
if( apiGetAttribute == null && apiPostAttribute == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
||||
{
|
||||
Type = apiGetAttribute != null ? API.GET : API.POST,
|
||||
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
||||
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
||||
};
|
||||
|
||||
|
||||
|
||||
if (apiPostAttribute != null) // 如果存在 WebAPIAttribute 属性
|
||||
{
|
||||
var url = AddRoutesUrl(autoHostingAttribute,
|
||||
webApiAttribute,
|
||||
controllerType, method);
|
||||
Console.WriteLine(url);
|
||||
if (url == null) continue;
|
||||
_controllerAutoHosting[url] = true;
|
||||
_controllerTypes[url] = controllerType;
|
||||
|
||||
_controllerInstances[url] = null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
|
||||
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
|
||||
{
|
||||
var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
|
||||
Console.WriteLine(url);
|
||||
if (url == null) continue;
|
||||
_controllerAutoHosting[url] = true;
|
||||
_controllerTypes[url] = controllerType;
|
||||
_controllerInstances[url] = null;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 手动注册 自动实例化控制器实例
|
||||
/// </summary>
|
||||
public void RegisterAutoController<T>() // 方法声明,用于动态注册路由
|
||||
{
|
||||
Type controllerType = typeof(T); // 获取控制器实例的类型
|
||||
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
||||
{
|
||||
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
|
||||
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
|
||||
if (apiGetAttribute == null && apiPostAttribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
||||
{
|
||||
Type = apiGetAttribute != null ? API.GET : API.POST,
|
||||
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
||||
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
||||
};
|
||||
|
||||
|
||||
|
||||
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
|
||||
|
||||
if (url == null) continue;
|
||||
_controllerAutoHosting[url] = true;
|
||||
_controllerTypes[url] = controllerType;
|
||||
|
||||
_controllerInstances[url] = null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 手动注册 实例持久控制器实例
|
||||
/// </summary>
|
||||
/// <param name="controllerInstance"></param>
|
||||
public void RegisterController<TController>(TController controllerInstance) where TController : ControllerBase // 方法声明,用于动态注册路由
|
||||
{
|
||||
if(controllerInstance == null) return;
|
||||
Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型
|
||||
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
||||
{
|
||||
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
|
||||
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
|
||||
if (apiGetAttribute == null && apiPostAttribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
||||
{
|
||||
Type = apiGetAttribute != null ? API.GET : API.POST,
|
||||
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
||||
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
||||
};
|
||||
|
||||
|
||||
|
||||
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
|
||||
|
||||
if (url == null) continue;
|
||||
_controllerInstances[url] = controllerInstance;
|
||||
_controllerAutoHosting[url] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从方法中收集路由信息
|
||||
/// </summary>
|
||||
/// <param name="controllerType"></param>
|
||||
public string AddRoutesUrl(AutoHostingAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method)
|
||||
{
|
||||
string controllerName;
|
||||
if (autoHostingAttribute == null || string.IsNullOrWhiteSpace(autoHostingAttribute.Url))
|
||||
{
|
||||
controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
|
||||
}
|
||||
else
|
||||
{
|
||||
controllerName = autoHostingAttribute.Url;
|
||||
}
|
||||
|
||||
var httpMethod = webAttribute.Type; // 获取 HTTP 方法
|
||||
var customUrl = webAttribute.Url; // 获取自定义 URL
|
||||
|
||||
string url;
|
||||
|
||||
if (webAttribute.IsUrl)
|
||||
{
|
||||
|
||||
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
|
||||
{
|
||||
url = $"/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
|
||||
}
|
||||
else
|
||||
{
|
||||
customUrl = CleanUrl(customUrl);
|
||||
url = $"/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||
}
|
||||
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
|
||||
{
|
||||
url = $"/{controllerName}".ToLower(); // 构建默认 URL
|
||||
}
|
||||
else
|
||||
{
|
||||
customUrl = CleanUrl(customUrl);
|
||||
url = $"/{controllerName}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||
}
|
||||
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
||||
}
|
||||
|
||||
return url;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 收集路由信息
|
||||
/// </summary>
|
||||
/// <param name="controllerType"></param>
|
||||
public void CollectRoutes(Type controllerType)
|
||||
{
|
||||
string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
|
||||
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
||||
{
|
||||
var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
|
||||
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
|
||||
{
|
||||
var customUrl = routeAttribute.Url; // 获取自定义 URL
|
||||
string url;
|
||||
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
|
||||
{
|
||||
url = $"/api/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
|
||||
}
|
||||
else
|
||||
{
|
||||
customUrl = CleanUrl(customUrl);
|
||||
url = $"/api/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||
}
|
||||
var httpMethod = routeAttribute.Type; // 获取 HTTP 方法
|
||||
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 解析路由,调用对应的方法
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> RouteAsync(HttpListenerContext context)
|
||||
{
|
||||
var request = context.Request; // 获取请求对象
|
||||
var response = context.Response; // 获取响应对象
|
||||
var url = request.Url; // 获取请求的 URL
|
||||
var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法
|
||||
|
||||
var template = request.Url.AbsolutePath.ToLower();
|
||||
|
||||
|
||||
if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
|
||||
|
||||
ControllerBase controllerInstance;
|
||||
if (!_controllerAutoHosting[template])
|
||||
{
|
||||
controllerInstance = (ControllerBase)_controllerInstances[template];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
controllerInstance = (ControllerBase)serviceRegistry.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (controllerInstance == null)
|
||||
{
|
||||
return false; // 未找到控制器实例
|
||||
}
|
||||
|
||||
controllerInstance.Url = url.AbsolutePath;
|
||||
object result;
|
||||
switch (httpMethod) // 根据请求的 HTTP 方法执行不同的操作
|
||||
{
|
||||
case "GET": // 如果是 GET 请求,传入方法、控制器、url参数
|
||||
result = InvokeControllerMethodWithRouteValues(method, controllerInstance, routeValues);
|
||||
break;
|
||||
case "POST": // POST 请求传入方法、控制器、请求体内容,url参数
|
||||
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
|
||||
controllerInstance.BobyData = requestBody;
|
||||
var requestJObject = requestBody.FromJSON<object>();
|
||||
|
||||
result = InvokeControllerMethod(method, controllerInstance, requestJObject, routeValues);
|
||||
break;
|
||||
default:
|
||||
|
||||
result = null;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Return(response, result); // 返回结果
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetLog(string Url, string BobyData = "")
|
||||
{
|
||||
return Environment.NewLine +
|
||||
"Url : " + Url + Environment.NewLine +
|
||||
"Data : " + BobyData + Environment.NewLine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET请求的控制器方法
|
||||
/// </summary>
|
||||
private object InvokeControllerMethodWithRouteValues(MethodInfo method, object controllerInstance, Dictionary<string, string> routeValues)
|
||||
{
|
||||
object[] parameters = GetMethodParameters(method, routeValues);
|
||||
return InvokeMethod(method, controllerInstance, parameters);
|
||||
}
|
||||
|
||||
private static readonly Dictionary<MethodInfo, ParameterInfo[]> methodParameterCache = new Dictionary<MethodInfo, ParameterInfo[]>();
|
||||
/// <summary>
|
||||
/// POST请求的调用控制器方法
|
||||
/// </summary>
|
||||
public object InvokeControllerMethod(MethodInfo method, object controllerInstance, dynamic requestData, Dictionary<string, string> routeValues)
|
||||
{
|
||||
object[] cachedMethodParameters;
|
||||
|
||||
if (!methodParameterCache.TryGetValue(method, out ParameterInfo[] parameters))
|
||||
{
|
||||
parameters = method.GetParameters();
|
||||
}
|
||||
|
||||
cachedMethodParameters = new object[parameters.Length];
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
string paramName = parameters[i].Name;
|
||||
bool isUrlData = parameters[i].GetCustomAttribute(typeof(IsUrlDataAttribute)) != null;
|
||||
bool isBobyData = parameters[i].GetCustomAttribute(typeof(IsBobyDataAttribute)) != null;
|
||||
|
||||
if (isUrlData)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(paramName) && routeValues.TryGetValue(paramName, out string value))
|
||||
{
|
||||
cachedMethodParameters[i] = ConvertValue(value, parameters[i].ParameterType);
|
||||
}
|
||||
else
|
||||
{
|
||||
cachedMethodParameters[i] = null;
|
||||
}
|
||||
}
|
||||
else if (isBobyData)
|
||||
{
|
||||
cachedMethodParameters[i] = ConvertValue(requestData.ToString(), parameters[i].ParameterType);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (requestData.ContainsKey(paramName))
|
||||
{
|
||||
if (parameters[i].ParameterType == typeof(string))
|
||||
{
|
||||
cachedMethodParameters[i] = requestData[paramName].ToString();
|
||||
}
|
||||
else if (parameters[i].ParameterType == typeof(bool))
|
||||
{
|
||||
cachedMethodParameters[i] = requestData[paramName?.ToLower()].ToBool();
|
||||
}
|
||||
else if (parameters[i].ParameterType == typeof(int))
|
||||
{
|
||||
cachedMethodParameters[i] = requestData[paramName].ToInt();
|
||||
}
|
||||
else if (parameters[i].ParameterType == typeof(double))
|
||||
{
|
||||
cachedMethodParameters[i] = requestData[paramName].ToDouble();
|
||||
}
|
||||
else
|
||||
{
|
||||
cachedMethodParameters[i] = ConvertValue(requestData[paramName], parameters[i].ParameterType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cachedMethodParameters[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存方法和参数的映射
|
||||
//methodParameterCache[method] = cachedMethodParameters;
|
||||
|
||||
|
||||
// 调用方法
|
||||
|
||||
return method.Invoke(controllerInstance, cachedMethodParameters);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 检查方法入参参数类型,返回对应的入参数组
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="routeValues"></param>
|
||||
/// <returns></returns>
|
||||
private object[] GetMethodParameters(MethodInfo method, Dictionary<string, string> routeValues)
|
||||
{
|
||||
ParameterInfo[] methodParameters = method.GetParameters();
|
||||
object[] parameters = new object[methodParameters.Length];
|
||||
|
||||
for (int i = 0; i < methodParameters.Length; i++)
|
||||
{
|
||||
|
||||
string paramName = methodParameters[i].Name;
|
||||
|
||||
|
||||
if (routeValues.TryGetValue(paramName, out string value))
|
||||
{
|
||||
parameters[i] = ConvertValue(value, methodParameters[i].ParameterType);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
parameters[i] = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/*/// <summary>
|
||||
/// 转为对应的类型
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <returns></returns>
|
||||
private object ConvertValue(object value, Type targetType)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject(value.ToString(), targetType);
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
catch (JsonSerializationException ex)
|
||||
{
|
||||
// 如果无法转为对应的JSON对象
|
||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
||||
int endIndex = ex.Message.IndexOf("'", startIndex); // 查找类型信息结束的索引
|
||||
var typeInfo = ex.Message.Substring(startIndex, endIndex - startIndex); // 提取出错类型信息,该怎么传出去?
|
||||
return null;
|
||||
}
|
||||
catch // (Exception ex)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}*/
|
||||
/// <summary>
|
||||
/// 转为对应的类型
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <returns></returns>
|
||||
private object ConvertValue(string value, Type targetType)
|
||||
{
|
||||
if(targetType == typeof(string))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
return JsonConvert.DeserializeObject(value.ToString(), targetType);
|
||||
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
catch (JsonSerializationException ex)
|
||||
{
|
||||
// 如果无法转为对应的JSON对象
|
||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
||||
int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引
|
||||
var typeInfo = ex.Message[startIndex..endIndex]; // 提取出错类型信息,该怎么传出去?
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
catch // (Exception ex)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调用控制器方法传入参数
|
||||
/// </summary>
|
||||
/// <param name="method">方法</param>
|
||||
/// <param name="controllerInstance">控制器实例</param>
|
||||
/// <param name="methodParameters">参数列表</param>
|
||||
/// <returns></returns>
|
||||
private static object InvokeMethod(MethodInfo method, object controllerInstance, object[] methodParameters)
|
||||
{
|
||||
|
||||
object result = null;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
result = method?.Invoke(controllerInstance, methodParameters);
|
||||
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
string targetType = ExtractTargetTypeFromExceptionMessage(ex.Message);
|
||||
|
||||
// 如果方法调用失败
|
||||
result = new
|
||||
{
|
||||
error = $"函数签名类型[{targetType}]不符合",
|
||||
};
|
||||
}
|
||||
catch (JsonSerializationException ex)
|
||||
{
|
||||
|
||||
// 查找类型信息开始的索引
|
||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length;
|
||||
// 查找类型信息结束的索引
|
||||
int endIndex = ex.Message.IndexOf('\'');
|
||||
// 提取类型信息
|
||||
string typeInfo = ex.Message[startIndex..endIndex];
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
|
||||
return result; // 调用方法并返回结果
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 方法声明,用于解析 URL 获取路由参数
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
/// <returns></returns>
|
||||
private static Dictionary<string, string> GetUrlData(Uri uri)
|
||||
{
|
||||
Dictionary<string, string> routeValues = [];
|
||||
|
||||
var pathParts = uri.ToString().Split('?'); // 拆分 URL,获取路径部分
|
||||
|
||||
if (pathParts.Length > 1) // 如果包含查询字符串
|
||||
{
|
||||
var queryParams = HttpUtility.ParseQueryString(pathParts[1]); // 解析查询字符串
|
||||
|
||||
foreach (string key in queryParams) // 遍历查询字符串的键值对
|
||||
{
|
||||
if (key == null) continue;
|
||||
|
||||
routeValues[key] = queryParams[key]; // 将键值对添加到路由参数字典中
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return routeValues; // 返回路由参数字典
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取Body中的消息
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
private static async Task<string> ReadRequestBodyAsync(HttpListenerRequest request)
|
||||
{
|
||||
using (Stream stream = request.InputStream)
|
||||
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
|
||||
{
|
||||
return await reader.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 返回响应消息
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
/// <param name="msg"></param>
|
||||
private static void Return(HttpListenerResponse response, dynamic msg)
|
||||
{
|
||||
string resultData;
|
||||
if (response != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (msg is IEnumerable && msg is not string)
|
||||
{
|
||||
// If msg is a collection (e.g., array or list), serialize it as JArray
|
||||
resultData = JArray.FromObject(msg).ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, serialize it as JObject
|
||||
resultData = JObject.FromObject(msg).ToString();
|
||||
}
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(resultData);
|
||||
response.ContentLength64 = buffer.Length;
|
||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If serialization fails, use the original message's string representation
|
||||
resultData = msg.ToString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析JSON
|
||||
/// </summary>
|
||||
/// <param name="requestBody"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private static dynamic ParseJson(string requestBody)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(requestBody))
|
||||
{
|
||||
throw new Exception("Invalid JSON format");
|
||||
}
|
||||
return JObject.Parse(requestBody);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new Exception("Invalid JSON format");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修正方法特性中的URL格式
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
private static string CleanUrl(string url)
|
||||
{
|
||||
|
||||
while (url.Length > 0 && url[0] == '/') // 去除开头的斜杠
|
||||
{
|
||||
url = url[1..];
|
||||
}
|
||||
|
||||
while (url.Length > 0 && url[^1] == '/') // 去除末尾的斜杠
|
||||
{
|
||||
url = url[..^1];
|
||||
}
|
||||
|
||||
for (int i = 0; i < url.Length - 1; i++) // 去除连续的斜杠
|
||||
{
|
||||
if (url[i] == '/' && url[i + 1] == '/')
|
||||
{
|
||||
url = url.Remove(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return url; // 返回清理后的 URL
|
||||
}
|
||||
/// <summary>
|
||||
/// 从控制器调用方法的异常中获取出出错类型的信息
|
||||
/// </summary>
|
||||
/// <param name="errorMessage"></param>
|
||||
/// <returns></returns>
|
||||
public static string ExtractTargetTypeFromExceptionMessage(string errorMessage)
|
||||
{
|
||||
string targetText = "为类型“";
|
||||
int startIndex = errorMessage.IndexOf(targetText);
|
||||
if (startIndex != -1)
|
||||
{
|
||||
startIndex += targetText.Length;
|
||||
int endIndex = errorMessage.IndexOf('\'');
|
||||
if (endIndex != -1)
|
||||
{
|
||||
return errorMessage[startIndex..endIndex];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Http
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// HTTP接口监听类
|
||||
/// </summary>
|
||||
public class WebServer
|
||||
{
|
||||
private readonly HttpListener listener; // HTTP 监听器
|
||||
private Router router; // 路由器
|
||||
private readonly RequestLimiter requestLimiter; //接口防刷
|
||||
|
||||
|
||||
|
||||
public WebServer()
|
||||
|
||||
{
|
||||
listener = new HttpListener();
|
||||
|
||||
requestLimiter = new RequestLimiter(5, 8);
|
||||
|
||||
}
|
||||
|
||||
// 启动服务器
|
||||
public WebServer Start(string prefixe, ISereinIOC serviceContainer)
|
||||
{
|
||||
try
|
||||
{
|
||||
router = new Router(serviceContainer);
|
||||
if (listener.IsListening)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!prefixe.Substring(prefixe.Length - 1, 1).Equals(@"/"))
|
||||
{
|
||||
prefixe += @"/";
|
||||
}
|
||||
|
||||
|
||||
listener.Prefixes.Add(prefixe); // 添加监听前缀
|
||||
listener.Start(); // 开始监听
|
||||
|
||||
Console.WriteLine($"开始监听:{prefixe}");
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (listener.IsListening)
|
||||
{
|
||||
var context = await listener.GetContextAsync(); // 获取请求上下文
|
||||
_ = Task.Run(() => ProcessRequestAsync(context)); // 处理请求)
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
catch (HttpListenerException ex) when (ex.ErrorCode == 183)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 处理请求
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
private async Task ProcessRequestAsync(HttpListenerContext context)
|
||||
{
|
||||
// 添加CORS头部
|
||||
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
|
||||
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
|
||||
|
||||
// 处理OPTIONS预检请求
|
||||
if (context.Request.HttpMethod == "OPTIONS")
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||
context.Response.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
var isPass = await router.RouteAsync(context); // 路由解析
|
||||
if (isPass)
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||
context.Response.Close(); // 关闭响应
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
context.Response.Close(); // 关闭响应
|
||||
}
|
||||
|
||||
//var isPass = requestLimiter.AllowRequest(context.Request);
|
||||
//if (isPass)
|
||||
//{
|
||||
// // 如果路由没有匹配,返回 404
|
||||
// router.RouteAsync(context); // 路由解析
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
||||
// context.Response.Close(); // 关闭响应
|
||||
//}
|
||||
|
||||
// var request = context.Request;
|
||||
// 获取远程终结点信息
|
||||
//var remoteEndPoint = context.Request.RemoteEndPoint;
|
||||
//// 获取用户的IP地址和端口
|
||||
//IPAddress ipAddress = remoteEndPoint.Address;
|
||||
//int port = remoteEndPoint.Port;
|
||||
//Console.WriteLine("外部连接:" + ipAddress.ToString() + ":" + port);
|
||||
}
|
||||
|
||||
// 停止服务器
|
||||
public void Stop()
|
||||
{
|
||||
if (listener.IsListening)
|
||||
{
|
||||
listener?.Stop(); // 停止监听
|
||||
listener?.Close(); // 关闭监听器
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterAutoController<T>()
|
||||
{
|
||||
//var instance = Activator.CreateInstance(typeof(T));
|
||||
router.RegisterAutoController<T>();
|
||||
}
|
||||
|
||||
/*public void RegisterRoute<T>(T controllerInstance)
|
||||
{
|
||||
router.RegisterRoute(controllerInstance);
|
||||
}*/
|
||||
}
|
||||
/// <summary>
|
||||
/// 判断访问接口的频次是否正常
|
||||
/// </summary>
|
||||
public class RequestLimiter(int seconds, int maxRequests)
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, Queue<DateTime>> requestHistory = new ();
|
||||
private readonly TimeSpan interval = TimeSpan.FromSeconds(seconds);
|
||||
private readonly int maxRequests = maxRequests;
|
||||
|
||||
/// <summary>
|
||||
/// 判断访问接口的频次是否正常
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool AllowRequest(HttpListenerRequest request)
|
||||
{
|
||||
var clientIp = request.RemoteEndPoint.Address.ToString();
|
||||
var clientPort = request.RemoteEndPoint.Port;
|
||||
var clientKey = clientIp + ":" + clientPort;
|
||||
|
||||
var now = DateTime.Now;
|
||||
|
||||
// 尝试从字典中获取请求队列,不存在则创建新的队列
|
||||
var requests = requestHistory.GetOrAdd(clientKey, new Queue<DateTime>());
|
||||
|
||||
lock (requests)
|
||||
{
|
||||
// 移除超出时间间隔的请求记录
|
||||
while (requests.Count > 0 && now - requests.Peek() > interval)
|
||||
{
|
||||
requests.Dequeue();
|
||||
}
|
||||
|
||||
// 如果请求数超过限制,拒绝请求
|
||||
if (requests.Count >= maxRequests)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加当前请求时间,并允许请求
|
||||
requests.Enqueue(now);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user