[fix]修正UdpServer在接收广播时连续启动接收的错误,在StarAgent中,此时可能收到广播包,SocketFlags是Broadcast,需要清空,否则报错“参考的对象类型不支持尝试的操作”; 无需设置SocketOptionName.PacketInformation,在ReceiveMessageFromAsync时会自动设置,并且支持ipv6;
石头 编写于 2024-10-10 00:36:00 石头 提交于 2024-10-10 00:45:43
X
using System;
using System.Collections.Generic;
using System.Reflection;
using NewLife.Model;
using NewLife.Reflection;

namespace NewLife.Remoting
{
    /// <summary>接口管理器</summary>
    public interface IApiManager
    {
        /// <summary>可提供服务的方法</summary>
        IDictionary<String, ApiAction> Services { get; }

        /// <summary>注册服务提供类。该类的所有公开方法将直接暴露</summary>
        /// <typeparam name="TService"></typeparam>
        void Register<TService>();

        /// <summary>注册服务</summary>
        /// <param name="controller">控制器对象</param>
        /// <param name="method">动作名称。为空时遍历控制器所有公有成员方法</param>
        void Register(Object controller, String method);

        /// <summary>查找服务</summary>
        /// <param name="action"></param>
        /// <returns></returns>
        ApiAction Find(String action);
    }

    class ApiManager : IApiManager
    {
        private readonly ApiServer _server;

        /// <summary>可提供服务的方法</summary>
        public IDictionary<String, ApiAction> Services { get; } = new Dictionary<String, ApiAction>(StringComparer.OrdinalIgnoreCase);

        public ApiManager(ApiServer server) => _server = server;

        private void RegisterAll(Object controller, Type type)
        {
            // 找到容器,注册控制器
            var container = _server?.ServiceProvider?.GetService<IObjectContainer>();
            if (container != null)
            {
                if (controller == null)
                    container.AddTransient(type, type);
                else
                    container.AddSingleton(type, controller);
            }

            // 是否要求Api特性
            var requireApi = type.GetCustomAttribute<ApiAttribute>() != null;

            var flag = BindingFlags.Public | BindingFlags.Instance;
            // 如果要求Api特性,则还需要遍历私有方法和静态方法
            if (requireApi) flag |= BindingFlags.NonPublic | BindingFlags.Static;
            foreach (var mi in type.GetMethods(flag))
            {
                if (mi.IsSpecialName) continue;
                if (mi.DeclaringType == typeof(Object)) continue;
                if (requireApi && mi.GetCustomAttribute<ApiAttribute>() == null) continue;

                var act = new ApiAction(mi, type)
                {
                    Controller = controller
                };

                Services[act.Name] = act;
            }
        }

        /// <summary>注册服务提供类。该类的所有公开方法将直接暴露</summary>
        /// <typeparam name="TService"></typeparam>
        public void Register<TService>() => RegisterAll(null, typeof(TService));

        /// <summary>注册服务</summary>
        /// <param name="controller">控制器对象</param>
        /// <param name="method">动作名称。为空时遍历控制器所有公有成员方法</param>
        public void Register(Object controller, String method)
        {
            if (controller == null) throw new ArgumentNullException(nameof(controller));

            var type = controller is Type ? controller as Type : controller.GetType();

            if (!method.IsNullOrEmpty())
            {
                var mi = type.GetMethodEx(method);
                var act = new ApiAction(mi, type)
                {
                    Controller = controller
                };

                Services[act.Name] = act;
            }
            else
            {
                RegisterAll(controller, type);
            }
        }

        /// <summary>查找服务</summary>
        /// <param name="action"></param>
        /// <returns></returns>
        public ApiAction Find(String action)
        {
            if (Services.TryGetValue(action, out var mi)) return mi;

            // 局部模糊匹配
            if (action.Contains('/'))
            {
                var ctrl = action.Substring(null, "/");
                if (Services.TryGetValue(ctrl + "/*", out mi)) return mi;
            }

            // 全局模糊匹配
            if (Services.TryGetValue("*", out mi)) return mi;

            return null;
        }
    }
}