NewLife/NewLife.Remoting

[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
a3cc9e5
Tree
1 Parent(s) 86631c2
Summary: 5 changed files with 45 additions and 9 deletions.
Modified +13 -8
Modified +9 -0
Modified +13 -0
Modified +6 -0
Modified +4 -1
Modified +13 -8
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;
             }
         }
 
Modified +9 -0
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
             {
                 // 解码结果
Modified +13 -0
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>
Modified +6 -0
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)
         {
Modified +4 -1
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);
     }