• asp.net core mvc 之 DynamicApi


      这段时间闲赋在家,感觉手痒,故想折腾一些东西.

      由于之前移植了一个c#版本的spring cloud feign客户端(https://github.com/daixinkai/feign.net),所以想弄个配套的服务端动态接口,实现服务即接口的功能.虽然ABP框架内部包含一个功能强大的DynamicWebApi,但是我只是想要一个独立简单的组件,用来实现以下效果:

      有一个业务服务 : 

       public interface ITestService
        {
            Task<string> GetName(int id);
        }

    自动生成类似以下的接口

        [Route("api/test")]
        public class TestController : ControllerBase
        {
            public TestController(ITestService testService)
            {
                _testService = testService;
            }
    
            ITestService _testService;
    
            [HttpGet("name/{id}")]
            public Task<string> GetName(int id)
            {
                return _testService.GetName(id);
            }
    
        }

    项目地址 : https://github.com/daixinkai/Microsoft.AspNetCore.Mvc.DynamicApi

    -------------------------------------------------------------------------------------------------

    首先定义一个DynamicApiAttribute,替代RouteAttribute的功能

        [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
        public class DynamicApiAttribute : Attribute, Microsoft.AspNetCore.Mvc.Routing.IRouteTemplateProvider
        {
            public DynamicApiAttribute() { }
            public DynamicApiAttribute(string template)
            {
                Template = template;
            }
            public string Template { get; set; }public int? Order { get; set; }
            public string Name { get; set; }
        }

    思路就是查找标记了DynamicApiAttribute特性的接口,生成一个代理类型注册为控制器

    1. BuildProxyType: 定义一个 TypeBuilder

    先生成一个类型为当前接口类型的字段 : 

    FieldBuilder interfaceInstanceFieldBuilder = typeBuilder.DefineField("_interfaceInstance", interfaceType, FieldAttributes.Private);

    生成构造函数,接收一个当前接口类型的对象,并赋值给上述字段

         ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
                    MethodAttributes.Public,
                    CallingConventions.Standard,
                    new Type[] { interfaceType });
                ILGenerator constructorIlGenerator = constructorBuilder.GetILGenerator();
                constructorIlGenerator.Emit(OpCodes.Ldarg_0);
                constructorIlGenerator.Emit(OpCodes.Ldarg_1);
                constructorIlGenerator.Emit(OpCodes.Stfld, interfaceInstanceFieldBuilder);
                constructorIlGenerator.Emit(OpCodes.Ret);

    查找接口的所有方法,全部生成

      foreach (var method in interfaceType.GetMethodsIncludingBaseInterfaces())
                {
                    BuildMethod(typeBuilder, interfaceType, method, interfaceInstanceFieldBuilder);
                }
            static void BuildMethod(TypeBuilder typeBuilder, Type interfaceType, MethodInfo method, FieldBuilder interfaceInstanceFieldBuilder)
            {
                MethodAttributes methodAttributes =
                        MethodAttributes.Public
                        | MethodAttributes.HideBySig
                        | MethodAttributes.NewSlot
                        | MethodAttributes.Virtual
                        | MethodAttributes.Final;
                var parameters = method.GetParameters();
                Type[] parameterTypes = parameters.Select(s => s.ParameterType).ToArray();
                MethodBuilder methodBuilder = typeBuilder.DefineMethod(method.Name, methodAttributes, CallingConventions.Standard, method.ReturnType, parameterTypes);
    
                #region parameterName
    
                for (int i = 0; i < parameters.Length; i++)
                {
                    methodBuilder.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].Name);
                }
    
                #endregion
    
                typeBuilder.DefineMethodOverride(methodBuilder, method);
                ILGenerator iLGenerator = methodBuilder.GetILGenerator();
                iLGenerator.Emit(OpCodes.Ldarg_0); // this
                iLGenerator.Emit(OpCodes.Ldfld, interfaceInstanceFieldBuilder);
                for (int i = 0; i < parameterTypes.Length; i++)
                {
                    iLGenerator.Emit(OpCodes.Ldarg_S, i + 1);
                }
                iLGenerator.Emit(OpCodes.Call, method);
                iLGenerator.Emit(OpCodes.Ret);
                var datas = CustomAttributeData.GetCustomAttributes(method);
                foreach (var data in datas)
                {
                    CustomAttributeBuilder customAttributeBuilder = new CustomAttributeBuilder(data.Constructor, data.ConstructorArguments.Select(s => s.Value).ToArray());
                    methodBuilder.SetCustomAttribute(customAttributeBuilder);
                }
            }

    最后别忘了复制特性 

          var datas = CustomAttributeData.GetCustomAttributes(interfaceType);
                foreach (var data in datas)
                {
                    CustomAttributeBuilder customAttributeBuilder = new CustomAttributeBuilder(data.Constructor, data.ConstructorArguments.Select(s => s.Value).ToArray());
                    typeBuilder.SetCustomAttribute(customAttributeBuilder);
                }

    这样代理类型就生成完毕了

    2.注册到Mvc框架中

    由于默认ControllerFeatureProvider不支持生成的代理类型,需要自定义实现

        public class DynamicApiControllerFeatureProvider : ControllerFeatureProvider
        {
            protected override bool IsController(TypeInfo typeInfo)
            {
                return typeInfo.IsProxyApi();
            }
        }
           public static IMvcBuilder AddDynamicApi(this IMvcBuilder builder)
            {
    
                var feature = new ControllerFeature();
    
                foreach (AssemblyPart assemblyPart in builder.PartManager.ApplicationParts.OfType<AssemblyPart>())
                {
                    foreach (var type in assemblyPart.Types)
                    {
                        if (type.IsInterface && type.IsDefinedIncludingBaseInterfaces<DynamicApiAttribute>() && !type.IsDefined(typeof(NonDynamicApiAttribute)) && !type.IsGenericType)
                        {
                            feature.Controllers.Add(DynamicApiProxy.GetProxyType(type));//feature.Controllers.Add没什么卵用
                        }
                    }
                }
    
                builder.AddApplicationPart(DynamicApiProxy.DynamicAssembly.AssemblyBuilder);
    
                builder.PartManager.FeatureProviders.Add(new DynamicApiControllerFeatureProvider());
    
                
                return builder;
            }

    这样就完成了,是不是很简单实用! 另外DynamicApi支持Mvc内置的Filter

    3. 测试一下 

        [DynamicApi("api/testService")]
        public interface ITestService
        {
            //[Microsoft.AspNetCore.Authorization.Authorize]
            [HttpGet("name/{id}")]
            Task<string> GetName(int id);
        }
    
        public class TestService : ITestService
        {
            public Task<string> GetName(int id)
            {
                return Task.FromResult("Name" + id);
            }
        }

    最后别忘了注入服务

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddDynamicApi();
                services.AddTransient<ITestService, TestService>();
            }

    大功告成

  • 相关阅读:
    Springboot+mybatis-plus+mysql+clickhouse集成多数据源
    对集合里每个元素是一个对象,按照对象某一个属性值给这个集合排序
    vue的a-tree-select选择父节点回显的是子节点
    Es简单条件查询
    使用Ant Desigen在vue里面实现分页以及表头的模糊查询
    搭建第一个vue项目
    Address localhost:1099 is already in use
    spring的控制反转DI---基于注解实现
    mybatis下的ResultMap配置一对一以及一对多
    mybatis入门
  • 原文地址:https://www.cnblogs.com/pokemon/p/11596941.html
Copyright © 2020-2023  润新知