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

由于之前移植了一个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 = ; i < parameters.Length; i++)
{
methodBuilder.DefineParameter(i + , 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 = ; i < parameterTypes.Length; i++)
{
iLGenerator.Emit(OpCodes.Ldarg_S, i + );
}
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>();
}

大功告成

asp.net core mvc 之 DynamicApi的更多相关文章

  1. ASP.NET Core MVC/WebAPi 模型绑定探索

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  2. ASP.NET Core MVC 配置全局路由前缀

    前言 大家好,今天给大家介绍一个 ASP.NET Core MVC 的一个新特性,给全局路由添加统一前缀.严格说其实不算是新特性,不过是Core MVC特有的. 应用背景 不知道大家在做 Web Ap ...

  3. ASP.NET Core MVC 中的 [Controller] 和 [NonController]

    前言 我们知道,在 MVC 应用程序中,有一部分约定的内容.其中关于 Controller 的约定是这样的. 每个 Controller 类的名字以 Controller 结尾,并且放置在 Contr ...

  4. ASP.NET Core 中文文档 第二章 指南(2)用 Visual Studio 和 ASP.NET Core MVC 创建首个 Web API

    原文:Building Your First Web API with ASP.NET Core MVC and Visual Studio 作者:Mike Wasson 和 Rick Anderso ...

  5. ASP.NET Core 中文文档 第二章 指南(4.1)ASP.NET Core MVC 与 Visual Studio 入门

    原文:Getting started with ASP.NET Core MVC and Visual Studio 作者:Rick Anderson 翻译:娄宇(Lyrics) 校对:刘怡(Alex ...

  6. ASP.NET Core 中文文档 第四章 MVC(01)ASP.NET Core MVC 概览

    原文:Overview of ASP.NET Core MVC 作者:Steve Smith 翻译:张海龙(jiechen) 校对:高嵩 ASP.NET Core MVC 是使用模型-视图-控制器(M ...

  7. ASP.NET Core MVC TagHelper实践HighchartsNET快速图表控件-开源

    ASP.NET Core MVC TagHelper最佳实践HighchartsNET快速图表控件支持ASP.NET Core. 曾经在WebForms上写过 HighchartsNET快速图表控件- ...

  8. ASP.NET Core MVC 在linux上的创建及发布

    前言 ASP.NET core转眼都发布半月多了,社区最近也是非常活跃,虽然最近从事python工作,但也一直对.NET念念不忘,看过了园区大神们搭建的Asp.net core项目之后,自己也是跃跃欲 ...

  9. ASP.NET Core - ASP.NET Core MVC 的功能划分

    概述 大型 Web 应用比小型 Web 应用需要更好的组织.在大型应用中,ASP.NET MVC(和 Core MVC)所用的默认组织结构开始成为你的负累.你可以使用两种简单的技术来更新组织方法并及时 ...

随机推荐

  1. python所有的标准异常类:

    异常名称 描述 BaseException 所有异常的基类 SystemExit 解释器请求退出 KeyboardInterrupt 用户中断执行(通常是输入^C) Exception 常规错误的基类 ...

  2. 【乘风破浪】Android系统启动流程整理

    前言 对于一个Android应用层开发者来说,了解Android系统的启动流程对理解Android系统有很大的帮助.这其中包含了大量的细节,而且前面很多步骤包含了C/C++实现的native层逻辑,作 ...

  3. 关于ionic app $http.get()无法请求,导致页面没有数据的问题

    ionic app 打包后在真机上运行,无法用正常使用http.get(),这种情况被称为“白名单”,解决方法:切换到项目根目录,执行命令:cordova plugin add cordova-plu ...

  4. spring boot整合mybatis框架及增删改查(jsp视图)

    工具:idea.SQLyog 版本:springboot1.5.9版本.mysql5.1.62 第一步:新建项目 第二步:整合依赖(pom.xml) <dependencies> < ...

  5. Servlet 常用API学习(一)

    Servlet常用API学习 一.Servlet体系结构(图片来自百度图片) 二.ServletConfig接口 Servlet在有些情况下可能需要访问Servlet容器或借助Servlet容器访问外 ...

  6. mysql 实战

    建表语句: CREATE TABLE employee ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(150) NOT NULL DEFAULT ...

  7. Linux搭建nginx负载均衡(两台服务器之间)

    负载均衡种类 第一种:通过硬件负载解决,常见的有NetScaler.F5.Radware和Array等商用的负载均衡器,价格比较昂贵 第二种:通过软件负载解决,常见的软件有LVS.Nginx.apac ...

  8. unicode的编码与解码

  9. Timus-1005. Stone Pile-01背包

    传送门:http://acm.timus.ru/problem.aspx?space=1&num=1005 参考:https://www.cnblogs.com/yinzm/p/6629222 ...

  10. CF - 1131 D Gourmet choice

    题目传送门 先把 = 的人用并查集合并在一起. 然后 < > 的建边, 跑一遍 toposort 之后就好了. 入度为0点的值肯定为1, 然后就是因为这个是按照时间线走过来的,所以一个点的 ...