using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using NewLife.Net;
namespace NewLife.Http;
/// <summary>HttpæœåС噍</summary>
/// <remarks>
/// 主è¦èŒè´£ï¼š
/// 1. è·¯ç”±æ˜ å°„ï¼šæ”¯æŒç²¾ç¡®è·¯å¾„ã€å‚数化路由 {param}ã€é€šé…符 * ä¸‰ç§æ¨¡å¼ï¼›
/// 2. ä¸é—´ä»¶ç®¡é“:通过 Use() 注册ä¸é—´ä»¶ï¼ŒæŒ‰é¡ºåºæ‰§è¡Œï¼›
/// 3. 方法感知注册:MapGet/MapPost/MapPut/MapDelete 按 HTTP 方法过滤;
/// 4. 为æ¯ä¸ªç½‘络会è¯åˆ›å»ºå¯¹åº”çš„ <see cref="HttpSession"/> å议处ç†å™¨ã€‚
///
/// 线程安全说明:
/// - 典型使用场景下,路由在å¯åŠ¨é˜¶æ®µé›†ä¸æ³¨å†Œï¼Œè¿è¡ŒæœŸåªè¯»è®¿é—®ï¼›
/// - 若需è¦åœ¨è¿è¡ŒæœŸåЍæ€å¢žåˆ 路由,应在外部自行åºåˆ—åŒ–ï¼ˆåŠ é”)调用 Map 方法。
/// </remarks>
[DisplayName("HTTP server")]
public class HttpServer : NetServer, IHttpHost
{
#region 属性
/// <summary>Httpå“应头Serveråç§°</summary>
public String ServerName { get; set; }
/// <summary>è·¯ç”±æ˜ å°„ã€‚Key 为路径(å¯å« * 通é…),Value 为处ç†å™¨</summary>
public IDictionary<String, IHttpHandler> Routes { get; set; } = new Dictionary<String, IHttpHandler>(StringComparer.OrdinalIgnoreCase);
#endregion
#region æž„é€
/// <summary>实例化HttpæœåС噍</summary>
public HttpServer()
{
Name = "Http";
Port = 80;
ProtocolType = NetType.Http;
var ver = GetType().Assembly.GetName().Version ?? new Version();
ServerName = $"NewLife-HttpServer/{ver.Major}.{ver.Minor}";
}
#endregion
/// <summary>为会è¯åˆ›å»ºç½‘络数æ®å¤„ç†å™¨ã€‚å¯ä½œä¸ºä¸šåС处ç†å®žçŽ°ï¼Œä¹Ÿå¯ä»¥ä½œä¸ºå‰ç½®å议解æž</summary>
/// <param name="session">网络会è¯</param>
/// <returns>Http会è¯å¤„ç†å™¨</returns>
public override INetHandler? CreateHandler(INetSession session) => new HttpSession();
#region 路由注册
/// <summary>æ˜ å°„è·¯ç”±å¤„ç†å™¨</summary>
/// <param name="path">路径,如 /api/testã€/api/{id} 或 /api/*</param>
/// <param name="handler">处ç†å™¨</param>
public void Map(String path, IHttpHandler handler) => SetRoute(path, handler);
/// <summary>æ˜ å°„è·¯ç”±å¤„ç†å™¨ï¼ˆå§”托)</summary>
/// <param name="path">路径</param>
/// <param name="handler">处ç†å§”托</param>
public void Map(String path, HttpProcessDelegate handler) => SetRoute(path, new DelegateHandler { Callback = handler });
/// <summary>æ˜ å°„è·¯ç”±å¤„ç†å™¨ï¼ˆæ— å‚委托)</summary>
/// <typeparam name="TResult">返回类型</typeparam>
/// <param name="path">路径</param>
/// <param name="handler">处ç†å§”托</param>
public void Map<TResult>(String path, Func<TResult> handler) => SetRoute(path, new DelegateHandler { Callback = handler });
/// <summary>æ˜ å°„è·¯ç”±å¤„ç†å™¨ï¼ˆå•傿•°å§”托)</summary>
/// <typeparam name="TModel">傿•°ç±»åž‹</typeparam>
/// <typeparam name="TResult">返回类型</typeparam>
/// <param name="path">路径</param>
/// <param name="handler">处ç†å§”托</param>
public void Map<TModel, TResult>(String path, Func<TModel, TResult> handler) => SetRoute(path, new DelegateHandler { Callback = handler });
/// <summary>æ˜ å°„è·¯ç”±å¤„ç†å™¨ï¼ˆ2傿•°å§”托)</summary>
/// <typeparam name="T1">傿•°1类型</typeparam>
/// <typeparam name="T2">傿•°2类型</typeparam>
/// <typeparam name="TResult">返回类型</typeparam>
/// <param name="path">路径</param>
/// <param name="handler">处ç†å§”托</param>
public void Map<T1, T2, TResult>(String path, Func<T1, T2, TResult> handler) => SetRoute(path, new DelegateHandler { Callback = handler });
/// <summary>æ˜ å°„è·¯ç”±å¤„ç†å™¨ï¼ˆ3傿•°å§”托)</summary>
/// <typeparam name="T1">傿•°1类型</typeparam>
/// <typeparam name="T2">傿•°2类型</typeparam>
/// <typeparam name="T3">傿•°3类型</typeparam>
/// <typeparam name="TResult">返回类型</typeparam>
/// <param name="path">路径</param>
/// <param name="handler">处ç†å§”托</param>
public void Map<T1, T2, T3, TResult>(String path, Func<T1, T2, T3, TResult> handler) => SetRoute(path, new DelegateHandler { Callback = handler });
/// <summary>æ˜ å°„è·¯ç”±å¤„ç†å™¨ï¼ˆ4傿•°å§”托)</summary>
/// <typeparam name="T1">傿•°1类型</typeparam>
/// <typeparam name="T2">傿•°2类型</typeparam>
/// <typeparam name="T3">傿•°3类型</typeparam>
/// <typeparam name="T4">傿•°4类型</typeparam>
/// <typeparam name="TResult">返回类型</typeparam>
/// <param name="path">路径</param>
/// <param name="handler">处ç†å§”托</param>
public void Map<T1, T2, T3, T4, TResult>(String path, Func<T1, T2, T3, T4, TResult> handler) => SetRoute(path, new DelegateHandler { Callback = handler });
/// <summary>æ˜ å°„ GET 路由</summary>
/// <param name="path">è·¯å¾„ï¼Œæ”¯æŒ {param} 傿•°åŒ–</param>
/// <param name="handler">处ç†å§”托</param>
public void MapGet(String path, HttpProcessDelegate handler) => SetRoute(path, new MethodFilterHandler("GET", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ GET è·¯ç”±ï¼ˆæ— å‚)</summary>
public void MapGet<TResult>(String path, Func<TResult> handler) => SetRoute(path, new MethodFilterHandler("GET", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ GET 路由(å•å‚)</summary>
public void MapGet<TModel, TResult>(String path, Func<TModel, TResult> handler) => SetRoute(path, new MethodFilterHandler("GET", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ POST 路由</summary>
/// <param name="path">è·¯å¾„ï¼Œæ”¯æŒ {param} 傿•°åŒ–</param>
/// <param name="handler">处ç†å§”托</param>
public void MapPost(String path, HttpProcessDelegate handler) => SetRoute(path, new MethodFilterHandler("POST", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ POST è·¯ç”±ï¼ˆæ— å‚)</summary>
public void MapPost<TResult>(String path, Func<TResult> handler) => SetRoute(path, new MethodFilterHandler("POST", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ POST 路由(å•å‚)</summary>
public void MapPost<TModel, TResult>(String path, Func<TModel, TResult> handler) => SetRoute(path, new MethodFilterHandler("POST", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ PUT 路由</summary>
public void MapPut(String path, HttpProcessDelegate handler) => SetRoute(path, new MethodFilterHandler("PUT", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ PUT è·¯ç”±ï¼ˆæ— å‚)</summary>
public void MapPut<TResult>(String path, Func<TResult> handler) => SetRoute(path, new MethodFilterHandler("PUT", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ PUT 路由(å•å‚)</summary>
public void MapPut<TModel, TResult>(String path, Func<TModel, TResult> handler) => SetRoute(path, new MethodFilterHandler("PUT", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ DELETE 路由</summary>
public void MapDelete(String path, HttpProcessDelegate handler) => SetRoute(path, new MethodFilterHandler("DELETE", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ DELETE è·¯ç”±ï¼ˆæ— å‚)</summary>
public void MapDelete<TResult>(String path, Func<TResult> handler) => SetRoute(path, new MethodFilterHandler("DELETE", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„ DELETE 路由(å•å‚)</summary>
public void MapDelete<TModel, TResult>(String path, Func<TModel, TResult> handler) => SetRoute(path, new MethodFilterHandler("DELETE", new DelegateHandler { Callback = handler }));
/// <summary>æ˜ å°„æŽ§åˆ¶å™¨</summary>
/// <typeparam name="TController">控制器类型。AOT ä¸‹å…¶å…¬å¼€æž„é€ ä¸Žå…¬å¼€æ–¹æ³•å°†æŒ‰éœ€ä¿ç•™</typeparam>
/// <param name="path">å¯é€‰èµ·å§‹è·¯å¾„,默认 /{ControllerName}</param>
public void MapController<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)] TController>(String? path = null) => MapController(typeof(TController), path);
/// <summary>æ˜ å°„æŽ§åˆ¶å™¨</summary>
/// <param name="controllerType">控制器类型。AOT ä¸‹å…¶å…¬å¼€æž„é€ ä¸Žå…¬å¼€æ–¹æ³•å°†æŒ‰éœ€ä¿ç•™</param>
/// <param name="path">å¯é€‰èµ·å§‹è·¯å¾„,默认 /{ControllerName}</param>
public void MapController([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)] Type controllerType, String? path = null)
{
if (controllerType == null) throw new ArgumentNullException(nameof(controllerType));
if (path.IsNullOrEmpty()) path = "/" + controllerType.Name.TrimSuffix("Controller");
// 移除尾部 /* 防æ¢é‡å¤é€šé…,å†ç»Ÿä¸€è¿½åŠ
path = path.EndsWith("/*") ? path[..^2] : path;
var path2 = path.EnsureStart("/").EnsureEnd("/*");
// 挂载å‰ç¼€äº¤ç»™å¤„ç†å™¨ï¼šå¤šæ®µå‰ç¼€ï¼ˆå¦‚ /api/user)下,处ç†å™¨éœ€å…ˆå‰ªæŽ‰å‰ç¼€æ‰èƒ½å–出æ“作方法å
var handler = new ControllerHandler { ControllerType = controllerType, Path = path.EnsureStart("/").EnsureEnd("/") };
SetRoute(path2, handler);
}
/// <summary>æ˜ å°„é™æ€æ–‡ä»¶ç›®å½•</summary>
/// <param name="path">æ˜ å°„è·¯å¾„ï¼Œå¦‚ /js</param>
/// <param name="contentPath">内容目录,如 /wwwroot/js</param>
public void MapStaticFiles(String path, String contentPath)
{
if (contentPath.IsNullOrEmpty()) throw new ArgumentNullException(nameof(contentPath));
// 移除尾部 /* 防æ¢é‡å¤é€šé…,å†ç»Ÿä¸€è¿½åŠ
path = path.EndsWith("/*") ? path[..^2] : path;
path = path.EnsureStart("/");
var path2 = path.EnsureEnd("/").EnsureEnd("*");
SetRoute(path2, new StaticFilesHandler { Path = path.EnsureEnd("/"), ContentPath = contentPath });
}
/// <summary>æ˜ å°„å†…åµŒèµ„æºç›®å½•</summary>
/// <remarks>
/// 将程åºé›†ä¸çš„åµŒå…¥èµ„æºæ˜ 射为 HTTP 陿€æ–‡ä»¶æœåŠ¡ã€‚
/// 例如 MapEmbedded("/panel", "MyApp.Resources.Panel") å°† /panel/ ä¸‹çš„è¯·æ±‚æ˜ å°„åˆ°åµŒå…¥èµ„æºã€‚
/// </remarks>
/// <param name="path">æ˜ å°„è·¯å¾„ï¼Œå¦‚ /panel</param>
/// <param name="contentPath">资æºåå‰ç¼€ï¼Œå¦‚ MyApp.Resources.Panel</param>
/// <param name="assembly">èµ„æºæ‰€åœ¨çš„程åºé›†ã€‚为 null 时使用入å£ç¨‹åºé›†</param>
public void MapEmbedded(String path, String contentPath, System.Reflection.Assembly? assembly = null)
{
if (contentPath.IsNullOrEmpty()) throw new ArgumentNullException(nameof(contentPath));
// 移除尾部 /* 防æ¢é‡å¤é€šé…,å†ç»Ÿä¸€è¿½åŠ
path = path.EndsWith("/*") ? path[..^2] : path;
path = path.EnsureStart("/");
var path2 = path.EnsureEnd("/").EnsureEnd("*");
SetRoute(path2, new EmbeddedFileHandler { Path = path.EnsureEnd("/"), ContentPath = contentPath, Assembly = assembly });
}
/// <summary>æ˜ å°„å†…åµŒèµ„æºç›®å½•(泛型é‡è½½ï¼Œä»Žç±»åž‹æŽ¨æ–程åºé›†ï¼‰</summary>
/// <typeparam name="T">ç›®æ ‡ç¨‹åºé›†ä¸çš„任一类型</typeparam>
/// <param name="path">æ˜ å°„è·¯å¾„ï¼Œå¦‚ /panel</param>
/// <param name="contentPath">资æºåå‰ç¼€ï¼Œå¦‚ MyApp.Resources.Panel</param>
public void MapEmbedded<T>(String path, String contentPath) => MapEmbedded(path, contentPath, typeof(T).Assembly);
/// <summary>统一设置路由。自动识别 {param} è¯æ³•æ³¨å†Œåˆ°å‚æ•°åŒ–路由器</summary>
/// <param name="path">路径</param>
/// <param name="handler">处ç†å™¨</param>
private void SetRoute(String path, IHttpHandler handler)
{
if (path.IsNullOrEmpty()) throw new ArgumentNullException(nameof(path));
if (handler == null) throw new ArgumentNullException(nameof(handler));
// ç»Ÿä¸€è·¯å¾„æ ¼å¼ï¼šå¿…须以 / 开头
path = path.EnsureStart("/");
// 傿•°åŒ–è·¯ç”±ï¼ˆå« {param} è¯æ³•)注册到 HttpRouter
if (path.Contains('{'))
_router.Register(path, handler);
else
Routes[path] = handler; // 精确/通é…ç¬¦è·¯ç”±ä¿æŒåŽŸè¯ä¹‰
}
#endregion
#region ä¸é—´ä»¶
private readonly List<HttpMiddlewareDelegate> _middlewares = [];
/// <summary>注册ä¸é—´ä»¶ã€‚ä¸é—´ä»¶æŒ‰æ³¨å†Œé¡ºåºä¾æ¬¡æ‰§è¡Œï¼Œå¯åœ¨å¤„ç†å™¨å‰åŽæ·»åŠ é€»è¾‘</summary>
/// <param name="middleware">ä¸é—´ä»¶å§”æ‰˜ï¼šç¬¬ä¸€ä¸ªå‚æ•°ä¸ºä¸Šä¸‹æ–‡ï¼Œç¬¬äºŒä¸ªä¸ºè°ƒç”¨ä¸‹ä¸€ä¸é—´ä»¶çš„委托</param>
/// <remarks>
/// <code>
/// server.Use(async (ctx, next) => {
/// // 请求å‰é€»è¾‘
/// await next();
/// // 请求åŽé€»è¾‘
/// });
/// </code>
/// </remarks>
public void Use(HttpMiddlewareDelegate middleware)
{
if (middleware == null) throw new ArgumentNullException(nameof(middleware));
_middlewares.Add(middleware);
}
/// <summary>å¯ç”¨ CORS 跨域支æŒ</summary>
/// <param name="allowOrigin">å…许的æºï¼Œé»˜è®¤ *</param>
/// <param name="allowMethods">å…许的方法</param>
/// <param name="allowHeaders">å…许的请求头</param>
public void UseCors(String allowOrigin = "*", String allowMethods = "GET, POST, PUT, DELETE, OPTIONS", String allowHeaders = "Content-Type, Authorization, X-Requested-With")
{
var cors = new CorsMiddleware
{
AllowOrigin = allowOrigin,
AllowMethods = allowMethods,
AllowHeaders = allowHeaders
};
Use(cors.Invoke);
}
/// <summary>å¯ç”¨å…¨å±€é”™è¯¯å¤„ç†ä¸é—´ä»¶ï¼ˆæŽ¨è在最外层注册)</summary>
/// <param name="includeDetails">是å¦è¿”回详细异常信æ¯ï¼ˆå¼€å‘环境建议true)</param>
public void UseErrorHandler(Boolean includeDetails = false)
{
var handler = new ErrorHandlerMiddleware { IncludeDetails = includeDetails };
Use(handler.Invoke);
}
/// <summary>构建ä¸é—´ä»¶ç®¡é“,将最终处ç†å™¨åŒ…è£…ä¸ºç®¡é“æœ«ç«¯</summary>
/// <param name="context">Http上下文</param>
/// <param name="finalHandler">最终业务处ç†å™¨ï¼ˆå¯ä¸ºnull)</param>
/// <returns>å¯ç‰å¾…的任务</returns>
internal Task ExecutePipeline(IHttpContext context, IHttpHandler? finalHandler)
{
// æ— ä¸é—´ä»¶æ—¶ç›´æŽ¥æ‰§è¡Œå¤„ç†å™¨ï¼ˆçƒè·¯å¾„ï¼Œä¸æž„é€ ç®¡é“é—包)
if (_middlewares.Count == 0) return InvokeHandlerAsync(finalHandler, context);
// 构建管é“链:middleware1 → middleware2 → ... → finalHandler
Func<Task> handler = () => InvokeHandlerAsync(finalHandler, context);
// å呿ž„建:最内层是 handler,往外é€å±‚包装ä¸é—´ä»¶
for (var i = _middlewares.Count - 1; i >= 0; i--)
{
var middleware = _middlewares[i];
var next = handler;
handler = () => middleware(context, next);
}
return handler();
}
/// <summary>调用处ç†å™¨</summary>
/// <param name="handler">处ç†å™¨</param>
/// <param name="context">Http上下文</param>
/// <returns>处ç†ä»»åŠ¡</returns>
private static Task InvokeHandlerAsync(IHttpHandler? handler, IHttpContext context)
=> handler == null ? TaskEx.CompletedTask : handler.ProcessRequestAsync(context);
#endregion
#region 路由匹é…
/// <summary>傿•°åŒ–è·¯ç”±å™¨ï¼Œå¤„ç† {param} 模å¼è·¯ç”±</summary>
private readonly HttpRouter _router = new();
/// <summary>路径匹é…缓å˜ã€‚Key 为请求路径,Value 为匹é…到的路由键</summary>
/// <remarks>åªç¼“å˜é€šé…符/çŸè·¯å¾„的命ä¸ç»“æžœï¼Œä¸”å®¹é‡æœ‰ä¸Šé™ï¼Œé¿å¼€åŠ¨æ€ URL è®©ç¼“å˜æ— 界增长</remarks>
/// <remarks>声明为 ConcurrentDictionary è€Œéž IDictionary:TryAdd 是具体类型æˆå‘˜ï¼Œä½Žç‰ˆæœ¬ TFM 下 IDictionary 没有该扩展方法</remarks>
private readonly ConcurrentDictionary<String, String> _pathCache = new(StringComparer.OrdinalIgnoreCase);
/// <summary>路径匹é…缓å˜ä¸Šé™ï¼Œé»˜è®¤ 4096。超过åŽä¸å†å†™å…¥</summary>
public Int32 MaxPathCacheSize { get; set; } = 4096;
/// <summary>路径缓å˜å½“剿¡ç›®æ•°ã€‚独立计数:ConcurrentDictionary.Count 会å–全部桶é”,ä¸èƒ½æ”¾åœ¨å†™å…¥çƒè·¯å¾„上</summary>
private Int32 _pathCacheCount;
/// <summary>写入路径缓å˜ï¼ˆå®¹é‡å—é™ï¼‰ã€‚达到上é™åŽé™é»˜ä¸å†™ï¼ŒåŽç»è¯·æ±‚èµ°æ£å¸¸è·¯ç”±åŒ¹é…</summary>
/// <param name="path">请求路径</param>
/// <param name="key">匹é…到的路由键</param>
private void CachePath(String path, String key)
{
// å¹¶å‘下计数å¯èƒ½ç•¥è¶…上é™ï¼ˆå¤šå†™å‡ 个æ¡ç›®ï¼‰ï¼Œæ¢æ¥çƒè·¯å¾„å…é”判æ–
if (Interlocked.Increment(ref _pathCacheCount) > MaxPathCacheSize)
{
Interlocked.Decrement(ref _pathCacheCount);
return;
}
if (!_pathCache.TryAdd(path, key)) Interlocked.Decrement(ref _pathCacheCount);
}
/// <summary>判æ–通é…路由命ä¸çš„路径是å¦å€¼å¾—写入缓å˜</summary>
/// <param name="path">请求路径</param>
/// <param name="handler">命ä¸çš„处ç†å™¨</param>
/// <returns>是å¦ç¼“å˜</returns>
/// <remarks>
/// 缓å˜è®°å½•的是“具体 URL → 通é…é”®â€ï¼Œæ®µæ•°ä¸å—é™çš„ URL ä¼šè®©ç¼“å˜æ— 界增长。
/// 控制器路由的方法åä¸Žå‚æ•°æ®µæ•°é‡æœ‰é™ï¼ŒæŒ‰å‰ªæŽ‰æŒ‚è½½å‰ç¼€åŽçš„剩余段数判æ–,
/// é¿å…多段å‰ç¼€ï¼ˆå¦‚ /api/userï¼‰å› ä¸ºæ€»æ®µæ•°å大而永ä¸ç¼“å˜ï¼›å…¶å®ƒé€šé…路由沿用总段数上é™ã€‚
/// </remarks>
private static Boolean IsCacheablePath(String path, IHttpHandler handler)
{
if (handler is ControllerHandler controller && !controller.Path.IsNullOrEmpty() &&
path.StartsWith(controller.Path, StringComparison.OrdinalIgnoreCase))
{
var rest = path[controller.Path.Length..].TrimStart('/');
return rest.Length > 0 && rest.IndexOf('/') < 0;
}
return path.Split('/').Length <= 3;
}
/// <summary>匹é…处ç†å™¨ï¼ˆå…¼å®¹ IHttpHost 接å£ï¼‰</summary>
/// <param name="path">已规范化åŽçš„请求路径(ä¸å«æŸ¥è¯¢å—符串)</param>
/// <param name="request">Http请求对象(å¯ç”¨äºŽæ·±åº¦åŒ¹é…)</param>
/// <returns>匹é…到的处ç†å™¨ï¼›æ‰¾ä¸åˆ°æ—¶è¿”回 null</returns>
public IHttpHandler? MatchHandler(String path, HttpRequest? request)
{
// 使用临时å—典兼容旧接å£ï¼ˆå‚æ•°åŒ–è·¯ç”±çš„å‚æ•°ä¸ä¼šä¸¢å¤±ï¼Œå› 为调用方会通过新é‡è½½èŽ·å–)
var tempParams = new Dictionary<String, Object?>();
return MatchHandler(path, request, tempParams);
}
/// <summary>匹é…处ç†å™¨ï¼ˆå¢žå¼ºç‰ˆï¼ŒåŒæ—¶è¾“å‡ºè·¯ç”±å‚æ•°ï¼‰</summary>
/// <param name="path">已规范化åŽçš„请求路径(ä¸å«æŸ¥è¯¢å—符串)</param>
/// <param name="request">Http请求对象(å¯ç”¨äºŽæ·±åº¦åŒ¹é…)</param>
/// <param name="parameters">è¾“å‡ºè·¯ç”±å‚æ•°ï¼ˆå¦‚ {id}=123)</param>
/// <returns>匹é…到的处ç†å™¨ï¼›æ‰¾ä¸åˆ°æ—¶è¿”回 null</returns>
public IHttpHandler? MatchHandler(String path, HttpRequest? request, IDictionary<String, Object?> parameters)
{
if (path.IsNullOrEmpty()) return null;
// 直接精确匹é…
if (Routes.TryGetValue(path, out var handler)) return handler;
// 缓å˜åŒ¹é…
if (_pathCache.TryGetValue(path, out var p) && Routes.TryGetValue(p, out handler)) return handler;
// 傿•°åŒ–路由匹é…({param} 模å¼ï¼‰
handler = _router.Match(path, parameters);
if (handler != null)
{
// 傿•°åŒ–路由ä¸å†™ç¼“å˜ï¼šå‘½ä¸ä¾èµ–具体傿•°ï¼ˆç¼“å˜å€¼åªèƒ½æ˜¯è·¯å¾„自身),
// 而 Routes é‡Œæ²¡æœ‰è¯¥é”®ï¼ŒæŸ¥ç¼“å˜æ°¸è¿œå–ä¸åˆ°å¤„ç†å™¨ï¼Œåªä¼šéšæ¯ä¸ªä¸åŒ URL æ— ç•Œå¢žé•¿
return handler;
}
// 模糊匹é…(* 通é…符模å¼ï¼‰
foreach (var item in Routes)
{
var key = item.Key;
if (!key.Contains('*')) continue;
if (!key.IsMatch(path)) continue;
if (Routes.TryGetValue(key, out handler))
{
// åªç¼“å˜æ®µæ•°æœ‰é™ä¸”éžé™æ€æ–‡ä»¶çš„è·¯å¾„ï¼šé™æ€æ–‡ä»¶æŒ‰æ¯ä¸ªå…·ä½“ URL 缓å˜ï¼ˆå¦‚ /img/1.pngã€/img/2.pngï¼‰åŒæ ·ä¼šæ— 界增长
if (handler is not StaticFilesHandler && IsCacheablePath(path, handler)) CachePath(path, key);
return handler;
}
}
return null;
}
#endregion
}
|