XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension.NetCore.ServerInteractive

[DLL] Server interaction extension, including user identity verification and querying in conjunction with AutoConfig

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFEExtension.NetCore.ServerInteractive

feat: support multiple EntryPointAttribute per method and wildcard route matching

- Changed EntryPointAttribute to AllowMultiple=true - Updated EntryPointGenerator to collect all EntryPointAttribute instances per method - Added XFE0013 diagnostic for invalid wildcard usage (e.g., v1/a*b) - Added RouteMatchHelper for wildcard pattern matching (* matches any segment) - Updated XFEServerCore to try exact match first, then wildcard patterns - Updated XFEServerCoreBuilder to separate exact and wildcard routes - Empty path in [EntryPoint("")] is treated as "*" (match all) Agent-Logs-Url: https://github.com/XFEstudio/XFEExtension.NetCore.ServerInteractive/sessions/2a0c1e13-19b9-4694-8959-523c6d627a1a Co-authored-by: XFEstudio <132526994+XFEstudio@users.noreply.github.com>

43e8266
copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
提交于

代码差异

6 个文件 +160 -41
Modified XFEExtension.NetCore.ServerInteractive.SourceGenerator/AnalyzerReleases.Unshipped.md +2 -1
@@ -14,4 +14,5 @@ XFE0008 | XFEServerInteractive | Error | ClientRequestGenerator, [Documentation]
14 14 XFE0009 | XFEServerInteractive | Error | ClientRequestGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0009)
15 15 XFE0010 | XFEServerInteractive | Error | ClientRequestGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0010)
16 16 XFE0011 | XFEServerInteractive | Error | ClientRequestGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0011)
17 XFE0012 | XFEServerInteractive | Error | EntryPointGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0012)
17 XFE0012 | XFEServerInteractive | Error | EntryPointGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0012)
18 XFE0013 | XFEServerInteractive | Error | EntryPointGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0013)
Modified XFEExtension.NetCore.ServerInteractive.SourceGenerator/EntryPointGenerator.cs +65 -31
@@ -62,14 +62,24 @@ public class EntryPointGenerator : IIncrementalGenerator
62 62 helpLinkUri: "https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0012",
63 63 isEnabledByDefault: true);
64 64
65 private static readonly DiagnosticDescriptor InvalidWildcardUsageRule = new(
66 id: "XFE0013",
67 title: "EntryPoint通配符使用无效",
68 messageFormat: "入口点路径'{0}'中的通配符'*'必须作为完整的路径段使用(例如:v1/*/test),不能与其他字符混合(例如:v1/a*b)",
69 category: "XFEServerInteractive",
70 defaultSeverity: DiagnosticSeverity.Error,
71 helpLinkUri: "https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0013",
72 isEnabledByDefault: true);
73
65 74 public void Initialize(IncrementalGeneratorInitializationContext context)
66 75 {
67 76 // 找到所有标记了EntryPointAttribute的方法
68 77 var methodDeclarations = context.SyntaxProvider
69 78 .CreateSyntaxProvider(
70 79 predicate: static (s, _) => IsCandidateMethod(s),
71 transform: static (ctx, _) => GetMethodForGeneration(ctx))
72 .Where(static m => m is not null);
80 transform: static (ctx, _) => GetMethodsForGeneration(ctx))
81 .Where(static m => m is { IsDefault: false, Length: > 0 })
82 .SelectMany(static (m, _) => m);
73 83
74 84 // 按类分组
75 85 var compilationAndMethods = context.CompilationProvider.Combine(methodDeclarations.Collect());
@@ -81,25 +91,21 @@ public class EntryPointGenerator : IIncrementalGenerator
81 91
82 92 private static bool IsCandidateMethod(SyntaxNode node) => node is MethodDeclarationSyntax { AttributeLists.Count: > 0 };
83 93
84 private static MethodCandidate? GetMethodForGeneration(GeneratorSyntaxContext context)
94 private static ImmutableArray<MethodCandidate> GetMethodsForGeneration(GeneratorSyntaxContext context)
85 95 {
86 96 var methodDeclaration = (MethodDeclarationSyntax)context.Node;
87 97 var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclaration);
88 98
89 99 if (methodSymbol is null)
90 return null;
100 return default;
91 101
92 // 检查是否有EntryPointAttribute
93 var entryPointAttribute = methodSymbol.GetAttributes()
94 .FirstOrDefault(a => a.AttributeClass?.Name == "EntryPointAttribute");
102 // 获取所有EntryPointAttribute
103 var entryPointAttributes = methodSymbol.GetAttributes()
104 .Where(a => a.AttributeClass?.Name == "EntryPointAttribute")
105 .ToList();
95 106
96 if (entryPointAttribute is null)
97 return null;
98
99 // 获取Path参数
100 var path = entryPointAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString();
101 if (string.IsNullOrEmpty(path))
102 return null;
107 if (entryPointAttributes.Count == 0)
108 return default;
103 109
104 110 // 检查返回类型:根据返回类型(而非async关键字)判断同步/异步
105 111 // 注意:Task和Task<T>都是有效的异步返回类型(Task<T>可通过协变赋值给Func<Task>)
@@ -126,23 +132,35 @@ public class EntryPointGenerator : IIncrementalGenerator
126 132 ? LocationInfo.From(classDeclaration.Identifier.GetLocation())
127 133 : methodLocation;
128 134
129 return new MethodCandidate(
130 containingType.ContainingNamespace.ToDisplayString(),
131 containingType.Name,
132 methodSymbol.Name,
133 path!,
134 isAsync,
135 isContainingTypePartial,
136 methodSymbol.Parameters.Length,
137 hasValidReturnType,
138 returnType.ToDisplayString(),
139 methodLocation,
140 classLocation,
141 typeParameters,
142 typeConstraints);
135 var results = new List<MethodCandidate>();
136
137 foreach (var attr in entryPointAttributes)
138 {
139 var path = attr.ConstructorArguments.FirstOrDefault().Value?.ToString() ?? "*";
140 // 空路径或 "*" 均视为全匹配通配符
141 if (string.IsNullOrEmpty(path))
142 path = "*";
143
144 results.Add(new MethodCandidate(
145 containingType.ContainingNamespace.ToDisplayString(),
146 containingType.Name,
147 methodSymbol.Name,
148 path,
149 isAsync,
150 isContainingTypePartial,
151 methodSymbol.Parameters.Length,
152 hasValidReturnType,
153 returnType.ToDisplayString(),
154 methodLocation,
155 classLocation,
156 typeParameters,
157 typeConstraints));
158 }
159
160 return results.Count == 0 ? default : ImmutableArray.CreateRange(results);
143 161 }
144 162
145 private static void Execute(Compilation compilation, ImmutableArray<MethodCandidate?> methods, SourceProductionContext context)
163 private static void Execute(Compilation compilation, ImmutableArray<MethodCandidate> methods, SourceProductionContext context)
146 164 {
147 165 if (methods.IsDefaultOrEmpty)
148 166 return;
@@ -152,8 +170,6 @@ public class EntryPointGenerator : IIncrementalGenerator
152 170 // 校验并报告诊断信息
153 171 foreach (var method in methods)
154 172 {
155 if (method is null) continue;
156
157 173 var isValid = true;
158 174
159 175 // 校验:包含类型必须为partial
@@ -197,6 +213,24 @@ public class EntryPointGenerator : IIncrementalGenerator
197 213 isValid = false;
198 214 }
199 215
216 // 校验:通配符 '*' 必须作为完整路径段使用
217 if (method.Path.Contains("*") && method.Path != "*")
218 {
219 var segments = method.Path.Split('/');
220 foreach (var segment in segments)
221 {
222 if (segment.Contains("*") && segment != "*")
223 {
224 context.ReportDiagnostic(Diagnostic.Create(
225 InvalidWildcardUsageRule,
226 method.MethodLocation.ToLocation(),
227 method.Path));
228 isValid = false;
229 break;
230 }
231 }
232 }
233
200 234 if (isValid)
201 235 {
202 236 validMethods.Add(method);
Modified XFEExtension.NetCore.ServerInteractive/Attributes/EntryPointAttribute.cs +1 -1
@@ -3,7 +3,7 @@ namespace XFEExtension.NetCore.ServerInteractive.Attributes;
3 3 /// <summary>
4 4 /// 次级入口点特性,用于标记IServerCoreStandardService中的处理方法
5 5 /// </summary>
6 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
6 [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
7 7 public class EntryPointAttribute : Attribute
8 8 {
9 9 /// <summary>
Added XFEExtension.NetCore.ServerInteractive/Utilities/Helpers/RouteMatchHelper.cs +47 -0
@@ -0,0 +1,47 @@
1 namespace XFEExtension.NetCore.ServerInteractive.Utilities.Helpers;
2
3 /// <summary>
4 /// 路由通配符匹配辅助类
5 /// </summary>
6 internal static class RouteMatchHelper
7 {
8 /// <summary>
9 /// 判断路由是否包含通配符
10 /// </summary>
11 /// <param name="pattern">路由模式</param>
12 /// <returns>是否包含通配符</returns>
13 public static bool IsWildcardRoute(string pattern) => pattern.Contains('*');
14
15 /// <summary>
16 /// 将路由模式与实际路由进行通配符匹配
17 /// <para>支持的通配符:</para>
18 /// <para>- <c>*</c>:匹配任意路径</para>
19 /// <para>- <c>v1/*</c>:匹配 v1/ 后跟一个路径段</para>
20 /// <para>- <c>v1/*/test</c>:匹配 v1/ 后跟任意一个段再跟 /test</para>
21 /// </summary>
22 /// <param name="pattern">包含通配符的路由模式</param>
23 /// <param name="route">实际请求路由</param>
24 /// <returns>是否匹配</returns>
25 public static bool MatchWildcardRoute(string pattern, string route)
26 {
27 // 单独的 "*" 匹配所有路由
28 if (pattern == "*")
29 return true;
30
31 var patternSegments = pattern.Split('/');
32 var routeSegments = route.Split('/');
33
34 if (patternSegments.Length != routeSegments.Length)
35 return false;
36
37 for (var i = 0; i < patternSegments.Length; i++)
38 {
39 if (patternSegments[i] == "*")
40 continue;
41 if (patternSegments[i] != routeSegments[i])
42 return false;
43 }
44
45 return true;
46 }
47 }
Modified XFEExtension.NetCore.ServerInteractive/Utilities/Server/XFEServerCore.cs +30 -5
@@ -52,6 +52,10 @@ public abstract class XFEServerCore : ServerCoreServiceBase
52 52 /// </summary>
53 53 internal Dictionary<string, Func<IServerCoreStandardService>> StandardCoreServiceDictionary = [];
54 54 /// <summary>
55 /// 通配符路由服务工厂列表(模式 → 工厂)
56 /// </summary>
57 internal List<(string Pattern, Func<IServerCoreStandardService> Factory)> WildcardCoreServiceList = [];
58 /// <summary>
55 59 /// 网络通讯服务器
56 60 /// </summary>
57 61 public CyberCommServer CyberCommServer { get; internal set; } = new();
@@ -165,8 +169,29 @@ public abstract class XFEServerCore : ServerCoreServiceBase
165 169 Console.Write($"({ServerCoreName})【{clientIP}】请求路由-{route}:");
166 170 var stopWatch = Stopwatch.StartNew();
167 171
168 // 在字典中查找对应的服务
169 if (StandardCoreServiceDictionary.TryGetValue(route, out var serviceFactory))
172 // 在字典中查找对应的服务(先精确匹配,再通配符匹配)
173 Func<IServerCoreStandardService>? serviceFactory = null;
174 string? matchedPattern = null;
175
176 if (StandardCoreServiceDictionary.TryGetValue(route, out serviceFactory))
177 {
178 matchedPattern = route;
179 }
180 else
181 {
182 // 尝试通配符匹配
183 foreach (var (pattern, factory) in WildcardCoreServiceList)
184 {
185 if (RouteMatchHelper.MatchWildcardRoute(pattern, route))
186 {
187 matchedPattern = pattern;
188 serviceFactory = factory;
189 break;
190 }
191 }
192 }
193
194 if (serviceFactory is not null && matchedPattern is not null)
170 195 {
171 196 var serviceInstance = serviceFactory();
172 197 try
@@ -178,10 +203,10 @@ public abstract class XFEServerCore : ServerCoreServiceBase
178 203 serviceInstance.ReturnArgs = r;
179 204 serviceInstance.Initialize();
180 205
181 // 根据路由调用对应的处理方法(同步与异步互斥,优先同步)
182 if (serviceInstance.SyncEntryPoints.TryGetValue(route, out var syncHandler))
206 // 根据匹配的模式调用对应的处理方法(同步与异步互斥,优先同步)
207 if (serviceInstance.SyncEntryPoints.TryGetValue(matchedPattern, out var syncHandler))
183 208 syncHandler();
184 else if (serviceInstance.AsyncEntryPoints.TryGetValue(route, out var asyncHandler))
209 else if (serviceInstance.AsyncEntryPoints.TryGetValue(matchedPattern, out var asyncHandler))
185 210 await asyncHandler();
186 211 else
187 212 {
Modified XFEExtension.NetCore.ServerInteractive/Utilities/Server/XFEServerCoreBuilder.cs +15 -3
@@ -2,6 +2,7 @@
2 2 using XFEExtension.NetCore.ServerInteractive.Implements;
3 3 using XFEExtension.NetCore.ServerInteractive.Interfaces.CoreService;
4 4 using XFEExtension.NetCore.ServerInteractive.Options;
5 using XFEExtension.NetCore.ServerInteractive.Utilities.Helpers;
5 6 using XFEExtension.NetCore.StringExtension;
6 7
7 8 namespace XFEExtension.NetCore.ServerInteractive.Utilities.Server;
@@ -17,6 +18,7 @@ public abstract class XFEServerCoreBuilder : XFEBuilderBase<XFEServerCoreBuilder
17 18 private readonly List<IServerCoreOriginalService> _serverCoreServiceList = [];
18 19 private readonly List<Func<IServerCoreVerifyService>> _serverCoreVerifyServiceList = [];
19 20 private readonly Dictionary<string, Func<IServerCoreStandardService>> _serverStandardCoreServiceDictionary = [];
21 private readonly List<(string Pattern, Func<IServerCoreStandardService> Factory)> _serverWildcardCoreServiceList = [];
20 22
21 23 /// <summary>
22 24 /// 创建XFE服务器核心构建器
@@ -102,15 +104,24 @@ public abstract class XFEServerCoreBuilder : XFEBuilderBase<XFEServerCoreBuilder
102 104 if (entryPointList is null || entryPointList.Count == 0)
103 105 throw new InvalidOperationException($"类型 {typeof(T).Name} 的 EntryPointList 为空");
104 106
105 // 为每个入口点注册服务工厂
107 // 为每个入口点注册服务工厂(区分精确路由和通配符路由)
106 108 foreach (var route in entryPointList)
107 109 {
108 _serverStandardCoreServiceDictionary.Add(route, () =>
110 Func<IServerCoreStandardService> factory = () =>
109 111 {
110 112 var inst = new T();
111 113 ApplyParameter(inst);
112 114 return inst;
113 });
115 };
116
117 if (RouteMatchHelper.IsWildcardRoute(route))
118 {
119 _serverWildcardCoreServiceList.Add((route, factory));
120 }
121 else
122 {
123 _serverStandardCoreServiceDictionary.Add(route, factory);
124 }
114 125 }
115 126 return this;
116 127 }
@@ -128,6 +139,7 @@ public abstract class XFEServerCoreBuilder : XFEBuilderBase<XFEServerCoreBuilder
128 139 }
129 140 _xFEServerCore.ServerCoreVerifyServiceList = _serverCoreVerifyServiceList;
130 141 _xFEServerCore.StandardCoreServiceDictionary = _serverStandardCoreServiceDictionary;
142 _xFEServerCore.WildcardCoreServiceList = _serverWildcardCoreServiceList;
131 143
132 144 if (xFEServerCoreOptions is not null)
133 145 {