mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-02 15:50:47 +08:00
修复了已知的bug
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<Title>SereinFow</Title>
|
||||
<Version>1.2.4</Version>
|
||||
<Version>1.2.4.1</Version>
|
||||
<Description>动态节点流、可视化编辑的基本依赖,支持导入C# DLL生成自定义节点,提供二次开发支持,适合用于可视化编程和流程设计</Description>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/fhhyyp/serein-flow</RepositoryUrl>
|
||||
|
||||
@@ -257,7 +257,16 @@ namespace Serein.Library.Utils
|
||||
object result;
|
||||
if (type.IsEnum)
|
||||
{
|
||||
result = Enum.Parse(type, valueStr);
|
||||
if (int.TryParse(valueStr, out int numericValue))
|
||||
{
|
||||
// 输入是数字,直接转换
|
||||
result = Enum.ToObject(type, numericValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 输入是枚举名称
|
||||
result = Enum.Parse(type, valueStr, ignoreCase: true);
|
||||
}
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
{
|
||||
|
||||
@@ -125,6 +125,7 @@ namespace Serein.Library
|
||||
public static void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.General)
|
||||
{
|
||||
Debug.WriteLine($"{type} : {message}");
|
||||
Console.WriteLine($"{type} : {message}");
|
||||
SereinEnv.environment?.WriteLine(type,message,@class);
|
||||
}
|
||||
|
||||
|
||||
@@ -194,16 +194,15 @@ namespace Serein.Library.Utils
|
||||
/// <param name="instance"></param>
|
||||
/// <returns></returns>
|
||||
public T InjectDependenciesProperty<T>(T instance)
|
||||
{
|
||||
{
|
||||
var type = instance.GetType();
|
||||
var properties = type.GetType()
|
||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToArray()
|
||||
.Where(p => p.CanWrite // 可写属性
|
||||
&& p.GetCustomAttribute<AutoInjectionAttribute>() != null // 有特性标注需要注入
|
||||
&& p.GetValue(instance) == null); // 属性为空
|
||||
var propertys = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Where(p => p.CanWrite // 可写属性
|
||||
&& p.GetCustomAttribute<AutoInjectionAttribute>() is not null // 有特性标注需要注入
|
||||
&& p.GetValue(instance) is null); // 属性为空
|
||||
|
||||
// 属性注入
|
||||
foreach (var property in properties)
|
||||
foreach (var property in propertys)
|
||||
{
|
||||
var propertyType = property.PropertyType;
|
||||
if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
|
||||
@@ -215,7 +214,7 @@ namespace Serein.Library.Utils
|
||||
|
||||
// 字段注入
|
||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic )
|
||||
.Where(f => f.GetCustomAttribute<AutoInjectionAttribute>() != null
|
||||
.Where(f => f.GetCustomAttribute<AutoInjectionAttribute>() != null
|
||||
&& f.GetValue(instance) == null);
|
||||
|
||||
foreach (var field in fields)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.2.4</Version>
|
||||
<Version>1.2.4.1</Version>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@@ -4,9 +4,25 @@ namespace Serein.Extend.NewtonsoftJson
|
||||
{
|
||||
public static class NewtonsoftJsonExtend
|
||||
{
|
||||
/// <summary>
|
||||
/// 对象转 Json 文本
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToJsonText(this object data)
|
||||
{
|
||||
return JsonHelper.Serialize(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Json 文本转对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="jsonText"></param>
|
||||
/// <returns></returns>
|
||||
public static T ToJsonObject<T>(this string jsonText) where T : class
|
||||
{
|
||||
return JsonHelper.Deserialize<T>(jsonText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,6 @@ using System.Diagnostics.CodeAnalysis;
|
||||
namespace Serein.Extend.NewtonsoftJson
|
||||
{
|
||||
|
||||
public enum ProviderType
|
||||
{
|
||||
Default = 0,
|
||||
Web = 1,
|
||||
}
|
||||
/// <summary>
|
||||
/// 基于Newtonsoft.Json的IJsonProvider实现
|
||||
/// </summary>
|
||||
@@ -20,39 +15,23 @@ namespace Serein.Extend.NewtonsoftJson
|
||||
private JsonSerializerSettings? settings;
|
||||
|
||||
/// <summary>
|
||||
/// 基于Newtonsoft.Json的JSON门户实现
|
||||
/// 基于Newtonsoft.Json的JSON门户实现,默认首字母小写、忽略null
|
||||
/// </summary>
|
||||
public NewtonsoftJsonProvider()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于Newtonsoft.Json的JSON门户实现
|
||||
/// </summary>
|
||||
/// <param name="jsonType"></param>
|
||||
public NewtonsoftJsonProvider(ProviderType jsonType)
|
||||
{
|
||||
settings = jsonType switch
|
||||
{
|
||||
ProviderType.Web => new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(), // 控制首字母小写
|
||||
NullValueHandling = NullValueHandling.Ignore // 可选:忽略 null
|
||||
},
|
||||
_ => new JsonSerializerSettings
|
||||
{
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public NewtonsoftJsonProvider(JsonSerializerSettings settings)
|
||||
{
|
||||
settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(), // 控制首字母小写
|
||||
NullValueHandling = NullValueHandling.Ignore // 可选:忽略 null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用自定义的序列化设置
|
||||
/// </summary>
|
||||
/// <param name="settings"></param>
|
||||
public NewtonsoftJsonProvider(JsonSerializerSettings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<BaseOutputPath>..\.\.Output</BaseOutputPath>
|
||||
|
||||
<Title>为 SereinFlow 提供的 JSON 扩展</Title>
|
||||
<Version>1.0.1</Version>
|
||||
<Version>1.0.2</Version>
|
||||
<Description>通过 NewtonsoftJson 实现JSON门户扩展,用于解决 Serein.Proto.* 项目下需要 JSON 序列化与反序列化的场景</Description>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>1.2.4</Version>
|
||||
<Version>1.2.4.1</Version>
|
||||
<IsRoslynComponent>false</IsRoslynComponent><!--控制代码生成器-->
|
||||
<!--<IsRoslynComponent>true</IsRoslynComponent>-->
|
||||
<BaseOutputPath>..\.\.Output</BaseOutputPath>
|
||||
|
||||
200
Serein.Proto.HttpApi/ApiHandleConfig.cs
Normal file
200
Serein.Proto.HttpApi/ApiHandleConfig.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Proto.HttpApi
|
||||
{
|
||||
/// <summary>
|
||||
/// api请求处理模块
|
||||
/// </summary>
|
||||
public class ApiHandleConfig
|
||||
{
|
||||
private readonly DelegateDetails delegateDetails;
|
||||
|
||||
/// <summary>
|
||||
/// Post请求处理方法中,入参参数类型
|
||||
/// </summary>
|
||||
public enum PostArgType
|
||||
{
|
||||
/// <summary>
|
||||
/// 不做处理
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// 使用Url参数
|
||||
/// </summary>
|
||||
IsUrlData,
|
||||
/// <summary>
|
||||
/// 使用整体的Boby参数
|
||||
/// </summary>
|
||||
IsBobyData,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加处理配置
|
||||
/// </summary>
|
||||
/// <param name="routerInfo"></param>
|
||||
public ApiHandleConfig(RouterInfo routerInfo)
|
||||
{
|
||||
|
||||
var methodInfo = routerInfo.MethodInfo;
|
||||
delegateDetails = new DelegateDetails(methodInfo);
|
||||
var parameterInfos = methodInfo.GetParameters();
|
||||
ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray();
|
||||
ParameterName = parameterInfos.Select(t => t.Name!).ToArray();
|
||||
|
||||
PostArgTypes = parameterInfos.Select(p =>
|
||||
{
|
||||
bool isUrlData = p.GetCustomAttribute(typeof(UrlAttribute)) != null;
|
||||
bool isBobyData = p.GetCustomAttribute(typeof(BodyAttribute)) != null;
|
||||
if (isBobyData)
|
||||
{
|
||||
return PostArgType.IsBobyData;
|
||||
}
|
||||
else if (isUrlData)
|
||||
{
|
||||
return PostArgType.IsUrlData;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PostArgType.None;
|
||||
}
|
||||
}).ToArray();
|
||||
RouterInfo = routerInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 路由信息
|
||||
/// </summary>
|
||||
public RouterInfo RouterInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Post请求处理方法中,入参参数类型
|
||||
/// </summary>
|
||||
public PostArgType[] PostArgTypes { get;private set; }
|
||||
/// <summary>
|
||||
/// 参数名称
|
||||
/// </summary>
|
||||
public string[] ParameterName { get; private set; }
|
||||
/// <summary>
|
||||
/// 参数类型
|
||||
/// </summary>
|
||||
public Type[] ParameterType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 处理Get请求
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object?[] GetArgsOfGet(Dictionary<string, string> 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] = JsonHelper.Deserialize(argValue, type); // JsonConvert.DeserializeObject(argValue, type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = type.IsValueType ? Activator.CreateInstance(type) : null;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 POST 获取参数
|
||||
/// </summary>
|
||||
/// <param name="routeData"></param>
|
||||
/// <param name="jsonObject"></param>
|
||||
/// <returns></returns>
|
||||
public (bool, int, Type, Exception?) TryGetArgsOfPost(Dictionary<string, string> routeData, IJsonToken jsonObject, out object?[] argData)
|
||||
{
|
||||
argData = null;
|
||||
object?[] args = new object[ParameterType.Length];
|
||||
int i = 0;
|
||||
|
||||
try
|
||||
{
|
||||
for (; 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] = JsonHelper.Deserialize(argValue, type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = type.IsValueType ? Activator.CreateInstance(type) : null;
|
||||
}
|
||||
}
|
||||
else if (jsonObject != null && PostArgTypes[i] == PostArgType.IsBobyData)
|
||||
{
|
||||
args[i] = jsonObject.ToObject(type);
|
||||
}
|
||||
else if (jsonObject != null)
|
||||
{
|
||||
if (jsonObject.TryGetValue(argName, out var jsonToken))
|
||||
{
|
||||
args[i] = jsonToken.ToObject(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = type.IsValueType ? Activator.CreateInstance(type) : null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
argData = args;
|
||||
return (true ,-1, null, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
argData = [];
|
||||
return (false,i, ParameterType[i], ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 处理请求
|
||||
/// </summary>
|
||||
/// <param name="instance"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<object?> HandleAsync(object instance, object?[] args)
|
||||
{
|
||||
var result = await delegateDetails.InvokeAsync(instance, args);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
80
Serein.Proto.HttpApi/Attribute.cs
Normal file
80
Serein.Proto.HttpApi/Attribute.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Proto.HttpApi
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示参数为url中的数据
|
||||
/// </summary>
|
||||
public class UrlAttribute : 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>
|
||||
public class BodyAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 标记该类为 Web Api 处理类
|
||||
/// </summary>
|
||||
public class WebApiControllerAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// URL 路径
|
||||
/// </summary>
|
||||
public string Url { get; }
|
||||
public WebApiControllerAttribute(string url = "")
|
||||
{
|
||||
Url = url;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 方法的接口类型与附加URL
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class WebApiAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP 请求类型
|
||||
/// </summary>
|
||||
public ApiType ApiType;
|
||||
/// <summary>
|
||||
/// URL 路径
|
||||
/// </summary>
|
||||
public string Url = string.Empty;
|
||||
|
||||
public WebApiAttribute(ApiType http = ApiType.POST, string url = "")
|
||||
{
|
||||
ApiType = http;
|
||||
Url = url;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ApiType
|
||||
{
|
||||
POST,
|
||||
GET,
|
||||
//PUT,
|
||||
//DELETE
|
||||
}
|
||||
}
|
||||
32
Serein.Proto.HttpApi/ControllerBase.cs
Normal file
32
Serein.Proto.HttpApi/ControllerBase.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace Serein.Proto.HttpApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Web Api 控制器基类
|
||||
/// </summary>
|
||||
public class ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 请求的url
|
||||
/// </summary>
|
||||
public string? Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求的body数据
|
||||
/// </summary>
|
||||
public string? Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取日志信息
|
||||
/// </summary>
|
||||
/// <param name="ex"></param>
|
||||
/// <returns></returns>
|
||||
public string GetLog(Exception ex)
|
||||
{
|
||||
return "Url : " + Url + Environment.NewLine +
|
||||
"Ex : " + ex + Environment.NewLine +
|
||||
"Data : " + Body + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
437
Serein.Proto.HttpApi/PathRouter.cs
Normal file
437
Serein.Proto.HttpApi/PathRouter.cs
Normal file
@@ -0,0 +1,437 @@
|
||||
using Serein.Library;
|
||||
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 static Serein.Proto.HttpApi.PathRouter;
|
||||
using Enum = System.Enum;
|
||||
using Type = System.Type;
|
||||
|
||||
namespace Serein.Proto.HttpApi
|
||||
{
|
||||
/// <summary>
|
||||
/// 路由接口
|
||||
/// </summary>
|
||||
public interface IPathRouter
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加处理模块
|
||||
/// </summary>
|
||||
/// <param name="controllerType"></param>
|
||||
void AddHandle(Type controllerType);
|
||||
/// <summary>
|
||||
/// 获取路由信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<RouterInfo> GetRouterInfos();
|
||||
|
||||
/// <summary>
|
||||
/// 路由解析开始处理
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
Task<InvokeResult> HandleAsync(HttpListenerContext context);
|
||||
}
|
||||
|
||||
|
||||
public enum HandleState
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认值
|
||||
/// </summary>
|
||||
None ,
|
||||
/// <summary>
|
||||
/// 没有异常
|
||||
/// </summary>
|
||||
Ok,
|
||||
/// <summary>
|
||||
/// 没有对应的控制器
|
||||
/// </summary>
|
||||
NotHanleController,
|
||||
/// <summary>
|
||||
/// 没有对应的Http请求类型
|
||||
/// </summary>
|
||||
NotHttpApiRequestType,
|
||||
/// <summary>
|
||||
/// 没有处理配置
|
||||
/// </summary>
|
||||
NotHandleConfig,
|
||||
/// <summary>
|
||||
/// 无法实例化控制器
|
||||
/// </summary>
|
||||
HanleControllerIsNull,
|
||||
|
||||
/// <summary>
|
||||
/// 调用参数获取错误
|
||||
/// </summary>
|
||||
InvokeArgError,
|
||||
|
||||
/// <summary>
|
||||
/// 调用发生异常
|
||||
/// </summary>
|
||||
InvokeErrored,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调用结果
|
||||
/// </summary>
|
||||
public class InvokeResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 调用状态
|
||||
/// </summary>
|
||||
public HandleState State { get; set; }
|
||||
/// <summary>
|
||||
/// 调用正常时这里会有数据
|
||||
/// </summary>
|
||||
public object? Data{ get; set; }
|
||||
/// <summary>
|
||||
/// 调用失败时这里可能会有异常数据
|
||||
/// </summary>
|
||||
public Exception? Exception { get; set; }
|
||||
|
||||
public static InvokeResult Ok(object? data) => new InvokeResult
|
||||
{
|
||||
Data = data,
|
||||
State = HandleState.Ok,
|
||||
};
|
||||
public static InvokeResult Fail(HandleState state, Exception? ex = null) => new InvokeResult
|
||||
{
|
||||
State = state,
|
||||
Exception = ex,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 路由信息
|
||||
/// </summary>
|
||||
public class RouterInfo
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// 接口类型
|
||||
/// </summary>
|
||||
public ApiType ApiType { get; set; }
|
||||
/// <summary>
|
||||
/// 接口URL
|
||||
/// </summary>
|
||||
public required string Url { get; set; }
|
||||
/// <summary>
|
||||
/// 对应的处理方法
|
||||
/// </summary>
|
||||
public required MethodInfo MethodInfo { get; set; }
|
||||
#else
|
||||
/// <summary>
|
||||
/// 接口类型
|
||||
/// </summary>
|
||||
public ApiType ApiType { get; set; }
|
||||
/// <summary>
|
||||
/// 接口URL
|
||||
/// </summary>
|
||||
public string Url { get; set; }
|
||||
/// <summary>
|
||||
/// 对应的处理方法
|
||||
/// </summary>
|
||||
public MethodInfo MethodInfo { get; set; }
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 路由注册与解析
|
||||
/// </summary>
|
||||
public class PathRouter : IPathRouter
|
||||
{
|
||||
private readonly ISereinIOC SereinIOC; // 用于存储路由信息
|
||||
|
||||
/// <summary>
|
||||
/// 控制器实例对象的类型,每次调用都会重新实例化,[Url - ControllerType]
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, Type> _controllerTypes = new ConcurrentDictionary<string, Type>();
|
||||
|
||||
/// <summary>
|
||||
/// 用于存储路由信息,[GET|POST - [Url - ApiHandleConfig]]
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, ApiHandleConfig>> HandleModels = new ConcurrentDictionary<string, ConcurrentDictionary<string, ApiHandleConfig>>();
|
||||
|
||||
public PathRouter(ISereinIOC SereinIOC)
|
||||
{
|
||||
this.SereinIOC = SereinIOC;
|
||||
foreach (ApiType method in Enum.GetValues(typeof(ApiType))) // 遍历 HTTP 枚举类型的所有值
|
||||
{
|
||||
HandleModels.TryAdd(method.ToString(), new ConcurrentDictionary<string, ApiHandleConfig>()); // 初始化每种 HTTP 方法对应的路由字典
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取路由信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<RouterInfo> GetRouterInfos()
|
||||
{
|
||||
var t = HandleModels.Select(kvp => kvp.Value.Select(kvp2 => kvp2.Value.RouterInfo));
|
||||
return t.SelectMany(x => x).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加处理模块
|
||||
/// </summary>
|
||||
/// <param name="controllerType"></param>
|
||||
public void AddHandle(Type controllerType)
|
||||
{
|
||||
if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回
|
||||
|
||||
var autoHostingAttribute = controllerType.GetCustomAttribute<WebApiControllerAttribute>();
|
||||
if(autoHostingAttribute is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"类型 \"{controllerType}\" 需要实现 \"WebApiControllerAttribute\" 才可以作为路由控制器");
|
||||
return;
|
||||
}
|
||||
var methods = controllerType.GetMethods().Where(m => m.GetCustomAttribute<WebApiAttribute>() != null).ToArray();
|
||||
|
||||
|
||||
foreach (var method in methods) // 遍历控制器类型的所有方法
|
||||
{
|
||||
var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
|
||||
if (routeAttribute is null) // 如果存在 WebAPIAttribute 属性
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var url = GetRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
|
||||
if (url is null) continue;
|
||||
|
||||
SereinEnv.WriteLine(InfoType.INFO, url);
|
||||
var apiType = routeAttribute.ApiType.ToString();
|
||||
|
||||
var routerInfo = new RouterInfo
|
||||
{
|
||||
ApiType = routeAttribute.ApiType,
|
||||
Url = url,
|
||||
MethodInfo = method,
|
||||
};
|
||||
|
||||
var config = new ApiHandleConfig(routerInfo);
|
||||
if(!HandleModels.TryGetValue(apiType, out var configs))
|
||||
{
|
||||
configs = new ConcurrentDictionary<string, ApiHandleConfig>();
|
||||
HandleModels[apiType] = configs;
|
||||
}
|
||||
configs.TryAdd(url, config);
|
||||
_controllerTypes.TryAdd(url,controllerType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在外部调用API后,解析路由,调用对应的方法
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<InvokeResult> HandleAsync(HttpListenerContext context)
|
||||
{
|
||||
var request = context.Request; // 获取请求对象
|
||||
var httpMethod = request.HttpMethod.ToUpper(); // 获取请求的 HTTP 方法
|
||||
if (!HandleModels.TryGetValue(httpMethod, out var modules) || request.Url is null)
|
||||
{
|
||||
return InvokeResult.Fail(HandleState.NotHttpApiRequestType); // 没有对应HTTP请求方法的处理
|
||||
}
|
||||
var template = request.Url.AbsolutePath.ToLower() ;
|
||||
if (!_controllerTypes.TryGetValue(template, out var controllerType))
|
||||
{
|
||||
return InvokeResult.Fail(HandleState.NotHanleController); // 没有对应的处理器
|
||||
}
|
||||
if (!modules.TryGetValue(template, out var config))
|
||||
{
|
||||
return InvokeResult.Fail(HandleState.NotHandleConfig); // 没有对应的处理配置
|
||||
}
|
||||
|
||||
ControllerBase controllerInstance;
|
||||
try
|
||||
{
|
||||
controllerInstance = (ControllerBase)SereinIOC.CreateObject(controllerType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return InvokeResult.Fail(HandleState.HanleControllerIsNull); // 未找到控制器实例
|
||||
}
|
||||
|
||||
|
||||
var url = request.Url; // 获取请求的完整URL
|
||||
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
|
||||
controllerInstance.Url = url.AbsolutePath;
|
||||
|
||||
object?[] args;
|
||||
switch (httpMethod)
|
||||
{
|
||||
case "GET":
|
||||
args = config.GetArgsOfGet(routeValues); // Get请求
|
||||
break;
|
||||
case "POST":
|
||||
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
|
||||
controllerInstance.Body = requestBody;
|
||||
if (!JsonHelper.TryParse(requestBody, out var requestJObject))
|
||||
{
|
||||
var exTips = $"body 无法转换为 json 数据, body: {requestBody}";
|
||||
return InvokeResult.Fail(HandleState.InvokeArgError, new Exception(exTips));
|
||||
}
|
||||
(var isPass, var index, var type, var ex) = config.TryGetArgsOfPost(routeValues, requestJObject, out args);
|
||||
if (!isPass)
|
||||
{
|
||||
var exTips = $"尝试转换第{index}个入参参数时,类型 {type.FullName} 参数获取失败:{ex?.Message}";
|
||||
return InvokeResult.Fail(HandleState.InvokeArgError, new Exception(exTips));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return InvokeResult.Fail(HandleState.NotHttpApiRequestType);
|
||||
}
|
||||
try
|
||||
{
|
||||
var invokeResult = await config.HandleAsync(controllerInstance, args);
|
||||
|
||||
return InvokeResult.Ok(invokeResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return InvokeResult.Fail(HandleState.InvokeErrored, ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 读取Body中的消息
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> ReadRequestBodyAsync(HttpListenerRequest request)
|
||||
{
|
||||
using (Stream stream = request.InputStream)
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
|
||||
{
|
||||
return await reader.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从方法中收集路由信息,返回方法对应的url
|
||||
/// </summary>
|
||||
/// <param name="autoHostingAttribute">类的特性</param>
|
||||
/// <param name="webAttribute">方法的特性</param>
|
||||
/// <param name="controllerType">控制器类型</param>
|
||||
/// <param name="method">方法信息</param>
|
||||
/// <returns>方法对应的urk</returns>
|
||||
private static string GetRoutesUrl(WebApiControllerAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method)
|
||||
{
|
||||
string controllerName;
|
||||
if (string.IsNullOrWhiteSpace(autoHostingAttribute.Url))
|
||||
{
|
||||
controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
|
||||
}
|
||||
else
|
||||
{
|
||||
controllerName = autoHostingAttribute.Url;
|
||||
}
|
||||
|
||||
var httpMethod = webAttribute.ApiType; // 获取 HTTP 方法
|
||||
var customUrl = webAttribute.Url; // 获取自定义 URL
|
||||
|
||||
string url;
|
||||
controllerName = CleanUrl(controllerName);
|
||||
if (string.IsNullOrWhiteSpace(customUrl))
|
||||
{
|
||||
url = $"/{controllerName}/{method.Name}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||
}
|
||||
else
|
||||
{
|
||||
if(customUrl == "/")
|
||||
{
|
||||
url = $"/{controllerName}".ToLower(); // 使用控制器
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
customUrl = CleanUrl(customUrl);
|
||||
url = $"/{controllerName}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||
}
|
||||
}
|
||||
return url;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修正方法特性中的URL格式
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
private static string CleanUrl(string url)
|
||||
{
|
||||
|
||||
while (url.Length > 0 && url[0] == '/') // 去除开头的斜杠
|
||||
{
|
||||
url = url.Substring(1);
|
||||
}
|
||||
|
||||
while (url.Length > 0 && url[url.Length - 1] == '/') // 去除末尾的斜杠
|
||||
{
|
||||
url = url.Substring(0, url.Length - 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>
|
||||
/// 方法声明,用于解析 URL 获取路由参数
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
/// <returns></returns>
|
||||
private Dictionary<string, string> GetUrlData(Uri uri)
|
||||
{
|
||||
Dictionary<string, string> routeValues = new Dictionary<string, string>();
|
||||
|
||||
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]; // 将键值对添加到路由参数字典中
|
||||
//}
|
||||
var parsedQuery = QueryStringParser.ParseQueryString(pathParts[1]);
|
||||
foreach (var kvp in parsedQuery)
|
||||
{
|
||||
//Console.WriteLine($"{kvp.Key}: {kvp.Value}");
|
||||
routeValues[kvp.Key.ToLower()] = kvp.Value; // 将键值对添加到路由参数字典中
|
||||
}
|
||||
}
|
||||
|
||||
return routeValues; // 返回路由参数字典
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
39
Serein.Proto.HttpApi/QueryStringParser.cs
Normal file
39
Serein.Proto.HttpApi/QueryStringParser.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Serein.Proto.HttpApi
|
||||
{
|
||||
internal class QueryStringParser
|
||||
{
|
||||
public static Dictionary<string, string> ParseQueryString(string query)
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
|
||||
if (string.IsNullOrEmpty(query))
|
||||
return result;
|
||||
|
||||
// 如果字符串以'?'开头,移除它
|
||||
if (query.StartsWith("?"))
|
||||
query = query.Substring(1);
|
||||
|
||||
// 拆分键值对
|
||||
var pairs = query.Split('&');
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
// 忽略空的键值对
|
||||
if (string.IsNullOrEmpty(pair)) continue;
|
||||
|
||||
// 用等号分隔键和值
|
||||
var keyValue = pair.Split(new[] { '=' }, 2);
|
||||
|
||||
var key = Uri.UnescapeDataString(keyValue[0]); // 解码键
|
||||
var value = keyValue.Length > 1 ? Uri.UnescapeDataString(keyValue[1]) : string.Empty; // 解码值
|
||||
|
||||
result[key] = value; // 添加到字典中
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Serein.Proto.HttpApi/RequestLimiter.cs
Normal file
59
Serein.Proto.HttpApi/RequestLimiter.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
|
||||
namespace Serein.Proto.HttpApi
|
||||
{
|
||||
/// <summary>
|
||||
/// 判断访问接口的频次是否正常
|
||||
/// </summary>
|
||||
public class RequestLimiter
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, Queue<DateTime>> requestHistory = new ConcurrentDictionary<string, Queue<DateTime>>();
|
||||
private readonly TimeSpan interval;
|
||||
private readonly int maxRequests;
|
||||
|
||||
public RequestLimiter(int seconds, int maxRequests)
|
||||
{
|
||||
interval = TimeSpan.FromSeconds(seconds);
|
||||
this.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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
23
Serein.Proto.HttpApi/Serein.Proto.HttpApi.csproj
Normal file
23
Serein.Proto.HttpApi/Serein.Proto.HttpApi.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net462</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>..\.\.Output</BaseOutputPath>
|
||||
|
||||
<Title>基于Json数据载体的HttpApi交互工具包</Title>
|
||||
<Version>0.0.9</Version>
|
||||
<Description>基于Json数据载体的HttpApi交互工具包</Description>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
121
Serein.Proto.HttpApi/SereinWebApiService.cs
Normal file
121
Serein.Proto.HttpApi/SereinWebApiService.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Utils;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace Serein.Proto.HttpApi
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 对于 HttpListenerContext 的拓展服务
|
||||
/// </summary>
|
||||
public class SereinWebApiService
|
||||
{
|
||||
private readonly IPathRouter _pathRouter;
|
||||
//private RequestLimiter? requestLimiter;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化处理器
|
||||
/// </summary>
|
||||
/// <param name="pathRouter"></param>
|
||||
public SereinWebApiService(IPathRouter pathRouter)
|
||||
{
|
||||
_pathRouter = pathRouter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加处理模块
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public void AddHandleModule<T>() where T : ControllerBase
|
||||
{
|
||||
_pathRouter.AddHandle(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加处理模块
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
public void AddHandleModule(Type type)
|
||||
{
|
||||
_pathRouter.AddHandle(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取路由信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<RouterInfo> GetRouterInfos()
|
||||
{
|
||||
return _pathRouter.GetRouterInfos();
|
||||
}
|
||||
|
||||
private Func<InvokeResult, (object, HttpStatusCode)> OnBeforeReplying;
|
||||
|
||||
/// <summary>
|
||||
/// 设置回复前的处理函数
|
||||
/// </summary>
|
||||
/// <param name="func"></param>
|
||||
public void SetOnBeforeReplying(Func<InvokeResult, (object, HttpStatusCode)> func)
|
||||
{
|
||||
OnBeforeReplying = func;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理请求
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public async Task HandleAsync(HttpListenerContext context)
|
||||
{
|
||||
// 获取远程终结点信息
|
||||
var remoteEndPoint = context.Request.RemoteEndPoint;
|
||||
// 获取用户的IP地址和端口
|
||||
IPAddress ipAddress = remoteEndPoint.Address;
|
||||
int port = remoteEndPoint.Port;
|
||||
SereinEnv.WriteLine(InfoType.INFO, "外部连接:" + ipAddress.ToString() + ":" + port);
|
||||
|
||||
// 添加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;
|
||||
}
|
||||
/*if (requestLimiter is not null)
|
||||
{
|
||||
if (!requestLimiter.AllowRequest(context.Request))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, "接口超时");
|
||||
context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
||||
}
|
||||
}*/
|
||||
|
||||
var invokeResult = await _pathRouter.HandleAsync(context); // 路由解析
|
||||
(var result, var code) = (OnBeforeReplying is not null) switch
|
||||
{
|
||||
true => OnBeforeReplying.Invoke(invokeResult),
|
||||
false => (invokeResult.Data, HttpStatusCode.OK),
|
||||
};
|
||||
var json = (result is not null) switch
|
||||
{
|
||||
true => JsonHelper.Serialize(result),
|
||||
false => string.Empty
|
||||
};
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(json);
|
||||
var response = context.Response; // 获取响应对象
|
||||
response.ContentLength64 = buffer.Length;
|
||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
context.Response.StatusCode = (int)code; // 返回 200 成功
|
||||
context.Response.Close(); // 关闭响应
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Serein.Proto.HttpApi/WebApiServer.cs
Normal file
17
Serein.Proto.HttpApi/WebApiServer.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
using Serein.Library;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Proto.HttpApi
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// HTTP接口监听类
|
||||
/// </summary>
|
||||
public partial class WebApiServer
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<!--<TargetFrameworks>net8.0;net462</TargetFrameworks>-->
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>..\.\.Output</BaseOutputPath>
|
||||
|
||||
<Title>全异步Modbus协议客户端工具包</Title>
|
||||
<Version>1.0.0</Version>
|
||||
<Version>1.0.1</Version>
|
||||
<Description>提供TCP、UDP、RTU三种方式客户端;创建方式:IModbusClient client = ModbusClientFactory.Create...;</Description>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<!--<PackageReference Include="System.Buffers" Version="4.6.1" />-->
|
||||
|
||||
<PackageReference Include="System.IO.Ports" Version="9.0.7" />
|
||||
<PackageReference Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Proto.WebSocket.Handle
|
||||
{
|
||||
|
||||
@@ -24,6 +24,13 @@ namespace Serein.Proto.WebSocket
|
||||
/// <returns></returns>
|
||||
ISereinWebSocketService AddHandleModule<T>() where T : ISocketHandleModule, new();
|
||||
|
||||
/// <summary>
|
||||
/// 添加处理模块
|
||||
/// </summary>
|
||||
/// <param name="socketHandleModule">接口实例</param>
|
||||
/// <returns></returns>
|
||||
ISereinWebSocketService AddHandleModule(ISocketHandleModule socketHandleModule);
|
||||
|
||||
/// <summary>
|
||||
/// 添加处理模块
|
||||
/// </summary>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0;net462</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>..\.\.Output</BaseOutputPath>
|
||||
|
||||
<Title>基于Json数据载体的WebSocket交互工具包</Title>
|
||||
<Version>1.0.1</Version>
|
||||
<Version>1.0.2</Version>
|
||||
<Description>基于Json数据载体的WebSocket交互工具包</Description>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,19 +2,10 @@
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Proto.WebSocket.Attributes;
|
||||
using Serein.Proto.WebSocket.Handle;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.WebSockets;
|
||||
using System.Reactive;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using NetWebSocket = System.Net.WebSockets.WebSocket;
|
||||
|
||||
namespace Serein.Proto.WebSocket
|
||||
@@ -73,6 +64,19 @@ namespace Serein.Proto.WebSocket
|
||||
var type = typeof(T);
|
||||
Func<ISocketHandleModule> instanceFactory = () => (T)Activator.CreateInstance(type);
|
||||
return AddHandleModule(type, instanceFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加处理模块,使用指定的实例工厂和异常追踪回调
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="instanceFactory"></param>
|
||||
/// <param name="onExceptionTracking"></param>
|
||||
public ISereinWebSocketService AddHandleModule(ISocketHandleModule socketHandleModule)
|
||||
{
|
||||
var type = socketHandleModule.GetType();
|
||||
Func<ISocketHandleModule> instanceFactory = () => socketHandleModule;
|
||||
return AddHandleModule(type, instanceFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -380,11 +384,11 @@ namespace Serein.Proto.WebSocket
|
||||
/// <returns></returns>
|
||||
public async Task PushDataAsync(object latestData)
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
var json = JsonSerializer.Serialize(latestData, options);
|
||||
//var options = new JsonSerializerOptions
|
||||
//{
|
||||
// PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
//};
|
||||
var json = JsonHelper.Serialize(latestData);
|
||||
var buffer = Encoding.UTF8.GetBytes(json);
|
||||
var segment = new ArraySegment<byte>(buffer);
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Proto.WebSocket
|
||||
{
|
||||
|
||||
public class ClassA : ISocketHandleModule
|
||||
{
|
||||
|
||||
}
|
||||
public class ClassB : ISocketHandleModule
|
||||
{
|
||||
|
||||
}
|
||||
public class ClassC : ISocketHandleModule
|
||||
{
|
||||
|
||||
}
|
||||
internal class TestClass
|
||||
{
|
||||
public void Run()
|
||||
{
|
||||
SereinWebSocketService sereinWebSocketService = new SereinWebSocketService();
|
||||
sereinWebSocketService.AddHandleModule<ClassA>();
|
||||
sereinWebSocketService.AddHandleModule<ClassB>(() => new ClassB());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Proto.WebSocket.Handle;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace Serein.Proto.WebSocket
|
||||
{
|
||||
@@ -110,7 +106,8 @@ namespace Serein.Proto.WebSocket
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="tag"></param>
|
||||
public bool TryGetTag<T>([NotNullWhen(true)] out T? tag)
|
||||
//public bool TryGetTag<T>([NotNullWhen(true)] out T? tag)
|
||||
public bool TryGetTag<T>(out T? tag)
|
||||
{
|
||||
lock (_wsTagLockObj)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<RepositoryUrl>https://github.com/fhhyyp/serein-flow</RepositoryUrl>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -28,6 +28,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Proto.WebSocket", "S
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Proto.Modbus", "Serein.Proto.Modbus\Serein.Proto.Modbus.csproj", "{EF9E51C0-CDDB-4B02-A304-87FFC31E61E0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Proto.HttpApi", "Serein.Proto.HttpApi\Serein.Proto.HttpApi.csproj", "{281B8E55-B9CD-4FE5-A72F-59CBB968D844}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -74,6 +76,10 @@ Global
|
||||
{EF9E51C0-CDDB-4B02-A304-87FFC31E61E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF9E51C0-CDDB-4B02-A304-87FFC31E61E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EF9E51C0-CDDB-4B02-A304-87FFC31E61E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{281B8E55-B9CD-4FE5-A72F-59CBB968D844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{281B8E55-B9CD-4FE5-A72F-59CBB968D844}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{281B8E55-B9CD-4FE5-A72F-59CBB968D844}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{281B8E55-B9CD-4FE5-A72F-59CBB968D844}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||
<ProjectReference Include="..\NodeFlow\Serein.NodeFlow.csproj" />
|
||||
<ProjectReference Include="..\Serein.Extend.NewtonsoftJson\Serein.Extend.NewtonsoftJson.csproj" />
|
||||
<ProjectReference Include="..\Serein.Proto.Modbus\Serein.Proto.Modbus.csproj" />
|
||||
<ProjectReference Include="..\Serein.Script\Serein.Script.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user