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
{
///
/// 路由接口
///
public interface IPathRouter
{
///
/// 添加处理模块
///
///
void AddHandle(Type controllerType);
///
/// 获取路由信息
///
///
List GetRouterInfos();
///
/// 路由解析开始处理
///
///
///
Task HandleAsync(HttpListenerContext context);
}
public enum HandleState
{
///
/// 默认值
///
None ,
///
/// 没有异常
///
Ok,
///
/// 没有对应的控制器
///
NotHanleController,
///
/// 没有对应的Http请求类型
///
NotHttpApiRequestType,
///
/// 没有处理配置
///
NotHandleConfig,
///
/// 无法实例化控制器
///
HanleControllerIsNull,
///
/// 调用参数获取错误
///
InvokeArgError,
///
/// 调用发生异常
///
InvokeErrored,
}
///
/// 调用结果
///
public class InvokeResult
{
///
/// 调用状态
///
public HandleState State { get; set; }
///
/// 调用正常时这里会有数据
///
public object? Data{ get; set; }
///
/// 调用失败时这里可能会有异常数据
///
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,
};
}
///
/// 路由信息
///
public class RouterInfo
{
#if NET6_0_OR_GREATER
///
/// 接口类型
///
public ApiType ApiType { get; set; }
///
/// 接口URL
///
public required string Url { get; set; }
///
/// 对应的处理方法
///
public required MethodInfo MethodInfo { get; set; }
#else
///
/// 接口类型
///
public ApiType ApiType { get; set; }
///
/// 接口URL
///
public string Url { get; set; }
///
/// 对应的处理方法
///
public MethodInfo MethodInfo { get; set; }
#endif
}
///
/// 路由注册与解析
///
public class PathRouter : IPathRouter
{
private readonly ISereinIOC SereinIOC; // 用于存储路由信息
///
/// 控制器实例对象的类型,每次调用都会重新实例化,[Url - ControllerType]
///
private readonly ConcurrentDictionary _controllerTypes = new ConcurrentDictionary();
///
/// 用于存储路由信息,[GET|POST - [Url - ApiHandleConfig]]
///
private readonly ConcurrentDictionary> HandleModels = new ConcurrentDictionary>();
public PathRouter(ISereinIOC SereinIOC)
{
this.SereinIOC = SereinIOC;
foreach (ApiType method in Enum.GetValues(typeof(ApiType))) // 遍历 HTTP 枚举类型的所有值
{
HandleModels.TryAdd(method.ToString(), new ConcurrentDictionary()); // 初始化每种 HTTP 方法对应的路由字典
}
}
///
/// 获取路由信息
///
///
public List GetRouterInfos()
{
var t = HandleModels.Select(kvp => kvp.Value.Select(kvp2 => kvp2.Value.RouterInfo));
return t.SelectMany(x => x).ToList();
}
///
/// 添加处理模块
///
///
public void AddHandle(Type controllerType)
{
if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回
var autoHostingAttribute = controllerType.GetCustomAttribute();
if(autoHostingAttribute is null)
{
SereinEnv.WriteLine(InfoType.WARN, $"类型 \"{controllerType}\" 需要实现 \"WebApiControllerAttribute\" 才可以作为路由控制器");
return;
}
var methods = controllerType.GetMethods().Where(m => m.GetCustomAttribute() != null).ToArray();
foreach (var method in methods) // 遍历控制器类型的所有方法
{
var routeAttribute = method.GetCustomAttribute(); // 获取方法上的 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();
HandleModels[apiType] = configs;
}
configs.TryAdd(url, config);
_controllerTypes.TryAdd(url,controllerType);
}
return;
}
///
/// 在外部调用API后,解析路由,调用对应的方法
///
///
///
public async Task 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);
}
}
///
/// 读取Body中的消息
///
///
///
private async Task ReadRequestBodyAsync(HttpListenerRequest request)
{
using (Stream stream = request.InputStream)
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
return await reader.ReadToEndAsync();
}
}
}
///
/// 从方法中收集路由信息,返回方法对应的url
///
/// 类的特性
/// 方法的特性
/// 控制器类型
/// 方法信息
/// 方法对应的urk
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;
}
///
/// 修正方法特性中的URL格式
///
///
///
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
}
///
/// 方法声明,用于解析 URL 获取路由参数
///
///
///
private Dictionary GetUrlData(Uri uri)
{
Dictionary routeValues = new Dictionary();
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; // 返回路由参数字典
}
}
}