[feat] ApiServer框架支持异步方法,支持返回XCode实体类,优先采取IAccessor二进制序列化,比Json序列化更快更节省空间。close: https://github.com/NewLifeX/NewLife.Remoting/issues/5 。 close: https://github.com/NewLifeX/X/issues/171大石头 authored at 2025-12-13 09:57:20
diff --git a/NewLife.Remoting/ApiAction.cs b/NewLife.Remoting/ApiAction.cs
index e6ecf82..61da6e0 100644
--- a/NewLife.Remoting/ApiAction.cs
+++ b/NewLife.Remoting/ApiAction.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using System.Threading.Tasks;
using NewLife.Data;
using NewLife.Log;
using NewLife.Reflection;
@@ -66,8 +67,12 @@ public class ApiAction : IExtend
if (ps[0].ParameterType.As<IAccessor>()) IsAccessorParameter = true;
}
- if (method.ReturnType.As<IPacket>()) IsPacketReturn = true;
- if (method.ReturnType.As<IAccessor>()) IsAccessorReturn = true;
+ var returnType = method.ReturnType;
+ if (returnType.As(typeof(Task<>)))
+ returnType = returnType.GetGenericArguments()[0];
+
+ if (returnType.As<IPacket>()) IsPacketReturn = true;
+ if (returnType.As<IAccessor>()) IsAccessorReturn = true;
}
/// <summary>获取名称</summary>
@@ -99,16 +104,16 @@ public class ApiAction : IExtend
{
var mi = Method;
- var type = mi.ReturnType;
- var rtype = type.Name;
- if (type.As<Task>())
+ var returnType = mi.ReturnType;
+ var rtype = returnType.Name;
+ if (returnType.As<Task>())
{
- if (!type.IsGenericType)
+ if (!returnType.IsGenericType)
rtype = "void";
else
{
- type = type.GetGenericArguments()[0];
- rtype = type.Name;
+ returnType = returnType.GetGenericArguments()[0];
+ rtype = returnType.Name;
}
}
diff --git a/NewLife.Remoting/ApiClient.cs b/NewLife.Remoting/ApiClient.cs
index ca1747b..4113d99 100644
--- a/NewLife.Remoting/ApiClient.cs
+++ b/NewLife.Remoting/ApiClient.cs
@@ -4,6 +4,7 @@ using NewLife.Log;
using NewLife.Messaging;
using NewLife.Model;
using NewLife.Net;
+using NewLife.Reflection;
using NewLife.Remoting.Http;
using NewLife.Serialization;
using NewLife.Threading;
@@ -378,6 +379,14 @@ public class ApiClient : ApiHost, IApiClient
return (TResult)(Object)new Packet(message.Data.ToArray());
}
+ // 二进制序列化
+ if (resultType.As<IAccessor>())
+ {
+ var result = ServiceProvider?.GetService(resultType) ?? resultType.CreateInstance();
+ if (result is IAccessor accessor && accessor.Read(message.Data.GetStream(), message))
+ return (TResult)result;
+ }
+
try
{
// 解码结果
diff --git a/NewLife.Remoting/IApiHandler.cs b/NewLife.Remoting/IApiHandler.cs
index 1bf7836..f8b8316 100644
--- a/NewLife.Remoting/IApiHandler.cs
+++ b/NewLife.Remoting/IApiHandler.cs
@@ -102,6 +102,8 @@ public class ApiHandler : IApiHandler
rs = controller.InvokeWithParams(api.Method, ctx.ActionParameters as IDictionary);
}
+ if (rs is Task task) rs = GetTaskResult(task);
+
ctx.Result = rs;
}
@@ -139,6 +141,17 @@ public class ApiHandler : IApiHandler
return rs;
}
+ private static Object? GetTaskResult(Task task)
+ {
+ task.GetAwaiter().GetResult();
+
+ var taskType = task.GetType();
+ if (!taskType.IsGenericType) return null;
+
+ var resultProperty = taskType.GetProperty("Result", BindingFlags.Public | BindingFlags.Instance);
+ return resultProperty?.GetValue(task);
+ }
+
/// <summary>准备上下文,可以借助Token重写Session会话集合</summary>
/// <param name="session">Api会话</param>
/// <param name="action">接口名</param>
diff --git a/Samples/Zero.RpcServer/ClientTest.cs b/Samples/Zero.RpcServer/ClientTest.cs
index 587d204..4c19886 100644
--- a/Samples/Zero.RpcServer/ClientTest.cs
+++ b/Samples/Zero.RpcServer/ClientTest.cs
@@ -2,6 +2,7 @@
using NewLife.Remoting;
using NewLife.Security;
using NewLife.Serialization;
+using XCode.Membership;
namespace Zero.RpcServer;
@@ -82,6 +83,11 @@ static class ClientTest
var state2 = Rand.NextString(8);
var infs = await client.InvokeAsync<IDictionary<String, Object>>("api/info", new { state, state2 });
client.WriteLog("服务端信息:{0}", infs.ToJson(true));
+
+ // 获取用户信息
+ client.WriteLog("获取用户信息");
+ var user = await client.InvokeAsync<User>("user/findbyid", new { id = 1 });
+ client.WriteLog("用户信息:{0}", user.ToJson(true, false, false));
}
catch (Exception ex)
{
diff --git a/Samples/Zero.RpcServer/UserController.cs b/Samples/Zero.RpcServer/UserController.cs
index afa97f2..1760503 100644
--- a/Samples/Zero.RpcServer/UserController.cs
+++ b/Samples/Zero.RpcServer/UserController.cs
@@ -13,7 +13,7 @@ internal class UserController : IApi, IActionFilter
public IApiSession Session { get; set; }
[Api(nameof(FindByID))]
- public User FindByID(Int32 id)
+ public async Task<User> FindByID(Int32 id)
{
// Session 用法同Web
var times = Session["Times"].ToInt();
@@ -29,6 +29,9 @@ internal class UserController : IApi, IActionFilter
throw new ApiException(ApiCode.TooManyRequests, $"[{ctx.ActionName}]调用次数过多!Times={times}");
}
+ // 模拟异步操作
+ await Task.Delay(100);
+
return User.FindByID(id);
}