XFEExtension.NetCore.ServerInteractive
[DLL] Server interaction extension, including user identity verification and querying in conjunction with AutoConfig
XFEstudio/XFEExtension.NetCore.ServerInteractive
Add Request/Response attributes and ClientRequestGenerator for StandardRequestServiceBase
- Add RequestAttribute and ResponseAttribute in Attributes/ - Remove PostRequest() and AnalyzeResponse() from IStandardRequestService - Add RequestPoints and ResponsePoints dictionaries to IStandardRequestService - Update StandardRequestServiceBase to partial class with virtual dictionaries - Add ClientRequestGenerator source generator (similar to EntryPointGenerator) - Add ClientRequestMethodCandidate model - Update XFEClientRequester to use dictionary-based routing - Update LoginRequestService, ReloginRequestService, GetLogRequestService, ClearLogRequestService to use [Request]/[Response] attributes - Register new diagnostic rules XFE0007-XFE0010 Agent-Logs-Url: https://github.com/XFEstudio/XFEExtension.NetCore.ServerInteractive/sessions/436def84-338f-4a12-8e86-51ed7035c2e6 Co-authored-by: XFEstudio <132526994+XFEstudio@users.noreply.github.com>
e95fa46
代码差异
@@ -8,4 +8,8 @@ Rule ID | Category | Severity | Notes
XFE0003 | XFEServerInteractive | Error | EntryPointGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0003)
XFE0004 | XFEServerInteractive | Error | EntryPointGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0004)
XFE0005 | XFEServerInteractive | Error | EntryPointGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0005)
XFE0006 | XFEServerInteractive | Error | EntryPointGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0006)
XFE0006 | XFEServerInteractive | Error | EntryPointGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0006)
XFE0007 | XFEServerInteractive | Error | ClientRequestGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0007)
XFE0008 | XFEServerInteractive | Error | ClientRequestGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0008)
XFE0009 | XFEServerInteractive | Error | ClientRequestGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0009)
XFE0010 | XFEServerInteractive | Error | ClientRequestGenerator, [Documentation](https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0010)
@@ -0,0 +1,316 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using XFEExtension.NetCore.ServerInteractive.SourceGenerator.Models;
namespace XFEExtension.NetCore.ServerInteractive.SourceGenerator;
/// <summary>
/// 客户端请求增量生成器
/// 用于自动生成StandardRequestServiceBase的请求/响应字典
/// </summary>
[Generator]
public class ClientRequestGenerator : IIncrementalGenerator
{
private static readonly DiagnosticDescriptor NonPartialClassRule = new(
id: "XFE0007",
title: "包含Request或Response方法的类必须为partial",
messageFormat: "类'{0}'必须声明为partial以便增量生成器可以生成请求/响应代码",
category: "XFEServerInteractive",
defaultSeverity: DiagnosticSeverity.Error,
helpLinkUri: "https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0007",
isEnabledByDefault: true);
private static readonly DiagnosticDescriptor MethodMustBeParameterlessRule = new(
id: "XFE0008",
title: "Request/Response方法不能有参数",
messageFormat: "方法'{0}'标记了[{1}]但包含参数,请求/响应方法必须是无参数的",
category: "XFEServerInteractive",
defaultSeverity: DiagnosticSeverity.Error,
helpLinkUri: "https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0008",
isEnabledByDefault: true);
private static readonly DiagnosticDescriptor InvalidReturnTypeRule = new(
id: "XFE0009",
title: "Request/Response方法返回类型无效",
messageFormat: "方法'{0}'的返回类型'{1}'无效,请求/响应方法必须返回object",
category: "XFEServerInteractive",
defaultSeverity: DiagnosticSeverity.Error,
helpLinkUri: "https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0009",
isEnabledByDefault: true);
private static readonly DiagnosticDescriptor InvalidPathCharactersRule = new(
id: "XFE0010",
title: "Request/Response路径包含无效字符",
messageFormat: "路径'{0}'包含无效字符(引号或反斜杠),这些字符不允许在路径中使用",
category: "XFEServerInteractive",
defaultSeverity: DiagnosticSeverity.Error,
helpLinkUri: "https://docs.xfegzs.com/View/Errors%2FServerInteractive%2FXFE0010",
isEnabledByDefault: true);
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 找到所有标记了RequestAttribute或ResponseAttribute的方法
var methodDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => IsCandidateMethod(s),
transform: static (ctx, _) => GetMethodForGeneration(ctx))
.Where(static m => m is not null);
// 按类分组
var compilationAndMethods = context.CompilationProvider.Combine(methodDeclarations.Collect());
// 生成源代码
context.RegisterSourceOutput(compilationAndMethods,
static (spc, source) => Execute(source.Left, source.Right, spc));
}
private static bool IsCandidateMethod(SyntaxNode node) => node is MethodDeclarationSyntax { AttributeLists.Count: > 0 };
private static ClientRequestMethodCandidate? GetMethodForGeneration(GeneratorSyntaxContext context)
{
var methodDeclaration = (MethodDeclarationSyntax)context.Node;
var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclaration);
if (methodSymbol is null)
return null;
// 检查是否有RequestAttribute或ResponseAttribute
var requestAttribute = methodSymbol.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.Name == "RequestAttribute");
var responseAttribute = methodSymbol.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.Name == "ResponseAttribute");
if (requestAttribute is null && responseAttribute is null)
return null;
var isRequest = requestAttribute is not null;
var attribute = isRequest ? requestAttribute! : responseAttribute!;
var attributeName = isRequest ? "Request" : "Response";
// 获取Path参数
var path = attribute.ConstructorArguments.FirstOrDefault().Value?.ToString();
if (string.IsNullOrEmpty(path))
return null;
// 检查返回类型:必须为object
var returnType = methodSymbol.ReturnType;
var isObjectReturn = returnType.SpecialType == SpecialType.System_Object;
// 检查包含类型是否为partial
var classDeclaration = methodDeclaration.Parent as ClassDeclarationSyntax;
var isContainingTypePartial = classDeclaration?.Modifiers.Any(SyntaxKind.PartialKeyword) ?? false;
// 获取泛型类型参数和约束
var typeParameters = classDeclaration?.TypeParameterList?.ToString().Trim() ?? "";
var typeConstraints = classDeclaration?.ConstraintClauses.ToString().Trim() ?? "";
var containingType = methodSymbol.ContainingType;
// 获取位置信息
var methodLocation = LocationInfo.From(methodDeclaration.GetLocation());
var classLocation = classDeclaration is not null
? LocationInfo.From(classDeclaration.Identifier.GetLocation())
: methodLocation;
// 收集源文件的using指令
var compilationUnit = methodDeclaration.SyntaxTree.GetRoot() as CompilationUnitSyntax;
var usingDirectives = compilationUnit?.Usings
.Select(u => u.ToString().Trim())
.ToArray() ?? new string[0];
return new ClientRequestMethodCandidate(
containingType.ContainingNamespace.ToDisplayString(),
containingType.Name,
methodSymbol.Name,
path!,
isRequest,
isContainingTypePartial,
methodSymbol.Parameters.Length,
isObjectReturn,
returnType.ToDisplayString(),
methodLocation,
classLocation,
typeParameters,
typeConstraints,
attributeName,
usingDirectives);
}
private static void Execute(Compilation compilation, ImmutableArray<ClientRequestMethodCandidate?> methods, SourceProductionContext context)
{
if (methods.IsDefaultOrEmpty)
return;
var validMethods = new List<ClientRequestMethodCandidate>();
// 校验并报告诊断信息
foreach (var method in methods)
{
if (method is null) continue;
var isValid = true;
// 校验:包含类型必须为partial
if (!method.IsContainingTypePartial)
{
context.ReportDiagnostic(Diagnostic.Create(
NonPartialClassRule,
method.ClassLocation.ToLocation(),
method.ClassName));
isValid = false;
}
// 校验:方法不能有参数
if (method.ParameterCount > 0)
{
context.ReportDiagnostic(Diagnostic.Create(
MethodMustBeParameterlessRule,
method.MethodLocation.ToLocation(),
method.MethodName,
method.AttributeName));
isValid = false;
}
// 校验:返回类型必须为object
if (!method.HasValidReturnType)
{
context.ReportDiagnostic(Diagnostic.Create(
InvalidReturnTypeRule,
method.MethodLocation.ToLocation(),
method.MethodName,
method.ReturnTypeName));
isValid = false;
}
// 校验:路径不能包含引号或反斜杠
if (method.Path.Contains("\"") || method.Path.Contains("\\"))
{
context.ReportDiagnostic(Diagnostic.Create(
InvalidPathCharactersRule,
method.MethodLocation.ToLocation(),
method.Path));
isValid = false;
}
if (isValid)
{
validMethods.Add(method);
}
}
if (validMethods.Count == 0)
return;
// 按类分组并生成代码
var methodsByClass = validMethods.GroupBy(m => (m.Namespace, m.ClassName, m.TypeParameters, m.TypeConstraints));
foreach (var group in methodsByClass)
{
var (namespaceName, className, typeParameters, typeConstraints) = group.Key;
var methodInfos = group.ToList();
var constraintsSuffix = string.IsNullOrEmpty(typeConstraints) ? "" : $" {typeConstraints}";
var requestMethods = methodInfos.Where(m => m.IsRequest).ToList();
var responseMethods = methodInfos.Where(m => !m.IsRequest).ToList();
// 收集所有路径(去重)
var allPaths = methodInfos.Select(m => m.Path).Distinct().ToList();
// 收集所有using指令(合并去重)
var allUsings = methodInfos
.SelectMany(m => m.UsingDirectives)
.Distinct()
.OrderBy(u => u)
.ToList();
var sourceBuilder = new StringBuilder();
sourceBuilder.AppendLine(@"// <auto-generated/>
#nullable enable
");
// 添加using指令
foreach (var usingDirective in allUsings)
{
sourceBuilder.AppendLine(usingDirective);
}
// 确保基础using存在
if (!allUsings.Any(u => u.Contains("System;") && !u.Contains("System.")))
sourceBuilder.AppendLine("using System;");
if (!allUsings.Any(u => u.Contains("System.Collections.Generic")))
sourceBuilder.AppendLine("using System.Collections.Generic;");
sourceBuilder.AppendLine($@"
namespace {namespaceName}
{{
/// <summary>
/// {className}的自动生成请求/响应字典部分类
/// </summary>
public partial class {className}{typeParameters}{constraintsSuffix}
{{
/// <summary>
/// 本类型的请求路由路径列表(覆盖基类的空列表)
/// </summary>
public new static List<string> RequestRouteList {{ get; }} = new()
{{");
// 添加所有路径到静态列表
foreach (var path in allPaths)
{
sourceBuilder.AppendLine($" \"{EscapeStringLiteral(path)}\",");
}
sourceBuilder.AppendLine($@" }};
private Dictionary<string, Func<object>>? _generatedRequestPoints;
/// <inheritdoc/>
public override Dictionary<string, Func<object>> RequestPoints
{{
get => _generatedRequestPoints ??= new Dictionary<string, Func<object>>()
{{");
// 添加请求方法
foreach (var method in requestMethods)
{
sourceBuilder.AppendLine($" {{ \"{EscapeStringLiteral(method.Path)}\", {method.MethodName} }},");
}
sourceBuilder.AppendLine($@" }};
}}
private Dictionary<string, Func<object>>? _generatedResponsePoints;
/// <inheritdoc/>
public override Dictionary<string, Func<object>> ResponsePoints
{{
get => _generatedResponsePoints ??= new Dictionary<string, Func<object>>()
{{");
// 添加响应方法
foreach (var method in responseMethods)
{
sourceBuilder.AppendLine($" {{ \"{EscapeStringLiteral(method.Path)}\", {method.MethodName} }},");
}
sourceBuilder.AppendLine(@" };
}
}
}");
context.AddSource($"{className}.ClientRequests.g.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
}
}
/// <summary>
/// 转义字符串字面量中的特殊字符
/// </summary>
private static string EscapeStringLiteral(string value)
{
return value.Replace("\\", "\\\\").Replace("\"", "\\\"");
}
}
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace XFEExtension.NetCore.ServerInteractive.SourceGenerator.Models;
/// <summary>
/// 客户端请求方法候选信息,包含验证所需的所有数据
/// </summary>
public class ClientRequestMethodCandidate(string namespaceName, string className, string methodName, string path, bool isRequest, bool isContainingTypePartial, int parameterCount, bool hasValidReturnType, string returnTypeName, LocationInfo methodLocation, LocationInfo classLocation, string typeParameters, string typeConstraints, string attributeName, string[] usingDirectives)
{
public string Namespace { get; } = namespaceName;
public string ClassName { get; } = className;
public string MethodName { get; } = methodName;
public string Path { get; } = path;
public bool IsRequest { get; } = isRequest;
public bool IsContainingTypePartial { get; } = isContainingTypePartial;
public int ParameterCount { get; } = parameterCount;
public bool HasValidReturnType { get; } = hasValidReturnType;
public string ReturnTypeName { get; } = returnTypeName;
public LocationInfo MethodLocation { get; } = methodLocation;
public LocationInfo ClassLocation { get; } = classLocation;
public string TypeParameters { get; } = typeParameters;
public string TypeConstraints { get; } = typeConstraints;
public string AttributeName { get; } = attributeName;
public string[] UsingDirectives { get; } = usingDirectives;
}
@@ -0,0 +1,22 @@
namespace XFEExtension.NetCore.ServerInteractive.Attributes;
/// <summary>
/// 请求特性,用于标记IStandardRequestService中构造请求体的方法
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class RequestAttribute : Attribute
{
/// <summary>
/// 请求路径
/// </summary>
public string Path { get; }
/// <summary>
/// 创建请求特性
/// </summary>
/// <param name="path">请求路径(例如:user/login)</param>
public RequestAttribute(string path)
{
Path = path;
}
}
@@ -0,0 +1,22 @@
namespace XFEExtension.NetCore.ServerInteractive.Attributes;
/// <summary>
/// 响应特性,用于标记IStandardRequestService中解析响应的方法
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class ResponseAttribute : Attribute
{
/// <summary>
/// 响应路径
/// </summary>
public string Path { get; }
/// <summary>
/// 创建响应特性
/// </summary>
/// <param name="path">响应路径(例如:user/login)</param>
public ResponseAttribute(string path)
{
Path = path;
}
}
@@ -6,8 +6,13 @@ namespace XFEExtension.NetCore.ServerInteractive.Implements.Requester;
/// <summary>
/// 标准请求服务基类
/// </summary>
public abstract class StandardRequestServiceBase : IStandardRequestService
public abstract partial class StandardRequestServiceBase : IStandardRequestService
{
/// <summary>
/// 本类型的请求路由路径列表(由增量生成器自动填充)
/// </summary>
public static List<string> RequestRouteList { get; } = [];
/// <inheritdoc/>
public string Session { get => XFEClientRequester.Session; set => XFEClientRequester.Session = value; }
/// <inheritdoc />
@@ -24,8 +29,8 @@ public abstract class StandardRequestServiceBase : IStandardRequestService
public XFEClientRequester XFEClientRequester { get; set; }
/// <inheritdoc/>
public abstract object AnalyzeResponse();
public virtual Dictionary<string, Func<object>> RequestPoints { get; } = new();
/// <inheritdoc/>
public abstract object PostRequest();
public virtual Dictionary<string, Func<object>> ResponsePoints { get; } = new();
}
@@ -30,13 +30,11 @@ public interface IStandardRequestService : IRequestServiceBase
/// </summary>
object[] Parameters { get; internal set; }
/// <summary>
/// 提交请求体
/// 请求方法字典,Key为请求路径,Value为对应的构造请求体方法
/// </summary>
/// <returns></returns>
object PostRequest();
Dictionary<string, Func<object>> RequestPoints { get; }
/// <summary>
/// 接收到返回后解析
/// 响应方法字典,Key为请求路径,Value为对应的解析响应方法
/// </summary>
/// <returns></returns>
object AnalyzeResponse();
Dictionary<string, Func<object>> ResponsePoints { get; }
}
@@ -1,17 +1,24 @@
using XFEExtension.NetCore.ServerInteractive.Implements.Requester;
using XFEExtension.NetCore.ServerInteractive.Attributes;
using XFEExtension.NetCore.ServerInteractive.Implements.Requester;
namespace XFEExtension.NetCore.ServerInteractive.Utilities.Requester.Services;
/// <summary>
/// 清除日志请求服务
/// </summary>
public class ClearLogRequestService : StandardRequestServiceBase
public partial class ClearLogRequestService : StandardRequestServiceBase
{
/// <inheritdoc/>
public override object AnalyzeResponse() => true;
/// <summary>
/// 解析清除日志响应
/// </summary>
[Response("log/clear")]
public object AnalyzeClearLogResponse() => true;
/// <inheritdoc/>
public override object PostRequest() => new
/// <summary>
/// 构造清除日志请求体
/// </summary>
[Request("log/clear")]
public object PostClearLogRequest() => new
{
session = Session,
deviceInfo = DeviceInfo
@@ -1,17 +1,24 @@
using XFEExtension.NetCore.ServerInteractive.Implements.Requester;
using XFEExtension.NetCore.ServerInteractive.Attributes;
using XFEExtension.NetCore.ServerInteractive.Implements.Requester;
namespace XFEExtension.NetCore.ServerInteractive.Utilities.Requester.Services;
/// <summary>
/// 获取日志请求服务
/// </summary>
public class GetLogRequestService : StandardRequestServiceBase
public partial class GetLogRequestService : StandardRequestServiceBase
{
/// <inheritdoc/>
public override object AnalyzeResponse() => Response;
/// <summary>
/// 解析获取日志响应
/// </summary>
[Response("log/get")]
public object AnalyzeGetLogResponse() => Response;
/// <inheritdoc/>
public override object PostRequest() => new
/// <summary>
/// 构造获取日志请求体
/// </summary>
[Request("log/get")]
public object PostGetLogRequest() => new
{
session = Session,
deviceInfo = DeviceInfo,
@@ -1,4 +1,5 @@
using System.Text.Json;
using XFEExtension.NetCore.ServerInteractive.Attributes;
using XFEExtension.NetCore.ServerInteractive.Implements.Requester;
using XFEExtension.NetCore.ServerInteractive.Interfaces;
using XFEExtension.NetCore.ServerInteractive.Models.RequesterModels;
@@ -9,7 +10,7 @@ namespace XFEExtension.NetCore.ServerInteractive.Utilities.Requester.Services;
/// <summary>
/// 登录请求服务
/// </summary>
public class LoginRequestService<T> : StandardRequestServiceBase where T : IUserFaceInfo
public partial class LoginRequestService<T> : StandardRequestServiceBase where T : IUserFaceInfo
{
private readonly JsonSerializerOptions _jsonSerializerOptions = new();
@@ -18,16 +19,22 @@ public class LoginRequestService<T> : StandardRequestServiceBase where T : IUser
/// </summary>
public LoginRequestService() => _jsonSerializerOptions.Converters.Add(new JsonDateTimeConverter());
/// <inheritdoc/>
public override object AnalyzeResponse()
/// <summary>
/// 解析登录响应
/// </summary>
[Response("user/login")]
public object AnalyzeLoginResponse()
{
var result = JsonSerializer.Deserialize<UserLoginResult<T>>(Response, _jsonSerializerOptions);
Session = result!.Session;
return result;
}
/// <inheritdoc/>
public override object PostRequest() => new
/// <summary>
/// 构造登录请求体
/// </summary>
[Request("user/login")]
public object PostLoginRequest() => new
{
deviceInfo = DeviceInfo,
account = Parameters[0],
@@ -1,4 +1,5 @@
using System.Text.Json;
using XFEExtension.NetCore.ServerInteractive.Attributes;
using XFEExtension.NetCore.ServerInteractive.Implements.Requester;
using XFEExtension.NetCore.ServerInteractive.Interfaces;
using XFEExtension.NetCore.ServerInteractive.Utilities.JsonConverter;
@@ -8,7 +9,7 @@ namespace XFEExtension.NetCore.ServerInteractive.Utilities.Requester.Services;
/// <summary>
/// 登录校验请求服务
/// </summary>
public class ReloginRequestService<T> : StandardRequestServiceBase where T : IUserFaceInfo
public partial class ReloginRequestService<T> : StandardRequestServiceBase where T : IUserFaceInfo
{
private readonly JsonSerializerOptions _jsonSerializerOptions = new();
@@ -16,11 +17,18 @@ public class ReloginRequestService<T> : StandardRequestServiceBase where T : IUs
/// 登录校验请求服务
/// </summary>
public ReloginRequestService() => _jsonSerializerOptions.Converters.Add(new JsonDateTimeConverter());
/// <inheritdoc/>
public override object AnalyzeResponse() => JsonSerializer.Deserialize<T>(Response, _jsonSerializerOptions)!;
/// <inheritdoc/>
public override object PostRequest() => new
/// <summary>
/// 解析登录校验响应
/// </summary>
[Response("user/relogin")]
public object AnalyzeReloginResponse() => JsonSerializer.Deserialize<T>(Response, _jsonSerializerOptions)!;
/// <summary>
/// 构造登录校验请求体
/// </summary>
[Request("user/relogin")]
public object PostReloginRequest() => new
{
session = Session,
deviceInfo = DeviceInfo,
@@ -74,16 +74,21 @@ public abstract class XFEClientRequester : IRequesterBase
xFEService.Route = serviceName;
xFEService.DeviceInfo = DeviceInfo;
xFEService.Parameters = parameters;
var (response, code) = await InteractiveHelper.GetServerResponse(RequestAddress + $"/{serviceName}", xFEService.PostRequest(), _jsonSerializerOptions);
if (!xFEService.RequestPoints.TryGetValue(serviceName, out var requestHandler))
throw new XFERequesterException($"未找到路由'{serviceName}'对应的[Request]方法");
var (response, code) = await InteractiveHelper.GetServerResponse(RequestAddress + $"/{serviceName}", requestHandler(), _jsonSerializerOptions);
result.StatusCode = code;
if (code == HttpStatusCode.OK)
{
xFEService.Response = response;
xFEService.UnescapedResponse = Regex.Unescape(response);
var requestResult = xFEService.AnalyzeResponse();
if (xFEService.ResponsePoints.TryGetValue(serviceName, out var responseHandler))
{
var requestResult = responseHandler();
result.Result = requestResult;
}
MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
result.Result = requestResult;
}
else
{
@@ -114,16 +119,21 @@ public abstract class XFEClientRequester : IRequesterBase
instance.Route = serviceName;
instance.DeviceInfo = DeviceInfo;
instance.Parameters = parameters;
var (response, code) = await InteractiveHelper.GetServerResponse(RequestAddress + $"/{serviceName}", instance.PostRequest(), _jsonSerializerOptions);
if (!instance.RequestPoints.TryGetValue(serviceName, out var requestHandler))
throw new XFERequesterException($"未找到路由'{serviceName}'对应的[Request]方法");
var (response, code) = await InteractiveHelper.GetServerResponse(RequestAddress + $"/{serviceName}", requestHandler(), _jsonSerializerOptions);
result.StatusCode = code;
if (code == HttpStatusCode.OK)
{
instance.Response = response;
instance.UnescapedResponse = Regex.Unescape(response);
var requestResult = instance.AnalyzeResponse();
if (instance.ResponsePoints.TryGetValue(serviceName, out var responseHandler))
{
var requestResult = responseHandler();
result.Result = requestResult;
}
MessageReceived?.Invoke(this, new ServerInteractiveEventArgsImpl("Success", code));
result.Message = "Success";
result.Result = requestResult;
}
else
{