namespace XFEExtension.NetCore.ServerInteractive.Utilities.Helpers; /// /// 路由通配符匹配辅助类 /// internal static class RouteMatchHelper { /// /// 判断路由是否包含通配符 /// /// 路由模式 /// 是否包含通配符 public static bool IsWildcardRoute(string pattern) => pattern.Split('/').Any(segment => segment is "*" or "**"); /// 验证通配符必须独占路径段,且双星号只能出现在末尾。 public static void ValidatePattern(string pattern) { ArgumentException.ThrowIfNullOrWhiteSpace(pattern); var segments = pattern.Split('/'); if (segments.Any(segment => segment.Length == 0 || segment.Contains('*') && segment is not ("*" or "**")) || Array.IndexOf(segments, "**") is var catchAllIndex && catchAllIndex >= 0 && catchAllIndex != segments.Length - 1) throw new ArgumentException($"路由模式无效:'{pattern}'。* 必须匹配一个完整路径段,** 只能作为最后一个路径段。", nameof(pattern)); } /// /// 将路由模式与实际路由进行通配符匹配 /// 支持的通配符: /// - *:匹配一个路径段 /// - **:匹配零个或多个剩余路径段,且只能位于末尾 /// - v1/*:匹配 v1/ 后跟一个路径段 /// - v1/*/test:匹配 v1/ 后跟任意一个段再跟 /test /// /// 包含通配符的路由模式 /// 实际请求路由 /// 是否匹配 public static bool MatchWildcardRoute(string pattern, string route) { var patternSegments = pattern.Split('/', StringSplitOptions.RemoveEmptyEntries); var routeSegments = route.Split('/', StringSplitOptions.RemoveEmptyEntries); var catchAllIndex = Array.IndexOf(patternSegments, "**"); if (catchAllIndex >= 0 && catchAllIndex != patternSegments.Length - 1) return false; if (catchAllIndex < 0 && patternSegments.Length != routeSegments.Length) return false; if (catchAllIndex >= 0 && routeSegments.Length < catchAllIndex) return false; var comparisonLength = catchAllIndex >= 0 ? catchAllIndex : patternSegments.Length; for (var index = 0; index < comparisonLength; index++) { if (patternSegments[index] == "*") continue; if (!patternSegments[index].Equals(routeSegments[index], StringComparison.Ordinal)) return false; } return true; } /// /// 计算路由模式的优先级,优先级规则如下 /// /// /// public static int GetWildcardPatternPriority(string pattern) { var segments = pattern.Split('/', StringSplitOptions.RemoveEmptyEntries); var literalSegmentCount = 0; var wildcardSegmentCount = 0; foreach (var segment in segments) { if (segment is "*" or "**") wildcardSegmentCount++; else literalSegmentCount++; } return (literalSegmentCount * 1000) - (wildcardSegmentCount * 10) + pattern.Length; } }