mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
重写了Web Api的逻辑,用Emit构造委托加速API处理
This commit is contained in:
@@ -51,7 +51,7 @@ namespace Serein.Library.Web
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class WebApiAttribute : Attribute
|
||||
{
|
||||
public API Http; // HTTP 请求类型
|
||||
public ApiType ApiType; // HTTP 请求类型
|
||||
public string Url; // URL 路径
|
||||
/// <summary>
|
||||
/// 方法名称不作为url的部分
|
||||
@@ -68,15 +68,15 @@ namespace Serein.Library.Web
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="url"></param>
|
||||
public WebApiAttribute(API http = API.POST, bool isUrl = true, string url = "")
|
||||
public WebApiAttribute(ApiType http = ApiType.POST, bool isUrl = true, string url = "")
|
||||
{
|
||||
Http = http;
|
||||
ApiType = http;
|
||||
Url = url;
|
||||
IsUrl = isUrl;
|
||||
}
|
||||
}
|
||||
|
||||
public enum API
|
||||
public enum ApiType
|
||||
{
|
||||
POST,
|
||||
GET,
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
@@ -20,10 +24,199 @@ namespace Serein.Library.Web
|
||||
{
|
||||
public interface IRouter
|
||||
{
|
||||
bool RegisterController(Type controllerType);
|
||||
void AddHandle(Type controllerType);
|
||||
Task<bool> ProcessingAsync(HttpListenerContext context);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class ApiHandleConfig
|
||||
{
|
||||
private readonly Delegate EmitDelegate;
|
||||
private readonly EmitHelper.EmitMethodType EmitMethodType;
|
||||
|
||||
public enum PostArgType
|
||||
{
|
||||
None,
|
||||
IsUrlData,
|
||||
IsBobyData,
|
||||
}
|
||||
public ApiHandleConfig(MethodInfo methodInfo)
|
||||
{
|
||||
EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out EmitDelegate);
|
||||
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;
|
||||
|
||||
|
||||
public async Task<object> HandleGet(object instance, 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] = JsonConvert.DeserializeObject(argValue, type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = type.IsValueType ? Activator.CreateInstance(type) : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object result;
|
||||
try
|
||||
{
|
||||
if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func<object, object[], Task<object>> hasResultTask)
|
||||
{
|
||||
result = await hasResultTask(instance, args);
|
||||
}
|
||||
else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
|
||||
{
|
||||
await task.Invoke(instance, args);
|
||||
result = null;
|
||||
}
|
||||
else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
|
||||
{
|
||||
result = func.Invoke(instance, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = null;
|
||||
await Console.Out.WriteLineAsync(ex.Message);
|
||||
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<object> HandlePost(object instance, JObject jsonObject, 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 (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;
|
||||
try
|
||||
{
|
||||
if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func<object, object[], Task<object>> hasResultTask)
|
||||
{
|
||||
result = await hasResultTask(instance, args);
|
||||
}
|
||||
else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
|
||||
{
|
||||
await task.Invoke(instance, args);
|
||||
result = null;
|
||||
}
|
||||
else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
|
||||
{
|
||||
result = func.Invoke(instance, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = null;
|
||||
await Console.Out.WriteLineAsync(ex.Message);
|
||||
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 路由注册与解析
|
||||
/// </summary>
|
||||
@@ -35,30 +228,30 @@ namespace Serein.Library.Web
|
||||
/// <summary>
|
||||
/// 控制器实例对象的类型,每次调用都会重新实例化,[Url - ControllerType]
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, Type> _controllerTypes; // 存储控制器类型
|
||||
private readonly ConcurrentDictionary<string, Type> _controllerTypes = new ConcurrentDictionary<string, Type>();
|
||||
|
||||
/// <summary>
|
||||
/// 用于存储路由信息,[GET|POST - [Url - Method]]
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>> _routes;
|
||||
//private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>> _routes = new ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>>();
|
||||
|
||||
|
||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, ApiHandleConfig>> HandleModels = new ConcurrentDictionary<string, ConcurrentDictionary<string, ApiHandleConfig>>();
|
||||
|
||||
|
||||
// private readonly ILoggerService loggerService; // 用于存储路由信息
|
||||
|
||||
//private Type PostRequest;
|
||||
|
||||
public Router(ISereinIOC SereinIOC)
|
||||
{
|
||||
this.SereinIOC = SereinIOC;
|
||||
_routes = new ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>>(); // 初始化路由字典
|
||||
_controllerTypes = new ConcurrentDictionary<string, Type>(); // 初始化控制器实例对象字典
|
||||
foreach (API method in Enum.GetValues(typeof(API))) // 遍历 HTTP 枚举类型的所有值
|
||||
foreach (ApiType method in Enum.GetValues(typeof(ApiType))) // 遍历 HTTP 枚举类型的所有值
|
||||
{
|
||||
_routes.TryAdd(method.ToString(), new ConcurrentDictionary<string, MethodInfo>()); // 初始化每种 HTTP 方法对应的路由字典
|
||||
HandleModels.TryAdd(method.ToString(), new ConcurrentDictionary<string, ApiHandleConfig>()); // 初始化每种 HTTP 方法对应的路由字典
|
||||
}
|
||||
#if false
|
||||
Type baseAttribute = typeof(AutoHostingAttribute);
|
||||
Type baseController = typeof(ControllerBase);
|
||||
|
||||
|
||||
// 获取当前程序集
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
// 获取包含“Controller”名称的类型
|
||||
@@ -69,9 +262,44 @@ namespace Serein.Library.Web
|
||||
foreach (var controllerType in controllerTypes)
|
||||
{
|
||||
RegisterController(controllerType);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void AddHandle(Type controllerType)
|
||||
{
|
||||
if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回
|
||||
|
||||
var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
|
||||
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 = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
|
||||
if (url is null) continue;
|
||||
|
||||
Console.WriteLine(url);
|
||||
var apiType = routeAttribute.ApiType.ToString();
|
||||
|
||||
var config = new ApiHandleConfig(method);
|
||||
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>
|
||||
@@ -84,14 +312,14 @@ namespace Serein.Library.Web
|
||||
var url = request.Url; // 获取请求的 URL
|
||||
var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法
|
||||
var template = request.Url.AbsolutePath.ToLower();
|
||||
if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
|
||||
if (!_controllerTypes.TryGetValue(template, out Type controllerType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
|
||||
|
||||
ControllerBase controllerInstance = (ControllerBase)SereinIOC.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
|
||||
ControllerBase controllerInstance = (ControllerBase)SereinIOC.Instantiate(controllerType);
|
||||
|
||||
if (controllerInstance is null)
|
||||
{
|
||||
@@ -99,195 +327,51 @@ namespace Serein.Library.Web
|
||||
}
|
||||
|
||||
controllerInstance.Url = url.AbsolutePath;
|
||||
|
||||
|
||||
if (!HandleModels.TryGetValue(httpMethod, out var modules))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!modules.TryGetValue(template,out var config))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
dynamic invokeResult;
|
||||
switch (httpMethod)
|
||||
{
|
||||
case "GET":
|
||||
invokeResult = config.HandleGet(controllerInstance, routeValues);
|
||||
break;
|
||||
case "POST":
|
||||
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
|
||||
controllerInstance.BobyData = requestBody;
|
||||
var requestJObject = JObject.Parse(requestBody);
|
||||
invokeResult = config.HandlePost(controllerInstance, requestJObject, routeValues);
|
||||
break;
|
||||
default:
|
||||
invokeResult = null;
|
||||
break;
|
||||
}
|
||||
object result;
|
||||
try
|
||||
{
|
||||
object result;
|
||||
switch (httpMethod) // 根据请求的 HTTP 方法执行不同的操作
|
||||
{
|
||||
case "GET": // 如果是 GET 请求,传入方法、控制器、url参数
|
||||
// loggerService.Information(GetLog(template));
|
||||
result = InvokeControllerMethodWithRouteValues(method, controllerInstance, routeValues);
|
||||
break;
|
||||
case "POST": // POST 请求传入方法、控制器、请求体内容,url参数
|
||||
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
|
||||
controllerInstance.BobyData = requestBody;
|
||||
var requestJObject = JObject.Parse(requestBody); //requestBody.FromJSON<JObject>();
|
||||
|
||||
// loggerService.Information(GetLog(template, requestBody));
|
||||
result = InvokeControllerMethod(method, controllerInstance, requestJObject, routeValues);
|
||||
break;
|
||||
default:
|
||||
result = null;
|
||||
break;
|
||||
}
|
||||
Return(response, result); // 返回结果
|
||||
return true;
|
||||
result = invokeResult.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception)
|
||||
{
|
||||
response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
||||
Return(response, ex.Message); // 返回结果
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动注册并实例化控制器类型
|
||||
/// </summary>
|
||||
/// <param name="controllerType"></param>
|
||||
public bool RegisterController(Type controllerType) // 方法声明,用于注册并实例化控制器类型
|
||||
{
|
||||
if (!controllerType.IsClass || controllerType.IsAbstract) return false; // 如果不是类或者是抽象类,则直接返回
|
||||
|
||||
var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
|
||||
var methods = controllerType.GetMethods().Where(m => m.GetCustomAttribute<WebApiAttribute>() != null).ToArray();
|
||||
|
||||
foreach (var method in methods) // 遍历控制器类型的所有方法
|
||||
{
|
||||
var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
|
||||
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
|
||||
{
|
||||
var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
|
||||
Console.WriteLine(url);
|
||||
if (url is null) continue;
|
||||
_controllerTypes[url] = controllerType;
|
||||
}
|
||||
result = invokeResult;
|
||||
}
|
||||
Return(response, result); // 返回结果
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
#region 调用Get Post对应的方法
|
||||
|
||||
/// <summary>
|
||||
/// GET请求的控制器方法
|
||||
/// </summary>
|
||||
private object InvokeControllerMethodWithRouteValues(MethodInfo method, object controllerInstance, Dictionary<string, string> routeValues)
|
||||
{
|
||||
object[] parameters = GetMethodParameters(method, routeValues);
|
||||
return InvokeMethod(method, controllerInstance, parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET请求调用控制器方法传入参数
|
||||
/// </summary>
|
||||
/// <param name="method">方法</param>
|
||||
/// <param name="controllerInstance">控制器实例</param>
|
||||
/// <param name="methodParameters">参数列表</param>
|
||||
/// <returns></returns>
|
||||
private 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("'", startIndex);
|
||||
// 提取类型信息
|
||||
string typeInfo = ex.Message.Substring(startIndex, endIndex - startIndex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
return result; // 调用方法并返回结果
|
||||
}
|
||||
|
||||
private readonly Dictionary<MethodInfo, ParameterInfo[]> methodParameterCache = new Dictionary<MethodInfo, ParameterInfo[]>();
|
||||
/// <summary>
|
||||
/// POST请求调用控制器方法传入参数
|
||||
/// </summary>
|
||||
public object InvokeControllerMethod(MethodInfo method, object controllerInstance, JObject requestData, Dictionary<string, string> routeValues)
|
||||
{
|
||||
if (requestData is null) return null;
|
||||
ParameterInfo[] parameters;
|
||||
object[] cachedMethodParameters;
|
||||
if (!methodParameterCache.TryGetValue(method, out 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(UrlAttribute)) != null;
|
||||
bool isBobyData = parameters[i].GetCustomAttribute(typeof(BobyAttribute)) != null;
|
||||
|
||||
if (isUrlData)
|
||||
{
|
||||
if (routeValues.ContainsKey(paramName))
|
||||
{
|
||||
cachedMethodParameters[i] = ConvertValue(routeValues[paramName], parameters[i].ParameterType);
|
||||
}
|
||||
else
|
||||
{
|
||||
cachedMethodParameters[i] = null;
|
||||
}
|
||||
}
|
||||
else if (isBobyData)
|
||||
{
|
||||
cachedMethodParameters[i] = ConvertValue(requestData.ToString(), parameters[i].ParameterType);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (requestData.ContainsKey(paramName))
|
||||
{
|
||||
var rd = requestData[paramName];
|
||||
if (parameters[i].ParameterType == typeof(string))
|
||||
{
|
||||
cachedMethodParameters[i] = rd.ToString();
|
||||
}
|
||||
else if (parameters[i].ParameterType == typeof(bool))
|
||||
{
|
||||
cachedMethodParameters[i] = rd.ToBool();
|
||||
}
|
||||
else if (parameters[i].ParameterType == typeof(int))
|
||||
{
|
||||
cachedMethodParameters[i] = rd.ToInt();
|
||||
}
|
||||
else if (parameters[i].ParameterType == typeof(double))
|
||||
{
|
||||
cachedMethodParameters[i] = rd.ToDouble();
|
||||
}
|
||||
else
|
||||
{
|
||||
cachedMethodParameters[i] = ConvertValue(rd, parameters[i].ParameterType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cachedMethodParameters[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存方法和参数的映射
|
||||
//methodParameterCache[method] = cachedMethodParameters;
|
||||
|
||||
|
||||
// 调用方法
|
||||
return method.Invoke(controllerInstance, cachedMethodParameters);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region 工具方法
|
||||
/// <summary>
|
||||
/// 读取Body中的消息
|
||||
/// </summary>
|
||||
@@ -304,72 +388,6 @@ namespace Serein.Library.Web
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查方法入参参数类型,返回对应的入参数组
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="routeValues"></param>
|
||||
/// <returns></returns>
|
||||
private object[] GetMethodParameters(MethodInfo method, Dictionary<string, string> routeValues)
|
||||
{
|
||||
ParameterInfo[] methodParameters = method.GetParameters();
|
||||
object[] parameters = new object[methodParameters.Length];
|
||||
|
||||
for (int i = 0; i < methodParameters.Length; i++)
|
||||
{
|
||||
string paramName = methodParameters[i].Name;
|
||||
if (routeValues.ContainsKey(paramName))
|
||||
{
|
||||
parameters[i] = ConvertValue(routeValues[paramName], methodParameters[i].ParameterType);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转为对应的类型
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <returns></returns>
|
||||
private object ConvertValue(object value, Type targetType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (targetType == typeof(string))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return JsonConvert.DeserializeObject(value.ToString(), targetType);
|
||||
}
|
||||
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
return value;
|
||||
}
|
||||
catch (JsonSerializationException ex)
|
||||
{
|
||||
// 如果无法转为对应的JSON对象
|
||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
||||
int endIndex = ex.Message.IndexOf("'", startIndex); // 查找类型信息结束的索引
|
||||
var typeInfo = ex.Message.Substring(startIndex, endIndex - startIndex); // 提取出错类型信息,该怎么传出去?
|
||||
return null;
|
||||
}
|
||||
catch // (Exception ex)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回响应消息
|
||||
/// </summary>
|
||||
@@ -449,7 +467,7 @@ namespace Serein.Library.Web
|
||||
controllerName = autoHostingAttribute.Url;
|
||||
}
|
||||
|
||||
var httpMethod = webAttribute.Http; // 获取 HTTP 方法
|
||||
var httpMethod = webAttribute.ApiType; // 获取 HTTP 方法
|
||||
var customUrl = webAttribute.Url; // 获取自定义 URL
|
||||
|
||||
string url;
|
||||
@@ -466,7 +484,7 @@ namespace Serein.Library.Web
|
||||
customUrl = CleanUrl(customUrl);
|
||||
url = $"/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||
}
|
||||
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
||||
//HandleModels[httpMethod.ToString()].TryAdd(url ); // 将 URL 和方法添加到对应的路由字典中
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -479,7 +497,7 @@ namespace Serein.Library.Web
|
||||
customUrl = CleanUrl(customUrl);
|
||||
url = $"/{controllerName}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||
}
|
||||
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
||||
// _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
||||
}
|
||||
|
||||
return url;
|
||||
@@ -539,7 +557,7 @@ namespace Serein.Library.Web
|
||||
foreach (var kvp in parsedQuery)
|
||||
{
|
||||
//Console.WriteLine($"{kvp.Key}: {kvp.Value}");
|
||||
routeValues[kvp.Key] = kvp.Value; // 将键值对添加到路由参数字典中
|
||||
routeValues[kvp.Key.ToLower()] = kvp.Value; // 将键值对添加到路由参数字典中
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,7 +585,6 @@ namespace Serein.Library.Web
|
||||
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,9 +10,18 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class AutoSocketHandleAttribute : Attribute
|
||||
{
|
||||
public string ThemeValue;
|
||||
public string ThemeValue = string.Empty;
|
||||
public bool IsReturnValue = true;
|
||||
//public Type DataType;
|
||||
}
|
||||
|
||||
public class SocketHandleModel
|
||||
{
|
||||
public string ThemeValue { get; set; } = string.Empty;
|
||||
public bool IsReturnValue { get; set; } = true;
|
||||
}
|
||||
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class AutoSocketModuleAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -20,17 +20,24 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
private readonly Delegate EmitDelegate;
|
||||
private readonly EmitHelper.EmitMethodType EmitMethodType;
|
||||
|
||||
public MyHandleConfig(ISocketControlBase instance, MethodInfo methodInfo)
|
||||
private Action<Exception, Action<object>> OnExceptionTracking;
|
||||
|
||||
public MyHandleConfig(SocketHandleModel model,ISocketControlBase instance, MethodInfo methodInfo, Action<Exception, Action<object>> onExceptionTracking)
|
||||
{
|
||||
EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo,out EmitDelegate);
|
||||
|
||||
this.Model = model;
|
||||
Instance = instance;
|
||||
var parameterInfos = methodInfo.GetParameters();
|
||||
ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray();
|
||||
ParameterName = parameterInfos.Select(t => t.Name).ToArray();
|
||||
this.HandleGuid = instance.HandleGuid;
|
||||
this.OnExceptionTracking = onExceptionTracking;
|
||||
|
||||
}
|
||||
public ISocketControlBase Instance { get; private set; }
|
||||
|
||||
private SocketHandleModel Model;
|
||||
private ISocketControlBase Instance;
|
||||
public Guid HandleGuid { get; }
|
||||
private string[] ParameterName;
|
||||
private Type[] ParameterType;
|
||||
|
||||
@@ -92,30 +99,44 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
|
||||
|
||||
}
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
//Stopwatch sw = new Stopwatch();
|
||||
//sw.Start();
|
||||
object result;
|
||||
if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func<object, object[], Task<object>> hasResultTask)
|
||||
try
|
||||
{
|
||||
result = await hasResultTask(Instance, args);
|
||||
if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func<object, object[], Task<object>> hasResultTask)
|
||||
{
|
||||
result = await hasResultTask(Instance, args);
|
||||
}
|
||||
else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
|
||||
{
|
||||
await task.Invoke(Instance, args);
|
||||
result = null;
|
||||
}
|
||||
else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
|
||||
{
|
||||
result = func.Invoke(Instance, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
|
||||
catch (Exception ex)
|
||||
{
|
||||
await task.Invoke(Instance, args);
|
||||
result = null;
|
||||
}
|
||||
else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
|
||||
{
|
||||
result = func.Invoke(Instance, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("构造委托无法正确调用");
|
||||
}
|
||||
sw.Stop();
|
||||
Console.WriteLine($"Emit Invoke:{sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs");
|
||||
await Console.Out.WriteLineAsync(ex.Message);
|
||||
this.OnExceptionTracking.Invoke(ex, (async data =>
|
||||
{
|
||||
|
||||
if(result != null && result.GetType().IsClass)
|
||||
var jsonText = JsonConvert.SerializeObject(data);
|
||||
await RecoverAsync.Invoke(jsonText);
|
||||
}));
|
||||
}
|
||||
//sw.Stop();
|
||||
//Console.WriteLine($"Emit Invoke:{sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs");
|
||||
|
||||
if(Model.IsReturnValue && result != null && result.GetType().IsClass)
|
||||
{
|
||||
var reusltJsonText = JsonConvert.SerializeObject(result);
|
||||
_ = RecoverAsync.Invoke($"{reusltJsonText}");
|
||||
|
||||
@@ -18,25 +18,30 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
public string ThemeJsonKey { get; }
|
||||
public string DataJsonKey { get; }
|
||||
|
||||
|
||||
|
||||
|
||||
public ConcurrentDictionary<string, MyHandleConfig> MyHandleConfigs = new ConcurrentDictionary<string, MyHandleConfig>();
|
||||
public void AddHandleConfigs(string themeValue, ISocketControlBase instance, MethodInfo methodInfo)
|
||||
public void AddHandleConfigs(SocketHandleModel model, ISocketControlBase instance, MethodInfo methodInfo
|
||||
, Action<Exception, Action<object>> onExceptionTracking)
|
||||
{
|
||||
if (!MyHandleConfigs.ContainsKey(themeValue))
|
||||
if (!MyHandleConfigs.ContainsKey(model.ThemeValue))
|
||||
{
|
||||
var myHandleConfig = new MyHandleConfig(instance, methodInfo);
|
||||
MyHandleConfigs[themeValue] = myHandleConfig;
|
||||
var myHandleConfig = new MyHandleConfig(model,instance, methodInfo, onExceptionTracking);
|
||||
MyHandleConfigs[model.ThemeValue] = myHandleConfig;
|
||||
}
|
||||
}
|
||||
public void ResetConfig(ISocketControlBase socketControlBase)
|
||||
public bool ResetConfig(ISocketControlBase socketControlBase)
|
||||
{
|
||||
foreach (var kv in MyHandleConfigs.ToArray())
|
||||
{
|
||||
var config = kv.Value;
|
||||
if (config.Instance.HandleGuid.Equals(socketControlBase.HandleGuid))
|
||||
if (config.HandleGuid.Equals(socketControlBase.HandleGuid))
|
||||
{
|
||||
MyHandleConfigs.TryRemove(kv.Key, out _);
|
||||
}
|
||||
}
|
||||
return MyHandleConfigs.Count == 0;
|
||||
}
|
||||
|
||||
public void ResetConfig()
|
||||
|
||||
@@ -28,7 +28,11 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
public ConcurrentDictionary<(string, string), MyHandleModule> MyHandleModuleDict
|
||||
= new ConcurrentDictionary<(string, string), MyHandleModule>();
|
||||
|
||||
|
||||
private Action<Exception, Action<object>> _onExceptionTracking;
|
||||
/// <summary>
|
||||
/// 异常跟踪
|
||||
/// </summary>
|
||||
public event Action<Exception, Action<object>> OnExceptionTracking;
|
||||
|
||||
private MyHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName)
|
||||
{
|
||||
@@ -52,13 +56,14 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
var themeKeyName = moduleAttribute.JsonThemeField;
|
||||
var dataKeyName = moduleAttribute.JsonDataField;
|
||||
var key = (themeKeyName, dataKeyName);
|
||||
if (MyHandleModuleDict.TryRemove(key, out var myHandleModules))
|
||||
if (MyHandleModuleDict.TryGetValue(key, out var myHandleModules))
|
||||
{
|
||||
myHandleModules.ResetConfig(socketControlBase);
|
||||
var isRemote = myHandleModules.ResetConfig(socketControlBase);
|
||||
if (isRemote) MyHandleModuleDict.TryGetValue(key, out _);
|
||||
}
|
||||
|
||||
}
|
||||
public void AddModule(ISocketControlBase socketControlBase)
|
||||
public void AddModule(ISocketControlBase socketControlBase, Action<Exception, Action<object>> onExceptionTracking)
|
||||
{
|
||||
var type = socketControlBase.GetType();
|
||||
var moduleAttribute = type.GetCustomAttribute<AutoSocketModuleAttribute>();
|
||||
@@ -67,10 +72,9 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加处理模块
|
||||
var themeKey = moduleAttribute.JsonThemeField;
|
||||
var dataKey = moduleAttribute.JsonDataField;
|
||||
|
||||
|
||||
var handlemodule = AddMyHandleModule(themeKey, dataKey);
|
||||
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Select(method =>
|
||||
@@ -78,23 +82,35 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
var methodsAttribute = method.GetCustomAttribute<AutoSocketHandleAttribute>();
|
||||
if (methodsAttribute is null)
|
||||
{
|
||||
return (string.Empty, null);
|
||||
return (null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(methodsAttribute.ThemeValue))
|
||||
{
|
||||
methodsAttribute.ThemeValue = method.Name;
|
||||
}
|
||||
var model = new SocketHandleModel
|
||||
{
|
||||
IsReturnValue = methodsAttribute.IsReturnValue,
|
||||
ThemeValue = methodsAttribute.ThemeValue,
|
||||
};
|
||||
var value = methodsAttribute.ThemeValue;
|
||||
return (value, method);
|
||||
return (model, method);
|
||||
}
|
||||
})
|
||||
.Where(x => !string.IsNullOrEmpty(x.value)).ToList();
|
||||
.Where(x => !(x.model is null)).ToList();
|
||||
if (methods.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ((var value, var method) in methods)
|
||||
Console.WriteLine($"add websocket handle model :");
|
||||
Console.WriteLine($"theme key, data key : {themeKey}, {dataKey}");
|
||||
foreach ((var model, var method) in methods)
|
||||
{
|
||||
handlemodule.AddHandleConfigs(value, socketControlBase, method);
|
||||
Console.WriteLine($"theme value : {model.ThemeValue}");
|
||||
handlemodule.AddHandleConfigs(model, socketControlBase, method, onExceptionTracking);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -106,7 +122,9 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
{
|
||||
foreach (var module in MyHandleModuleDict.Values)
|
||||
{
|
||||
|
||||
module.HandleSocketMsg(RecoverAsync, json);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -14,15 +14,8 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Network.WebSocketCommunication
|
||||
{
|
||||
[AutoRegister]
|
||||
public class WebSocketServer
|
||||
{
|
||||
public WebSocketServer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public SocketMsgHandleHelper MsgHandleHelper { get; } = new SocketMsgHandleHelper();
|
||||
|
||||
HttpListener listener;
|
||||
|
||||
Reference in New Issue
Block a user