diff --git a/FlowStartTool/Program.cs b/FlowStartTool/Program.cs index 7994ae7..b84451f 100644 --- a/FlowStartTool/Program.cs +++ b/FlowStartTool/Program.cs @@ -62,7 +62,7 @@ namespace Serein.FlowStartTool } IsRuning = true; - _ = StartFlow(flowProjectData, fileDataPath); + _ = Task.Run(async () => await StartFlow(flowProjectData, fileDataPath)); while (IsRuning) { Console.ReadKey(); diff --git a/Library/FlowNode/DelegateDetails.cs b/Library/FlowNode/DelegateDetails.cs index 8da348a..c8f27b0 100644 --- a/Library/FlowNode/DelegateDetails.cs +++ b/Library/FlowNode/DelegateDetails.cs @@ -82,7 +82,6 @@ namespace Serein.Library else if (_emitMethodType == EmitMethodType.Task && _emitDelegate is Func task) { await task.Invoke(instance, args); - result = null; } else if (_emitMethodType == EmitMethodType.Func && _emitDelegate is Func func) { diff --git a/Library/Network/Http/ApiHandleConfig.cs b/Library/Network/Http/ApiHandleConfig.cs new file mode 100644 index 0000000..c621fe5 --- /dev/null +++ b/Library/Network/Http/ApiHandleConfig.cs @@ -0,0 +1,180 @@ +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Serein.Library.Web; + +namespace Serein.Library.Network +{ + /// + /// api请求处理模块 + /// + public class ApiHandleConfig + { + private readonly DelegateDetails delegateDetails; + + /// + /// Post请求处理方法中,入参参数类型 + /// + public enum PostArgType + { + /// + /// 不做处理 + /// + None, + /// + /// 使用Url参数 + /// + IsUrlData, + /// + /// 使用整体的Boby参数 + /// + IsBobyData, + } + + /// + /// 添加处理配置 + /// + /// + public ApiHandleConfig(MethodInfo methodInfo) + { + delegateDetails = new DelegateDetails(methodInfo); + var parameterInfos = methodInfo.GetParameters(); + ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray(); + ParameterName = parameterInfos.Select(t => t.Name.ToLower()).ToArray(); + + PostArgTypes = parameterInfos.Select(p => + { + bool isUrlData = p.GetCustomAttribute(typeof(UrlAttribute)) != null; + bool isBobyData = p.GetCustomAttribute(typeof(BobyAttribute)) != null; + if (isBobyData) + { + return PostArgType.IsBobyData; + } + else if (isUrlData) + { + return PostArgType.IsUrlData; + } + else + { + return PostArgType.None; + } + }).ToArray(); + + + + } + + private readonly PostArgType[] PostArgTypes; + private readonly string[] ParameterName; + private readonly Type[] ParameterType; + + /// + /// 处理Get请求 + /// + /// + public object[] GetArgsOfGet(Dictionary routeData) + { + object[] args = new object[ParameterType.Length]; + for (int i = 0; i < ParameterType.Length; i++) + { + var type = ParameterType[i]; + var argName = ParameterName[i]; + if (routeData.TryGetValue(argName, out var argValue)) + { + if (type == typeof(string)) + { + args[i] = argValue; + } + else // if (type.IsValueType) + { + args[i] = JsonConvert.DeserializeObject(argValue, type); + } + } + else + { + args[i] = type.IsValueType ? Activator.CreateInstance(type) : null; + } + } + return args; + } + + public object[] GetArgsOfPost(Dictionary routeData, JObject jsonObject) + { + object[] args = new object[ParameterType.Length]; + for (int i = 0; i < ParameterType.Length; i++) + { + var type = ParameterType[i]; + var argName = ParameterName[i]; + if (PostArgTypes[i] == PostArgType.IsUrlData) + { + if (routeData.TryGetValue(argName, out var argValue)) + { + if (type == typeof(string)) + { + args[i] = argValue; + } + else // if (type.IsValueType) + { + args[i] = JsonConvert.DeserializeObject(argValue, type); + } + } + else + { + args[i] = type.IsValueType ? Activator.CreateInstance(type) : null; + } + } + else if (jsonObject != null && PostArgTypes[i] == PostArgType.IsBobyData) + { + args[i] = jsonObject; + } + else if (jsonObject != null) + { + var jsonValue = jsonObject.GetValue(argName); + if (jsonValue is null) + { + // 值类型返回默认值,引用类型返回null + args[i] = type.IsValueType ? Activator.CreateInstance(type) : null; + } + else + { + args[i] = jsonValue.ToObject(type); + } + } + } + return args; + } + + + /// + /// 处理请求 + /// + /// + /// + /// + public async Task HandleAsync(object instance, object[] args) + { + object result = null; + try + { + result = await delegateDetails.InvokeAsync(instance, args); + } + catch (Exception ex) + { + result = null; + await Console.Out.WriteLineAsync(ex.Message); + + } + return result; + + } + + } + + + +} diff --git a/Library/Network/Http/Router.cs b/Library/Network/Http/Router.cs index a2967ee..344afd5 100644 --- a/Library/Network/Http/Router.cs +++ b/Library/Network/Http/Router.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Serein.Library.Api; +using Serein.Library.Network; using Serein.Library.Utils; using System; using System.Collections; @@ -36,177 +37,6 @@ namespace Serein.Library.Web } - /// - /// api请求处理模块 - /// - public class ApiHandleConfig - { - private readonly DelegateDetails delegateDetails; - - /// - /// Post请求处理方法中,入参参数类型 - /// - public enum PostArgType - { - /// - /// 不做处理 - /// - None, - /// - /// 使用Url参数 - /// - IsUrlData, - /// - /// 使用整体的Boby参数 - /// - IsBobyData, - } - - /// - /// 添加处理配置 - /// - /// - public ApiHandleConfig(MethodInfo methodInfo) - { - delegateDetails = new DelegateDetails(methodInfo); - var parameterInfos = methodInfo.GetParameters(); - ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray(); - ParameterName = parameterInfos.Select(t => t.Name.ToLower()).ToArray(); - - PostArgTypes = parameterInfos.Select(p => - { - bool isUrlData = p.GetCustomAttribute(typeof(UrlAttribute)) != null; - bool isBobyData = p.GetCustomAttribute(typeof(BobyAttribute)) != null; - if (isBobyData) - { - return PostArgType.IsBobyData; - } - else if (isUrlData) - { - return PostArgType.IsUrlData; - } - else - { - return PostArgType.None; - } - }).ToArray(); - - - - } - private readonly PostArgType[] PostArgTypes; - private readonly string[] ParameterName; - private readonly Type[] ParameterType; - - /// - /// 处理Get请求 - /// - /// - /// - /// - public async Task HandleGet(object instance, Dictionary routeData) - { - object[] args = new object[ParameterType.Length]; - for (int i = 0; i < ParameterType.Length; i++) - { - var type = ParameterType[i]; - var argName = ParameterName[i]; - if (routeData.TryGetValue(argName, out var argValue)) - { - if (type == typeof(string)) - { - args[i] = argValue; - } - else // if (type.IsValueType) - { - args[i] = JsonConvert.DeserializeObject(argValue, type); - } - } - else - { - args[i] = type.IsValueType ? Activator.CreateInstance(type) : null; - } - - } - object result = null; - try - { - result = await delegateDetails.InvokeAsync(instance, args); - } - catch (Exception ex) - { - result = null; - await Console.Out.WriteLineAsync(ex.Message); - } - return result; - } - - /// - public async Task HandlePost(object instance, JObject jsonObject, Dictionary routeData) - { - object[] args = new object[ParameterType.Length]; - for (int i = 0; i < ParameterType.Length; i++) - { - var type = ParameterType[i]; - var argName = ParameterName[i]; - if (PostArgTypes[i] == PostArgType.IsUrlData) - { - if (routeData.TryGetValue(argName, out var argValue)) - { - if (type == typeof(string)) - { - args[i] = argValue; - } - else // if (type.IsValueType) - { - args[i] = JsonConvert.DeserializeObject(argValue, type); - } - } - else - { - args[i] = type.IsValueType ? Activator.CreateInstance(type) : null; - } - } - else if (PostArgTypes[i] == PostArgType.IsBobyData) - { - args[i] = jsonObject; - } - else - { - var jsonValue = jsonObject.GetValue(argName); - if (jsonValue is null) - { - // 值类型返回默认值,引用类型返回null - args[i] = type.IsValueType ? Activator.CreateInstance(type) : null; - } - else - { - args[i] = jsonValue.ToObject(type); - } - } - - - } - - object result = null; - try - { - result = await delegateDetails.InvokeAsync(instance, args); - } - catch (Exception ex) - { - result = null; - await Console.Out.WriteLineAsync(ex.Message); - - } - return result; - - } - - - } - - @@ -232,8 +62,6 @@ namespace Serein.Library.Web private readonly ConcurrentDictionary> HandleModels = new ConcurrentDictionary>(); - - public Router(ISereinIOC SereinIOC) { this.SereinIOC = SereinIOC; @@ -302,21 +130,19 @@ namespace Serein.Library.Web public async Task ProcessingAsync(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(); + var template = request.Url.AbsolutePath.ToLower(); if (!_controllerTypes.TryGetValue(template, out Type controllerType)) { - return false; + return false; // 没有对应的方法 } + var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法 if (!HandleModels.TryGetValue(httpMethod, out var modules)) { - return false; + return false; // 没有对应的处理模块 } if (!modules.TryGetValue(template, out var config)) { - return false; + return false; // 没有对应的处理配置 } ControllerBase controllerInstance = (ControllerBase)SereinIOC.Instantiate(controllerType); @@ -326,25 +152,30 @@ namespace Serein.Library.Web return false; // 未找到控制器实例 } - object invokeResult; + + var url = request.Url; // 获取请求的完整URL var routeValues = GetUrlData(url); // 解析 URL 获取路由参数 controllerInstance.Url = url.AbsolutePath; + + object[] args; switch (httpMethod) { case "GET": - invokeResult = await config.HandleGet(controllerInstance, routeValues); + args = config.GetArgsOfGet(routeValues); // Get请求 break; case "POST": var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容 controllerInstance.BobyData = requestBody; var requestJObject = JObject.Parse(requestBody); - invokeResult = await config.HandlePost(controllerInstance, requestJObject, routeValues); + args = config.GetArgsOfPost(routeValues, requestJObject); // Post请求 break; default: - invokeResult = null; - break; + return false; } - Return(response, invokeResult); // 返回结果 + var invokeResult = await config.HandleAsync(controllerInstance, args); + + var response = context.Response; // 获取响应对象 + ResponseApiMsg(response, invokeResult); // 返回结果 return true; } @@ -371,7 +202,7 @@ namespace Serein.Library.Web /// /// /// - private static void Return(HttpListenerResponse response, object msg) + private static void ResponseApiMsg(HttpListenerResponse response, object msg) { string resultData; if (response != null) diff --git a/Library/Network/Http/WebApiServer.cs b/Library/Network/Http/WebApiServer.cs index 12bfc28..cd09e0c 100644 --- a/Library/Network/Http/WebApiServer.cs +++ b/Library/Network/Http/WebApiServer.cs @@ -139,6 +139,8 @@ namespace Serein.Library.Web } } + + /// /// 判断访问接口的频次是否正常 /// diff --git a/Library/Network/WebSocket/Attribute.cs b/Library/Network/WebSocket/Attribute.cs index 995ea75..0096a8e 100644 --- a/Library/Network/WebSocket/Attribute.cs +++ b/Library/Network/WebSocket/Attribute.cs @@ -86,10 +86,71 @@ namespace Serein.Library.Network.WebSocketCommunication { } - internal class SocketHandleModule + + internal class WebSocketHandleConfiguration : HandleConfiguration { + /// + /// 主题 + /// public string ThemeValue { get; set; } = string.Empty; + } + + /// + /// socket模块处理数据配置 + /// + + public class HandleConfiguration + { + /// + /// Emit委托 + /// + public DelegateDetails DelegateDetails { get; set; } + + /// + /// 未捕获的异常跟踪 + /// + public Action> OnExceptionTracking { get; set; } + + /// + /// 所使用的实例 + /// + public ISocketHandleModule Instance { get; set; } + + /// + /// 是否需要返回 + /// public bool IsReturnValue { get; set; } = true; + + /// + /// 是否要求必须不为null + /// + public bool ArgNotNull { get; set; } = true; + + /// + /// 是否使Data整体内容作为入参参数 + /// + public bool[] UseData { get; set; } + + /// + /// 是否使用消息ID作为入参参数 + /// + public bool[] UseMsgId { get; set; } + + /// + /// 参数名称 + /// + public string[] ParameterName { get; set; } + + /// + /// 参数类型 + /// + public Type[] ParameterType { get; set; } + + /// + /// 是否检查变量为空 + /// + public bool[] IsCheckArgNotNull { get; set; } + } diff --git a/Library/Network/WebSocket/Handle/Attribute.cs b/Library/Network/WebSocket/Handle/Attribute.cs index 8184f84..421a71a 100644 --- a/Library/Network/WebSocket/Handle/Attribute.cs +++ b/Library/Network/WebSocket/Handle/Attribute.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -64,4 +65,6 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle } + + } diff --git a/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs b/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs deleted file mode 100644 index 7ffdd2e..0000000 --- a/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs +++ /dev/null @@ -1,250 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Serein.Library.Utils; -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.Library.Network.WebSocketCommunication.Handle -{ - /// - /// - /// - public class JsonMsgHandleConfig - { - public Guid HandleGuid { get; } - - - internal JsonMsgHandleConfig(SocketHandleModule model, - ISocketHandleModule instance, - MethodInfo methodInfo, - Action> onExceptionTracking, - bool ArgNotNull) - { - DelegateDetails = new DelegateDetails(methodInfo); - this.Module = model; - Instance = instance; - var parameterInfos = methodInfo.GetParameters(); - this.ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray(); - this.ParameterName = parameterInfos.Select(t => t.Name).ToArray(); - this.HandleGuid = instance.HandleGuid; - this.OnExceptionTracking = onExceptionTracking; - this.ArgNotNull = ArgNotNull; - - this.useData = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); - this.useMsgId = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); -#if NET5_0_OR_GREATER - this.IsCheckArgNotNull = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); -#endif - - - - if(IsCheckArgNotNull is null) - { - IsCheckArgNotNull = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); - } - else - { - // 兼容两种非空特性的写法 - var argNotNull = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); - for (int i = 0; i < IsCheckArgNotNull.Length; i++) - { - if (!IsCheckArgNotNull[i] && argNotNull[i]) - { - IsCheckArgNotNull[i] = true; - } - } - } - - } - - /// - /// 参数不能为空 - /// - private bool ArgNotNull; - /// - /// Emit委托 - /// - private readonly DelegateDetails DelegateDetails; - /// - /// 未捕获的异常跟踪 - /// - private readonly Action> OnExceptionTracking; - /// - /// 所在的模块 - /// - private readonly SocketHandleModule Module; - /// - /// 所使用的实例 - /// - private readonly ISocketHandleModule Instance; - /// - /// 参数名称 - /// - private readonly string[] ParameterName; - /// - /// 参数类型 - /// - private readonly Type[] ParameterType; - /// - /// 是否使Data整体内容作为入参参数 - /// - private readonly bool[] useData; - /// - /// 是否使用消息ID作为入参参数 - /// - private readonly bool[] useMsgId; - /// - /// 是否检查变量为空 - /// - private readonly bool[] IsCheckArgNotNull; - - public async void Handle(Func SendAsync,string msgId, JObject jsonObject) - { - object[] args = new object[ParameterType.Length]; - bool isCanInvoke = true;; // 表示是否可以调用方法 - for (int i = 0; i < ParameterType.Length; i++) - { - var type = ParameterType[i]; // 入参变量类型 - var argName = ParameterName[i]; // 入参参数名称 - #region 传递消息ID - if (useMsgId[i]) - { - args[i] = msgId; - } - #endregion - #region DATA JSON数据 - else if (useData[i]) - { - args[i] = jsonObject.ToObject(type); - } - #endregion - #region 值类型参数 - else if (type.IsValueType) - { - var jsonValue = jsonObject.GetValue(argName); - if (!(jsonValue is null)) - { - args[i] = jsonValue.ToObject(type); - } - else - { - if (ArgNotNull && !IsCheckArgNotNull[i]) // 检查不能为空 - { - - args[i] = Activator.CreateInstance(type); // 值类型返回默认值 - } - else - { - isCanInvoke = false; // 参数不能为空,终止调用 - break; - } - } - } - #endregion - #region 引用类型参数 - else if (type.IsClass) - { - var jsonValue = jsonObject.GetValue(argName); - if (!(jsonValue is null)) - { - args[i] = jsonValue.ToObject(type); - } - else - { - if (ArgNotNull && !IsCheckArgNotNull[i]) - { - - args[i] = null; // 引用类型返回null - } - else - { - isCanInvoke = false; // 参数不能为空,终止调用 - break; - } - } - } - #endregion - #region 传递消息委托 - else if (type.IsGenericType) // 传递SendAsync委托 - { - if (type.IsAssignableFrom(typeof(Func))) - { - args[i] = new Func(async data => - { - var jsonText = JsonConvert.SerializeObject(data); - await SendAsync.Invoke(jsonText); - }); - } - else if (type.IsAssignableFrom(typeof(Func))) - { - args[i] = new Func(async data => - { - await SendAsync.Invoke(data); - }); - } - else if (type.IsAssignableFrom(typeof(Action))) - { - args[i] = new Action(async data => - { - var jsonText = JsonConvert.SerializeObject(data); - await SendAsync.Invoke(jsonText); - }); - } - else if (type.IsAssignableFrom(typeof(Action))) - { - args[i] = new Action(async data => - { - var jsonText = JsonConvert.SerializeObject(data); - await SendAsync.Invoke(jsonText); - }); - } - } - #endregion - } - - if (!isCanInvoke) - { - return; - } - - object result; - try - { - result = await DelegateDetails.InvokeAsync(Instance, args); - } - catch (Exception ex) - { - result = null; - await Console.Out.WriteLineAsync(ex.Message); - this.OnExceptionTracking.Invoke(ex, (async exData => - { - await SendAsync.Invoke(exData); - })); - } - - if (Module.IsReturnValue) - { - _ = SendAsync.Invoke(result); - } - - } - - } - - - - - - -} diff --git a/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs b/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs index ea92151..e19a994 100644 --- a/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs +++ b/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; namespace Serein.Library.Network.WebSocketCommunication.Handle { + /// /// Json消息处理模块 /// @@ -19,43 +20,35 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// Json消息处理模块 /// - public WebSocketHandleModule(string themeJsonKey, string dataJsonKey, string msgIdJsonKey) + public WebSocketHandleModule(WebSocketHandleModuleConfig config) { - this.ThemeJsonKey = themeJsonKey; - this.DataJsonKey = dataJsonKey; - this.MsgIdJsonKey = msgIdJsonKey; + this.moduleConfig = config; } /// - /// 指示处理模块该使用 Json 中的哪个 Key 作为业务区别字段 + /// 模块的处理配置 /// - public string ThemeJsonKey { get; } + private readonly WebSocketHandleModuleConfig moduleConfig; /// - /// 指示处理模块该使用 Json 中的哪个 Key 作为业务数据字段 + /// 用来判断消息是否重复 /// - public string DataJsonKey { get; } - - /// - /// 指示处理模块该使用 Json 中的哪个 Key 作为业务消息ID字段 - /// - public string MsgIdJsonKey { get; } - + private HashSet _myMsgIdHash = new HashSet(); /// /// 存储处理数据的配置 /// - public ConcurrentDictionary MyHandleConfigs = new ConcurrentDictionary(); + public ConcurrentDictionary MyHandleConfigs = new ConcurrentDictionary(); + /// /// 添加处理配置 /// - /// 处理模块 - /// 处理配置 - internal bool AddHandleConfigs(SocketHandleModule module,JsonMsgHandleConfig jsonMsgHandleConfig) + /// 处理模块 + internal bool AddHandleConfigs(WebSocketHandleConfiguration config) { - if (!MyHandleConfigs.ContainsKey(module.ThemeValue)) + if (!MyHandleConfigs.ContainsKey(config.ThemeValue)) { - MyHandleConfigs[module.ThemeValue] = jsonMsgHandleConfig; + MyHandleConfigs[config.ThemeValue] = config; return true; } else @@ -74,7 +67,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle foreach (var kv in MyHandleConfigs.ToArray()) { var config = kv.Value; - if (config.HandleGuid.Equals(socketControlBase.HandleGuid)) + if (config.Instance.HandleGuid.Equals(socketControlBase.HandleGuid)) { MyHandleConfigs.TryRemove(kv.Key, out _); } @@ -91,38 +84,42 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle MyHandleConfigs.Clear(); } - private HashSet _myMsgIdHash = new HashSet(); + /// /// 处理JSON数据 /// - /// - /// - public void HandleSocketMsg(Func sendAsync, JObject jsonObject) + public async void HandleSocketMsg(WebSocketMsgContext context) // Func sendAsync, JObject jsonObject { - // 获取到消息 - string theme = jsonObject.GetValue(ThemeJsonKey)?.ToString(); + + var jsonObject = context.JsonObject; // 获取到消息 + string theme = jsonObject.GetValue(moduleConfig.ThemeJsonKey)?.ToString(); if (!MyHandleConfigs.TryGetValue(theme, out var handldConfig)) { - // 没有主题 - return; + return; // 没有主题 } - string msgId = jsonObject.GetValue(MsgIdJsonKey)?.ToString(); + context.MsgTheme = theme; // 添加主题 + string msgId = jsonObject.GetValue(moduleConfig.MsgIdJsonKey)?.ToString(); if (_myMsgIdHash.Contains(msgId)) { Console.WriteLine($"[{msgId}]{theme} 消息重复"); return; } + context.MsgId = msgId; // 添加 ID _myMsgIdHash.Add(msgId); try { - JObject dataObj = jsonObject.GetValue(DataJsonKey)?.ToObject(); - handldConfig.Handle(async (data) => + var dataObj = jsonObject.GetValue(moduleConfig.DataJsonKey)?.ToObject(); + context.MsgData = dataObj; // 添加消息 + if (TryGetParameters(handldConfig, context, out var args)) { - await this.SendAsync(sendAsync, msgId, theme, data); - }, msgId, dataObj); - + var result = await WebSocketHandleModule.HandleAsync(handldConfig, args); + if (handldConfig.IsReturnValue) + { + await context.RepliedAsync(moduleConfig, context, result); + } + } } catch (Exception ex) { @@ -133,52 +130,130 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// - /// 发送消息 + /// 调用 /// - /// - /// - /// - /// + /// + /// /// - public async Task SendAsync(Func sendAsync,string msgId, string theme, object data) + public static async Task HandleAsync(HandleConfiguration config, object[] args) { - JObject jsonData; + var result = await config.DelegateDetails.InvokeAsync(config.Instance, args); + return result; + } - if (data is null) + + /// + /// 获取入参参数 + /// + /// 处理配置 + /// 处理上下文 + /// 返回的入参参数 + /// + internal static bool TryGetParameters(HandleConfiguration config,WebSocketMsgContext context, out object[] args) + { + args = new object[config.ParameterType.Length]; + bool isCanInvoke = true; ; // 表示是否可以调用方法 + + for (int i = 0; i < config.ParameterType.Length; i++) { - jsonData = new JObject() + var type = config.ParameterType[i]; // 入参变量类型 + var argName = config.ParameterName[i]; // 入参参数名称 + #region 传递消息ID + if (config.UseMsgId[i]) { - [MsgIdJsonKey] = msgId, - [ThemeJsonKey] = theme, - }; - } - else - { - - JToken dataToken; - if ((data is System.Collections.IEnumerable || data is Array)) + args[i] = context.MsgId; + } + #endregion + #region DATA JSON数据 + else if (config.UseData[i]) + { + args[i] = context.MsgData.ToObject(type); + } + #endregion + #region 值类型参数 + else if (type.IsValueType) + { + var jsonValue = context.MsgData.GetValue(argName); + if (!(jsonValue is null)) { - dataToken = JArray.FromObject(data); + args[i] = jsonValue.ToObject(type); } else { - dataToken = JObject.FromObject(data); + if (config.ArgNotNull && !config.IsCheckArgNotNull[i]) // 检查不能为空 + { + + args[i] = Activator.CreateInstance(type); // 值类型返回默认值 + } + else + { + isCanInvoke = false; // 参数不能为空,终止调用 + break; + } } - - jsonData = new JObject() + } + #endregion + #region 引用类型参数 + else if (type.IsClass) + { + var jsonValue = context.MsgData.GetValue(argName); + if (!(jsonValue is null)) { - [MsgIdJsonKey] = msgId, - [ThemeJsonKey] = theme, - [DataJsonKey] = dataToken - }; - - - } + args[i] = jsonValue.ToObject(type); + } + else + { + if (config.ArgNotNull && !config.IsCheckArgNotNull[i]) + { - var msg = jsonData.ToString(); - - - await sendAsync.Invoke(msg); + args[i] = null; // 引用类型返回null + } + else + { + isCanInvoke = false; // 参数不能为空,终止调用 + break; + } + } + } + #endregion + #region 传递消息委托 + else if (type.IsGenericType) // 传递SendAsync委托 + { + if (type.IsAssignableFrom(typeof(Func))) + { + args[i] = new Func(async data => + { + var jsonText = JsonConvert.SerializeObject(data); + await context.SendAsync(jsonText); + }); + } + else if (type.IsAssignableFrom(typeof(Func))) + { + args[i] = new Func(async data => + { + await context.SendAsync(data); + }); + } + else if (type.IsAssignableFrom(typeof(Action))) + { + args[i] = new Action(async data => + { + var jsonText = JsonConvert.SerializeObject(data); + await context.SendAsync(jsonText); + }); + } + else if (type.IsAssignableFrom(typeof(Action))) + { + args[i] = new Action(async data => + { + var jsonText = JsonConvert.SerializeObject(data); + await context.SendAsync(jsonText); + }); + } + } + #endregion + } + return isCanInvoke; } diff --git a/Library/Network/WebSocket/Handle/WebSocketHandleModuleConfig.cs b/Library/Network/WebSocket/Handle/WebSocketHandleModuleConfig.cs new file mode 100644 index 0000000..c6b3d4b --- /dev/null +++ b/Library/Network/WebSocket/Handle/WebSocketHandleModuleConfig.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Network.WebSocketCommunication.Handle +{ + /// + /// 远程环境配置 + /// + public class WebSocketHandleModuleConfig + { + /// + /// 有关消息ID的 Json Key + /// + public string MsgIdJsonKey { get; set; } + /// + /// 有关消息主题的 Json Key + /// + public string ThemeJsonKey { get; set; } + /// + /// 有关数据的 Json Key + /// + public string DataJsonKey { get; set; } + } + +} diff --git a/Library/Network/WebSocket/Handle/WebSocketMsgContext.cs b/Library/Network/WebSocket/Handle/WebSocketMsgContext.cs new file mode 100644 index 0000000..3785f54 --- /dev/null +++ b/Library/Network/WebSocket/Handle/WebSocketMsgContext.cs @@ -0,0 +1,112 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Network.WebSocketCommunication.Handle +{ + + /// + /// 消息处理上下文 + /// + public class WebSocketMsgContext + { + public WebSocketMsgContext(Func sendAsync) + { + this._sendAsync = sendAsync; + } + + /// + /// 标记是否已经处理,如果是,则提前退出 + /// + public bool Handle { get; set; } + + /// + /// 消息本体(文本) + /// + public string Msg { get; set; } + + /// + /// 消息本体(JObject) + /// + public JObject JsonObject { get; set; } + + /// + /// 此次消息请求的主题 + /// + public string MsgTheme { get; set; } + + /// + /// 此次消息附带的ID + /// + public string MsgId { get; set; } + + /// + /// 此次消息的数据 + /// + public JObject MsgData { get; set; } + + + private Func _sendAsync; + + /// + /// 发送消息 + /// + /// + /// + public async Task SendAsync(string msg) + { + await _sendAsync.Invoke(msg); + } + + + /// + /// 返回消息 + /// + /// + /// + /// + /// + public async Task RepliedAsync(WebSocketHandleModuleConfig moduleConfig, + WebSocketMsgContext context, + object data) + { + JObject jsonData; + + if (data is null) + { + jsonData = new JObject() + { + [moduleConfig.MsgIdJsonKey] = context.MsgId, + [moduleConfig.ThemeJsonKey] = context.MsgTheme, + }; + } + else + { + JToken dataToken; + if (data is System.Collections.IEnumerable || data is Array) + { + dataToken = JArray.FromObject(data); + } + else + { + dataToken = JObject.FromObject(data); + } + + jsonData = new JObject() + { + [moduleConfig.MsgIdJsonKey] = context.MsgId, + [moduleConfig.ThemeJsonKey] = context.MsgTheme, + [moduleConfig.DataJsonKey] = dataToken + }; + } + var msg = jsonData.ToString(); + //Console.WriteLine($"[{msgId}] => {theme}"); + await SendAsync(msg); + } + + } + +} diff --git a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs index 24b2c63..fec41a0 100644 --- a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs +++ b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs @@ -16,6 +16,7 @@ using System.Linq.Expressions; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Cryptography; +using System.Diagnostics.CodeAnalysis; namespace Serein.Library.Network.WebSocketCommunication.Handle { @@ -39,16 +40,14 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// 添加消息处理与异常处理 /// - /// - /// - /// + /// 模块配置 /// - private WebSocketHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName, string msgIdKeyName) + private WebSocketHandleModule AddMyHandleModule(WebSocketHandleModuleConfig moduleConfig) { - var key = (themeKeyName, dataKeyName); + var key = (moduleConfig.ThemeJsonKey, moduleConfig.DataJsonKey); if (!MyHandleModuleDict.TryGetValue(key, out var myHandleModule)) { - myHandleModule = new WebSocketHandleModule(themeKeyName, dataKeyName, msgIdKeyName); + myHandleModule = new WebSocketHandleModule(moduleConfig); MyHandleModuleDict[key] = myHandleModule; } return myHandleModule; @@ -95,34 +94,75 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle var themeKey = moduleAttribute.ThemeKey; var dataKey = moduleAttribute.DataKey; var msgIdKey = moduleAttribute.MsgIdKey; - - var handleModule = AddMyHandleModule(themeKey, dataKey, msgIdKey); - var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Select(method => + var moduleConfig = new WebSocketHandleModuleConfig() + { + ThemeJsonKey = themeKey, + DataJsonKey = dataKey, + MsgIdJsonKey = msgIdKey, + }; + + var handleModule = AddMyHandleModule(moduleConfig); + var configs = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Select(methodInfo => { - var methodsAttribute = method.GetCustomAttribute(); + var methodsAttribute = methodInfo.GetCustomAttribute(); if (methodsAttribute is null) { - return (null, null,false); + return null; } else { if (string.IsNullOrEmpty(methodsAttribute.ThemeValue)) { - methodsAttribute.ThemeValue = method.Name; + methodsAttribute.ThemeValue = methodInfo.Name; } - var model = new SocketHandleModule + + #region 生成处理配置 + var config = new WebSocketHandleConfiguration { IsReturnValue = methodsAttribute.IsReturnValue, ThemeValue = methodsAttribute.ThemeValue, + ArgNotNull = methodsAttribute.ArgNotNull, }; + var parameterInfos = methodInfo.GetParameters(); + + config.DelegateDetails = new DelegateDetails(methodInfo); // 对应theme的emit构造委托调用工具类 + config.Instance = socketControlBase; // 调用emit委托时的实例 + config.OnExceptionTracking = onExceptionTracking; // 异常追踪 + config.ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray(); // 入参参数类型 + config.ParameterName = parameterInfos.Select(t => t.Name).ToArray(); // 入参参数名称 + config.UseData = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); // 是否使用整体data数据 + config.UseMsgId = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); // 是否使用消息ID +#if NET5_0_OR_GREATER + config.IsCheckArgNotNull = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); // 是否检查null +#endif + + if (config.IsCheckArgNotNull is null) + { + config.IsCheckArgNotNull = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); // 是否检查null + } + else + { + // 兼容两种非空特性的写法 + var argNotNull = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); // 是否检查null + for (int i = 0; i < config.IsCheckArgNotNull.Length; i++) + { + if (!config.IsCheckArgNotNull[i] && argNotNull[i]) + { + config.IsCheckArgNotNull[i] = true; + } + } + } + #endregion + + var value = methodsAttribute.ThemeValue; - var argNotNull = methodsAttribute.ArgNotNull; - return (model, method, argNotNull); + + return config; } }) - .Where(x => !(x.model is null)).ToList(); - if (methods.Count == 0) + .Where(config => config != null).ToList(); + if (configs.Count == 0) { return; } @@ -131,23 +171,10 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle Console.WriteLine($"add websocket handle model :"); Console.WriteLine($"theme key, data key : {themeKey}, {dataKey}"); - foreach ((var module, var method,var argNotNull) in methods) + foreach (var config in configs) { - Console.WriteLine($"theme value : {module.ThemeValue}"); - try - { - var jsonMsgHandleConfig = new JsonMsgHandleConfig(module, socketControlBase, method, onExceptionTracking, argNotNull); - var result = handleModule.AddHandleConfigs(module,jsonMsgHandleConfig); - if (!result) - { - throw new Exception("添加失败,已经添加过相同的配置"); - } - } - catch (Exception ex) - { - - Console.WriteLine($"error in add method: {method.Name}{Environment.NewLine}{ex}"); - } + Console.WriteLine($"theme value : {config.ThemeValue} "); + var result = handleModule.AddHandleConfigs(config); } } @@ -155,20 +182,21 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// 异步处理消息 /// - /// - /// + /// 此次请求的上下文 /// - public void HandleMsg(Func sendAsync, string message) + public void HandleMsg(WebSocketMsgContext context) { - JObject json = JObject.Parse(message); + // OnExceptionTracking foreach (var module in MyHandleModuleDict.Values) { - module.HandleSocketMsg(sendAsync, json); + module.HandleSocketMsg(context); } } + + } } diff --git a/Library/Network/WebSocket/WebSocketClient.cs b/Library/Network/WebSocket/WebSocketClient.cs index ba72748..439b1ef 100644 --- a/Library/Network/WebSocket/WebSocketClient.cs +++ b/Library/Network/WebSocket/WebSocketClient.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using static System.Net.Mime.MediaTypeNames; +using Newtonsoft.Json.Linq; namespace Serein.Library.Network.WebSocketCommunication { @@ -60,10 +61,10 @@ namespace Serein.Library.Network.WebSocketCommunication /// public async Task SendAsync(string message) { - Console.WriteLine("发送消息"); - await Task.Delay(2000); + //Console.WriteLine("发送消息"); + //await Task.Delay(2000); await SocketExtension.SendAsync(this._client, message); // 回复客户端 - Console.WriteLine(); + //Console.WriteLine(); //var buffer = Encoding.UTF8.GetBytes(message); //await _client.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None); } @@ -132,15 +133,17 @@ namespace Serein.Library.Network.WebSocketCommunication while (true) { var message = await msgQueueUtil.WaitMsgAsync(); // 有消息时通知 - //if (!msgQueueUtil.TryGetMsg(out var message)) // 获取消息 - //{ - // return; - //} - // 消息处理 - MsgHandleHelper.HandleMsg(async (text) => - { - await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 - }, message); // 处理消息 + + + _ = Task.Run(() => { + JObject json = JObject.Parse(message); + WebSocketMsgContext context = new WebSocketMsgContext(async (text) => + { + await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 + }); + context.JsonObject = json; + MsgHandleHelper.HandleMsg(context); // 处理消息 + }); } diff --git a/Library/Network/WebSocket/WebSocketServer.cs b/Library/Network/WebSocket/WebSocketServer.cs index 6c1da9c..71d6a30 100644 --- a/Library/Network/WebSocket/WebSocketServer.cs +++ b/Library/Network/WebSocket/WebSocketServer.cs @@ -152,7 +152,7 @@ namespace Serein.Library.Network.WebSocketCommunication } var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接 - _ = HandleWebSocketAsync(webSocketContext.WebSocket, authorizedHelper); // 处理消息 + _ = HandleWebSocketAsync(webSocketContext.WebSocket, authorizedHelper); // 处理消息 } } catch (Exception ex) @@ -231,17 +231,13 @@ namespace Serein.Library.Network.WebSocketCommunication public async Task HandleMsgAsync(WebSocket webSocket, - MsgQueueUtil msgQueueUtil, - WebSocketAuthorizedHelper authorizedHelper) + MsgQueueUtil msgQueueUtil, + WebSocketAuthorizedHelper authorizedHelper) { while (true) { var message = await msgQueueUtil.WaitMsgAsync(); // 有消息时通知 - //if (!msgQueueUtil.TryGetMsg(out var message)) // 获取消息 - //{ - // return; - //} if (IsCheckToken) { var authorizedResult = await authorizedHelper.HandleAuthorized(message); // 尝试检测授权 @@ -255,11 +251,17 @@ namespace Serein.Library.Network.WebSocketCommunication return; } } - // 消息处理 - MsgHandleHelper.HandleMsg(async (text) => - { - await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 - }, message); // 处理消息 + + _ = Task.Run(() => { + JObject json = JObject.Parse(message); + WebSocketMsgContext context = new WebSocketMsgContext(async (text) => + { + await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 + }); + context.JsonObject = json; + MsgHandleHelper.HandleMsg(context); // 处理消息 + }); + } diff --git a/Library/Utils/RemoteMsgUtil.cs b/Library/Utils/RemoteMsgUtil.cs index deb7b06..d6563f7 100644 --- a/Library/Utils/RemoteMsgUtil.cs +++ b/Library/Utils/RemoteMsgUtil.cs @@ -150,7 +150,7 @@ namespace Serein.Library.Utils }; } var msg = jsonData.ToString(); - Console.WriteLine($"[{msgId}] => {theme}"); + //Console.WriteLine($"[{msgId}] => {theme}"); await EnvClient.SendAsync(msg); } diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index ba39f55..920a628 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -182,9 +182,6 @@ namespace Serein.Workbench /// private void InitFlowEnvironmentEvent() { - - // 获取实现类的类型 - var implementationType = EnvDecorator.CurrentEnv.GetType().Name; EnvDecorator.OnDllLoad += FlowEnvironment_DllLoadEvent; EnvDecorator.OnProjectLoaded += FlowEnvironment_OnProjectLoaded; EnvDecorator.OnStartNodeChange += FlowEnvironment_StartNodeChangeEvent; @@ -2604,9 +2601,13 @@ namespace Serein.Workbench if (isConnect) { // 连接成功,加载远程项目 - var flowEnvInfo = await EnvDecorator.GetEnvInfoAsync(); - await Task.Delay(1000); - EnvDecorator.LoadProject(flowEnvInfo, string.Empty);// 加载远程环境的项目 + _ = Task.Run(async () => + { + var flowEnvInfo = await EnvDecorator.GetEnvInfoAsync(); + EnvDecorator.LoadProject(flowEnvInfo, string.Empty);// 加载远程环境的项目 + + }); + } }); windowEnvRemoteLoginView.Show();