XFEExtension.NetCore.ServerInteractive
[DLL] Server interaction extension, including user identity verification and querying in conjunction with AutoConfig
XFEstudio/XFEExtension.NetCore.ServerInteractive
fix: address review comments - best-match wildcards, priority ordering, and name→wildcard validation
1. TryMatchWildcardStandardService now uses priority-based best-match (most specific pattern wins) consistent with server-side XFEServerCore. 2. StandardClientInstanceRequestDictionary exact match now precedes wildcard fallback to prevent broad wildcards from swallowing instance requests. 3. AddRequest<T>() throws InvalidOperationException when a Name alias maps to a wildcard path, preventing literal '*' in request URLs. Agent-Logs-Url: https://github.com/XFEstudio/XFEExtension.NetCore.ServerInteractive/sessions/595a9bed-b384-4e66-a18b-640f71419a7b Co-authored-by: XFEstudio <132526994+XFEstudio@users.noreply.github.com>
7b6dfa9
代码差异
@@ -101,6 +101,23 @@ public abstract class XFEClientRequester : IRequesterBase
result.Message = response;
}
}
else if (StandardClientInstanceRequestDictionary.TryGetValue(serviceName, out var instance))
{
var (response, code) = await InteractiveHelper.GetServerResponse(RequestAddress + $"/{serviceName}", instance.ConstructBody(Session, DeviceInfo, parameters), _jsonSerializerOptions);
result.StatusCode = code;
if (code == HttpStatusCode.OK)
{
var requestResult = instance.ProcessResponse?.Invoke(response);
MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
result.Result = requestResult ?? new();
}
else
{
MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl(response, code));
result.Message = response;
}
}
else if (TryMatchWildcardStandardService(serviceName, out var wildcardFactory, out var matchedPattern))
{
var xFEService = wildcardFactory!();
@@ -134,23 +151,6 @@ public abstract class XFEClientRequester : IRequesterBase
result.Message = response;
}
}
else if (StandardClientInstanceRequestDictionary.TryGetValue(serviceName, out var instance))
{
var (response, code) = await InteractiveHelper.GetServerResponse(RequestAddress + $"/{serviceName}", instance.ConstructBody(Session, DeviceInfo, parameters), _jsonSerializerOptions);
result.StatusCode = code;
if (code == HttpStatusCode.OK)
{
var requestResult = instance.ProcessResponse?.Invoke(response);
MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
result.Result = requestResult ?? new();
}
else
{
MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl(response, code));
result.Message = response;
}
}
return result;
}
catch (Exception ex)
@@ -163,7 +163,7 @@ public abstract class XFEClientRequester : IRequesterBase
}
/// <summary>
/// 尝试通过通配符模式匹配标准请求服务
/// 尝试通过通配符模式匹配标准请求服务(选择最具体的匹配模式,与服务端行为一致)
/// </summary>
/// <param name="serviceName">请求路径</param>
/// <param name="factory">匹配到的服务工厂</param>
@@ -171,17 +171,36 @@ public abstract class XFEClientRequester : IRequesterBase
/// <returns>是否匹配成功</returns>
private bool TryMatchWildcardStandardService(string serviceName, out Func<IStandardRequestService>? factory, out string? matchedPattern)
{
foreach (var (pattern, serviceFactory) in WildcardStandardRequestServiceList)
// 在所有命中的候选中选择最具体的模式(字面量段越多越优先),避免结果依赖注册顺序
static int GetWildcardPatternPriority(string pattern)
{
if (RouteMatchHelper.MatchWildcardRoute(pattern, serviceName))
var segments = pattern.Split('/');
var literalSegmentCount = 0;
var wildcardSegmentCount = 0;
foreach (var segment in segments)
{
factory = serviceFactory;
matchedPattern = pattern;
return true;
if (segment == "*")
wildcardSegmentCount++;
else
literalSegmentCount++;
}
return (literalSegmentCount * 1000) - (wildcardSegmentCount * 10) + pattern.Length;
}
var bestPriority = int.MinValue;
factory = null;
matchedPattern = null;
return false;
foreach (var (pattern, serviceFactory) in WildcardStandardRequestServiceList)
{
if (!RouteMatchHelper.MatchWildcardRoute(pattern, serviceName)) continue;
var currentPriority = GetWildcardPatternPriority(pattern);
if (currentPriority <= bestPriority) continue;
bestPriority = currentPriority;
factory = serviceFactory;
matchedPattern = pattern;
}
return factory is not null;
}
}
@@ -66,6 +66,7 @@ public abstract class XFEClientRequesterBuilder : XFEBuilderBase<XFEClientReques
throw new InvalidOperationException($"类型 {typeof(T).Name} 的 RequestPoints/ResponsePoints/RequestRouteMap 为空,请确保已使用[Request]或[Response]标记方法");
// 为每个路径/名称注册服务工厂(通配符路径注册到通配符列表,其余注册到标准字典)
// 对于Name别名,需检查其目标路径是否为通配符:Name→通配符路径不支持,因为无法确定具体请求路径
foreach (var key in routeKeys)
{
if (RouteMatchHelper.IsWildcardRoute(key))
@@ -81,6 +82,9 @@ public abstract class XFEClientRequesterBuilder : XFEBuilderBase<XFEClientReques
}
else
{
// 检查Name别名是否映射到通配符路径
if (probeService.RequestRouteMap.TryGetValue(key, out var targetPath) && targetPath != key && RouteMatchHelper.IsWildcardRoute(targetPath))
throw new InvalidOperationException($"名称别名 '{key}' 映射到通配符路径 '{targetPath}',不支持通过名称调用通配符路由,请直接使用具体路径进行请求");
_standardRequestServiceDictionary.Add(key, () =>
{
var inst = new T();