From cd1b0a7401bad9c2b5bdb7d47662db33a92b953b Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Fri, 20 Sep 2024 17:30:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E8=87=AA=E8=BF=B0?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library.Core/Http/Attribute.cs | 116 --- Library.Core/Http/ControllerBase.cs | 17 - Library.Core/Http/Router.cs | 762 ------------------- Library.Core/Http/WebAPIAttribute.cs | 187 ----- Library.Core/NodeFlow/DynamicNodeCoreType.cs | 38 - Library.Core/NodeFlow/FlowStateType.cs | 25 - Library.Core/NodeFlow/Tool/Attribute.cs | 35 - Library.Core/NodeFlow/Tool/DynamicTool.cs | 202 ----- README.md | 20 +- 9 files changed, 12 insertions(+), 1390 deletions(-) delete mode 100644 Library.Core/Http/Attribute.cs delete mode 100644 Library.Core/Http/ControllerBase.cs delete mode 100644 Library.Core/Http/Router.cs delete mode 100644 Library.Core/Http/WebAPIAttribute.cs delete mode 100644 Library.Core/NodeFlow/DynamicNodeCoreType.cs delete mode 100644 Library.Core/NodeFlow/FlowStateType.cs delete mode 100644 Library.Core/NodeFlow/Tool/Attribute.cs delete mode 100644 Library.Core/NodeFlow/Tool/DynamicTool.cs diff --git a/Library.Core/Http/Attribute.cs b/Library.Core/Http/Attribute.cs deleted file mode 100644 index 670a840..0000000 --- a/Library.Core/Http/Attribute.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; - -namespace Serein.Library.Core.Http -{ - /// - /// 表示参数为url中的数据(Get请求中不需要显式标注) - /// - [AttributeUsage(AttributeTargets.Parameter)] - public sealed class IsUrlDataAttribute : Attribute - { - - } - - /// - /// 表示入参参数为整个boby的数据 - /// - /// 例如:User类型含有int id、string name字段 - /// - /// ① Add(User user) - /// 请求需要传入的json为 - /// {"user":{ - /// "id":2, - /// "name":"李志忠"}} - /// - /// ② Add([Boby]User user) - /// 请求需要传入的json为 - /// {"id":2,"name":"李志忠"} - /// - /// - [AttributeUsage(AttributeTargets.Parameter)] - public sealed class IsBobyDataAttribute : Attribute - { - - } - - /// - /// 表示该控制器会被自动注册(与程序集同一命名空间,暂时不支持运行时自动加载DLL,需要手动注册) - /// - [AttributeUsage(AttributeTargets.Class)] - public sealed class AutoHostingAttribute(string url = "") : Attribute - { - public string Url { get; } = url; - } - /// - /// 表示该属性为自动注入依赖项 - /// - [AttributeUsage(AttributeTargets.Property)] - public sealed class AutoInjectionAttribute : Attribute - { - } - - - /// - /// 方法的接口类型与附加URL - /// - /// - /// 假设UserController.Add()的WebAPI特性中 - /// http是HTTP.POST - /// url被显示标明“temp” - /// 那么请求的接口是POST,URL是 - /// [http://localhost:8080]/user/add/temp - /// - /// - /// - [AttributeUsage(AttributeTargets.Method)] - - public sealed class WebApiAttribute() : Attribute - - { - public API Type ; - public string Url ; - /// - /// 方法名称不作为url的部分 - /// - public bool IsUrl; - } - [AttributeUsage(AttributeTargets.Method)] - - public sealed class ApiPostAttribute() : Attribute - - { - public string Url; - /// - /// 方法名称不作为url的部分 - /// - public bool IsUrl = true; - } - [AttributeUsage(AttributeTargets.Method)] - - public sealed class ApiGetAttribute() : Attribute - - { - public string Url; - /// - /// 方法名称不作为url的部分 - /// - 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; - /// - /// 方法名称不作为url的部分 - /// - public bool IsUrl { get; } = isUrl; - }*/ - public enum API - { - POST, - GET, - //PUT, - //DELETE - } -} diff --git a/Library.Core/Http/ControllerBase.cs b/Library.Core/Http/ControllerBase.cs deleted file mode 100644 index 714c1a0..0000000 --- a/Library.Core/Http/ControllerBase.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Serein.Library.Core.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; - } - } -} diff --git a/Library.Core/Http/Router.cs b/Library.Core/Http/Router.cs deleted file mode 100644 index 1272568..0000000 --- a/Library.Core/Http/Router.cs +++ /dev/null @@ -1,762 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Serein.Library.Api.Api; -using Serein.Library.Core.IOC; -using Serein.Tool; -using System.Collections; -using System.Collections.Concurrent; -using System.Net; -using System.Reflection; -using System.Text; -using System.Web; -using Enum = System.Enum; -using Type = System.Type; - -namespace Serein.Library.Core.Http -{ - /* - Router类负责解析请求的url,url参数,boby参数 - 根据url - - web server 监听类,监听外部的请求 - router 选择对应的控制器 - agent 负责传入对应的参数,注入依赖 - - - - */ - - - /// - /// 路由注册与解析 - /// - public class Router - { - private readonly ConcurrentDictionary _controllerAutoHosting; // 存储是否实例化 - private readonly ConcurrentDictionary _controllerTypes; // 存储控制器类型 - private readonly ConcurrentDictionary _controllerInstances; // 存储控制器实例对象 - private readonly ConcurrentDictionary> _routes; // 用于存储路由信息 - - private readonly ISereinIoc serviceRegistry; // 用于存储路由信息 - - //private Type PostRequest; - - public Router(ISereinIoc serviceRegistry) // 构造函数,初始化 Router 类的新实例 - { - this.serviceRegistry = serviceRegistry; - - _routes = new ConcurrentDictionary>(); // 初始化路由字典 - - _controllerAutoHosting = new ConcurrentDictionary(); // 初始化控制器实例对象字典 - _controllerTypes = new ConcurrentDictionary(); // 初始化控制器实例对象字典 - _controllerInstances = new ConcurrentDictionary(); // 初始化控制器实例对象字典 - - foreach (API method in Enum.GetValues(typeof(API))) // 遍历 HTTP 枚举类型的所有值 - { - _routes.TryAdd(method.ToString(), new ConcurrentDictionary()); // 初始化每种 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; - } - } - } - - - /// - /// 自动注册 自动实例化控制器 类型 - /// - /// - public void AutoRegisterAutoController(Type controllerType) // 方法声明,用于注册并实例化控制器类型 - { - if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回 - - var autoHostingAttribute = controllerType.GetCustomAttribute(); - if (autoHostingAttribute != null) { - foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 - { - var apiGetAttribute = method.GetCustomAttribute(); - var apiPostAttribute = method.GetCustomAttribute(); - 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 自定义属性 - 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; - }*/ - } - } - } - /// - /// 手动注册 自动实例化控制器实例 - /// - public void RegisterAutoController() // 方法声明,用于动态注册路由 - { - Type controllerType = typeof(T); // 获取控制器实例的类型 - foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 - { - var apiGetAttribute = method.GetCustomAttribute(); - var apiPostAttribute = method.GetCustomAttribute(); - 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; - - } - } - - - /// - /// 手动注册 实例持久控制器实例 - /// - /// - public void RegisterController(TController controllerInstance) where TController : ControllerBase // 方法声明,用于动态注册路由 - { - if(controllerInstance == null) return; - Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型 - foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 - { - var apiGetAttribute = method.GetCustomAttribute(); - var apiPostAttribute = method.GetCustomAttribute(); - 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; - } - } - - /// - /// 从方法中收集路由信息 - /// - /// - 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; - - } - - - /// - /// 收集路由信息 - /// - /// - public void CollectRoutes(Type controllerType) - { - string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写 - foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 - { - var routeAttribute = method.GetCustomAttribute(); // 获取方法上的 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 和方法添加到对应的路由字典中 - } - } - } - - - /// - /// 解析路由,调用对应的方法 - /// - /// - /// - public async Task 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(); - - 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; - } - - /// - /// GET请求的控制器方法 - /// - private object InvokeControllerMethodWithRouteValues(MethodInfo method, object controllerInstance, Dictionary routeValues) - { - object[] parameters = GetMethodParameters(method, routeValues); - return InvokeMethod(method, controllerInstance, parameters); - } - - private static readonly Dictionary methodParameterCache = []; - /// - /// POST请求的调用控制器方法 - /// - public object InvokeControllerMethod(MethodInfo method, object controllerInstance, dynamic requestData, Dictionary 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); - - } - - - /// - /// 检查方法入参参数类型,返回对应的入参数组 - /// - /// - /// - /// - private object[] GetMethodParameters(MethodInfo method, Dictionary 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; - } - - /*/// - /// 转为对应的类型 - /// - /// - /// - /// - 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; - } - }*/ - /// - /// 转为对应的类型 - /// - /// - /// - /// - 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; - } - } - - /// - /// 调用控制器方法传入参数 - /// - /// 方法 - /// 控制器实例 - /// 参数列表 - /// - 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; // 调用方法并返回结果 - - } - - - /// - /// 方法声明,用于解析 URL 获取路由参数 - /// - /// - /// - private static Dictionary GetUrlData(Uri uri) - { - Dictionary 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; // 返回路由参数字典 - } - - /// - /// 读取Body中的消息 - /// - /// - /// - private static async Task ReadRequestBodyAsync(HttpListenerRequest request) - { - using Stream stream = request.InputStream; - using StreamReader reader = new(stream, Encoding.UTF8); - return await reader.ReadToEndAsync(); - } - /// - /// 返回响应消息 - /// - /// - /// - 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(); - } - - - } - } - - /// - /// 解析JSON - /// - /// - /// - /// - 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"); - } - } - - /// - /// 修正方法特性中的URL格式 - /// - /// - /// - 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 - } - /// - /// 从控制器调用方法的异常中获取出出错类型的信息 - /// - /// - /// - 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; - - } - } -} - diff --git a/Library.Core/Http/WebAPIAttribute.cs b/Library.Core/Http/WebAPIAttribute.cs deleted file mode 100644 index 7973e4c..0000000 --- a/Library.Core/Http/WebAPIAttribute.cs +++ /dev/null @@ -1,187 +0,0 @@ -using Serein.Library.Api.Api; -using Serein.Library.Core.IOC; -using System.Collections.Concurrent; -using System.Net; - -namespace Serein.Library.Core.Http -{ - - /// - /// HTTP接口监听类 - /// - 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; - } - } - - - /// - /// 处理请求 - /// - /// - /// - 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() - { - //var instance = Activator.CreateInstance(typeof(T)); - router.RegisterAutoController(); - } - - /*public void RegisterRoute(T controllerInstance) - { - router.RegisterRoute(controllerInstance); - }*/ - } - /// - /// 判断访问接口的频次是否正常 - /// - public class RequestLimiter(int seconds, int maxRequests) - { - private readonly ConcurrentDictionary> requestHistory = new (); - private readonly TimeSpan interval = TimeSpan.FromSeconds(seconds); - private readonly int maxRequests = maxRequests; - - /// - /// 判断访问接口的频次是否正常 - /// - /// - 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()); - - lock (requests) - { - // 移除超出时间间隔的请求记录 - while (requests.Count > 0 && now - requests.Peek() > interval) - { - requests.Dequeue(); - } - - // 如果请求数超过限制,拒绝请求 - if (requests.Count >= maxRequests) - { - return false; - } - - // 添加当前请求时间,并允许请求 - requests.Enqueue(now); - } - - return true; - } - } - -} diff --git a/Library.Core/NodeFlow/DynamicNodeCoreType.cs b/Library.Core/NodeFlow/DynamicNodeCoreType.cs deleted file mode 100644 index e97d99f..0000000 --- a/Library.Core/NodeFlow/DynamicNodeCoreType.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.LibraryCore.NodeFlow -{ - public enum DynamicNodeCoreType - { - /// - /// 初始化 - /// - Init, - /// - /// 开始载入 - /// - Loading, - /// - /// 结束 - /// - Exit, - - /// - /// 触发器 - /// - Flipflop, - /// - /// 条件节点 - /// - Condition, - /// - /// 动作节点 - /// - Action, - } - -} diff --git a/Library.Core/NodeFlow/FlowStateType.cs b/Library.Core/NodeFlow/FlowStateType.cs deleted file mode 100644 index aae089c..0000000 --- a/Library.Core/NodeFlow/FlowStateType.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.Library.Core.NodeFlow -{ - - //public enum FlowStateType - //{ - // /// - // /// 成功(方法成功执行) - // /// - // Succeed, - // /// - // /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误) - // /// - // Fail, - // /// - // /// 异常(节点没有成功执行,执行时发生非预期的错误) - // /// - // Error, - //} -} diff --git a/Library.Core/NodeFlow/Tool/Attribute.cs b/Library.Core/NodeFlow/Tool/Attribute.cs deleted file mode 100644 index 338d11b..0000000 --- a/Library.Core/NodeFlow/Tool/Attribute.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Serein.Library.Api.Enums; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.Library.Core.NodeFlow.Tool -{ - /// - /// 用来判断一个类是否需要注册并构建实例(单例模式场景使用) - /// - [AttributeUsage(AttributeTargets.Class)] - public class DynamicFlowAttribute(bool scan = true) : Attribute - { - public bool Scan { get; set; } = scan; - } - - /// - /// 标记一个方法是什么类型,加载dll后用来拖拽到画布中 - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - public class MethodDetailAttribute(DynamicNodeType methodDynamicType, - string methodTips = "", - bool scan = true, - string lockName = "") : Attribute - { - public bool Scan { get; set; } = scan; - public string MethodTips { get; } = methodTips; - public DynamicNodeType MethodDynamicType { get; } = methodDynamicType; - public string LockName { get; } = lockName; - } - - -} diff --git a/Library.Core/NodeFlow/Tool/DynamicTool.cs b/Library.Core/NodeFlow/Tool/DynamicTool.cs deleted file mode 100644 index 058eb11..0000000 --- a/Library.Core/NodeFlow/Tool/DynamicTool.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Drawing.Printing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.Library.Core.NodeFlow.Tool -{ - - #region 锁、tsk工具 (已注释) - /*public class LockManager - { - private readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); - - public void CreateLock(string name) - { - _locks.TryAdd(name, new LockQueue()); - } - - public async Task AcquireLockAsync(string name, CancellationToken cancellationToken = default) - { - if (!_locks.ContainsKey(name)) - { - throw new ArgumentException($"Lock with name '{name}' does not exist."); - } - - var lockQueue = _locks[name]; - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - lock (lockQueue.Queue) - { - lockQueue.Queue.Enqueue(tcs); - if (lockQueue.Queue.Count == 1) - { - tcs.SetResult(true); - } - } - - await tcs.Task.ConfigureAwait(false); - - // 处理取消操作 - if (cancellationToken.CanBeCanceled) - { - cancellationToken.Register(() => - { - lock (lockQueue.Queue) - { - if (lockQueue.Queue.Contains(tcs)) - { - tcs.TrySetCanceled(); - } - } - }); - } - } - - public void ReleaseLock(string name) - { - if (!_locks.ContainsKey(name)) - { - throw new ArgumentException($"Lock with name '{name}' does not exist."); - } - - var lockQueue = _locks[name]; - - lock (lockQueue.Queue) - { - if (lockQueue.Queue.Count > 0) - { - lockQueue.Queue.Dequeue(); - - if (lockQueue.Queue.Count > 0) - { - var next = lockQueue.Queue.Peek(); - next.SetResult(true); - } - } - } - } - - private class LockQueue - { - public Queue> Queue { get; } = new Queue>(); - } - } - - - public interface ITaskResult - { - object Result { get; } - } - - public class TaskResult : ITaskResult - { - public TaskResult(T result) - { - Result = result; - } - - public T Result { get; } - - object ITaskResult.Result => Result; - } - - public class DynamicTasks - { - private static readonly ConcurrentDictionary> TaskGuidPairs = new(); - public static Task GetTask(string Guid) - { - TaskGuidPairs.TryGetValue(Guid, out Task task); - return task; - } - - public static bool AddTask(string Guid, T result) - { - var task = Task.FromResult(new TaskResult(result)); - - return TaskGuidPairs.TryAdd(Guid, task); - } - } - public class TaskNodeManager - { - private readonly ConcurrentDictionary _taskQueues = new ConcurrentDictionary(); - - public void CreateTaskNode(string name) - { - _taskQueues.TryAdd(name, new TaskQueue()); - } - - public async Task WaitForTaskNodeAsync(string name, CancellationToken cancellationToken = default) - { - if (!_taskQueues.ContainsKey(name)) - { - throw new ArgumentException($"Task node with name '{name}' does not exist."); - } - - var taskQueue = _taskQueues[name]; - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - lock (taskQueue.Queue) - { - taskQueue.Queue.Enqueue(tcs); - if (taskQueue.Queue.Count == 1) - { - tcs.SetResult(true); - } - } - - await tcs.Task.ConfigureAwait(false); - - // 处理取消操作 - if (cancellationToken.CanBeCanceled) - { - cancellationToken.Register(() => - { - lock (taskQueue.Queue) - { - if (taskQueue.Queue.Contains(tcs)) - { - tcs.TrySetCanceled(); - } - } - }); - } - } - - public void CompleteTaskNode(string name) - { - if (!_taskQueues.ContainsKey(name)) - { - throw new ArgumentException($"Task node with name '{name}' does not exist."); - } - - var taskQueue = _taskQueues[name]; - - lock (taskQueue.Queue) - { - if (taskQueue.Queue.Count > 0) - { - taskQueue.Queue.Dequeue(); - - if (taskQueue.Queue.Count > 0) - { - var next = taskQueue.Queue.Peek(); - next.SetResult(true); - } - } - } - } - - private class TaskQueue - { - public Queue> Queue { get; } = new Queue>(); - } - }*/ - #endregion - - - -} diff --git a/README.md b/README.md index 0084495..4db7c47 100644 --- a/README.md +++ b/README.md @@ -22,19 +22,21 @@ https://space.bilibili.com/33526379 ## 1. 不生成节点控件的枚举值: * **Init - 初始化方法** - * 入参:**DynamicContext**(有且只有一个参数)。 + * 入参:**IDynamicContext**(有且只有一个参数)。 * 返回值:自定义,但软件目前不支持接收返回值。 * 描述:在运行时首先被调用。语义类似于构造方法。建议在Init方法内初始化类、注册类等一切需要在构造函数中执行的方法。 * **Loading - 加载方法** - * 入参:**DynamicContext**(有且只有一个参数)。 + * 入参:**IDynamicContext**(有且只有一个参数)。 * 返回值:自定义,但软件目前不支持接收返回值。 * 描述:当所有Dll的Init方法调用完成后,首先调用、也才会调用DLL的Loading方法。建议在Loading方法内进行业务上的初始化(例如启动Web,启动第三方服务)。 * **Exit - 结束方法** - * 入参:**DynamicContext**(有且只有一个参数)。 + * 入参:**IDynamicContext**(有且只有一个参数)。 * 返回值:自定义,但软件目前不支持接收返回值。 * 描述:当结束/手动结束运行时,会调用所有Dll的Exit方法。使用场景类似于:终止内部的其它线程,通知其它进程关闭,例如停止第三方服务。 + * **关于IDynamicContext说明** + * 基本说明:IDynamicContext是动态上下文接口,内部提供用以注册、获取实例的IOC容器SereinIoc接口,以及运行环境IFlowEnvironment接口,一般情况下,你无须关注IFlowEnvironment对外暴露的属性方法。除此之外,还有一个用以创建定时循环任务的方法CreateTimingTask,通过该方法可以实现类似于定时器的功能,它的运行周期收到运行环境管理。 ## 2. 基础节点 * 待更新 @@ -48,15 +50,17 @@ https://space.bilibili.com/33526379 * **Flipflop - 触发器** * 全局触发器 * 入参:依照Action节点。 - * 返回值:Task + * 返回值:Task * 描述:运行开始时,所有无上级节点的触发器节点(在当前分支中作为起始节点),分别建立新的线程运行,然后异步等待触发(如果有)。这种触发器拥有独自的DynamicContext上下文(共用同一个Ioc),执行完成之后,会重新从分支起点的触发器开始等待。 * 分支中的触发器 * 入参:依照Action节点。 - * 返回值:Task + * 返回值:Task * 描述:接收上一节点传递的上下文,同样进入异步等待,但执行完成后不会再次等待自身(只会触发一次)。 - * FlipflopContext - * 描述:内部有一套枚举描述,Succeed、Cancel,如果返回Succeed,会通过所有下级节点集合创建Task集合,然后调用WaitAll()进行等待(每个Task会新建新的DynamicContext上下文(共用同一个Ioc))。如果返回Cancel,则什么也不做。 - * 使用场景:配合TcsSignal使用,定时从PLC中获取状态,当某个变量发生改变时,会通知持有TaskCompletionSource的触发器,如果需要,可以传递对应的数据。 + * IFlipflopContext + * 基本说明:IFlipflopContext是一个接口,你无须关心内部实现。 + * 参数描述:State,状态枚举描述(Succeed、Cancel、Error、Cancel),如果返回Cancel,则不会执行后继分支,如果返回其它状态,则会获取对应的后继分支,开始执行。 + * 参数描述:TriggerData,内部分别有状态描述、触发的参数。触发状态有两种(External外部触发,Overtime超时触发),当你在代码中的其他地方主动触发了触发器,则该次触发类型为External,当你在创建触发器后超过了指定时间(创建触发器时会要求声明超时时间),则会自动触发,但触发类型为Overtime,触发参数未你在创建触发器时指定的值) + * 使用场景:配合ChannelFlowTrigger使用,定时从PLC中获取状态,当某个变量发生改变时,会通知持有 Channel的触发器,如果需要,可以传递对应的数据。 演示: ![image](https://github.com/fhhyyp/serein-flow/blob/cc5f8255135b96c6bb3669bc4aa8d8167a71c262/Image/%E6%BC%94%E7%A4%BA%20-%201.png) ![image](https://github.com/fhhyyp/serein-flow/blob/cc5f8255135b96c6bb3669bc4aa8d8167a71c262/Image/%E6%BC%94%E7%A4%BA%20-%202.png)