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 = ; 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的更多相关文章
- ASP.NET Core MVC/WebAPi 模型绑定探索
前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...
- ASP.NET Core MVC 配置全局路由前缀
前言 大家好,今天给大家介绍一个 ASP.NET Core MVC 的一个新特性,给全局路由添加统一前缀.严格说其实不算是新特性,不过是Core MVC特有的. 应用背景 不知道大家在做 Web Ap ...
- ASP.NET Core MVC 中的 [Controller] 和 [NonController]
前言 我们知道,在 MVC 应用程序中,有一部分约定的内容.其中关于 Controller 的约定是这样的. 每个 Controller 类的名字以 Controller 结尾,并且放置在 Contr ...
- 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 ...
- 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 ...
- ASP.NET Core 中文文档 第四章 MVC(01)ASP.NET Core MVC 概览
原文:Overview of ASP.NET Core MVC 作者:Steve Smith 翻译:张海龙(jiechen) 校对:高嵩 ASP.NET Core MVC 是使用模型-视图-控制器(M ...
- ASP.NET Core MVC TagHelper实践HighchartsNET快速图表控件-开源
ASP.NET Core MVC TagHelper最佳实践HighchartsNET快速图表控件支持ASP.NET Core. 曾经在WebForms上写过 HighchartsNET快速图表控件- ...
- ASP.NET Core MVC 在linux上的创建及发布
前言 ASP.NET core转眼都发布半月多了,社区最近也是非常活跃,虽然最近从事python工作,但也一直对.NET念念不忘,看过了园区大神们搭建的Asp.net core项目之后,自己也是跃跃欲 ...
- ASP.NET Core - ASP.NET Core MVC 的功能划分
概述 大型 Web 应用比小型 Web 应用需要更好的组织.在大型应用中,ASP.NET MVC(和 Core MVC)所用的默认组织结构开始成为你的负累.你可以使用两种简单的技术来更新组织方法并及时 ...
随机推荐
- freemarker导出复杂样式的Excel
freemarker导出复杂样式的Excel 代码地址: gitee https://gitee.com/suveng/demo/tree/master/chapter.002 代码存放于demo下面 ...
- node.js 初学 自我笔记整理 day01
node.js 概念问题: Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. npm是一个node的包管理工具 ,也是一个网站 ,还是一条命令.N ...
- Leetcode 5. Longest Palindromic Substring(最长回文子串, Manacher算法)
Leetcode 5. Longest Palindromic Substring(最长回文子串, Manacher算法) Given a string s, find the longest pal ...
- python大纲+变量基础详解
1.python的历史 python2和python3的区别 2004 Django框架的诞生 2.python是编程语言 python 是解释型语言 3.python的种类 cpyt ...
- HDU - 5128The E-pang Palace+暴力枚举,计算几何
第一次写计算几何,ac,感动. 不过感觉自己的代码还可以美化一下. 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5128 题意: 在一个坐标系中,有n个 ...
- CF - 1108 F MST Unification
题目传送门 题意:在一幅图中, 问需要使得多少条边加一,使得最小生成树只有一种方案. 题解:Kruskal, sort完之后,对于相通的一个边权w,我们可以分析出来有多少边是可以被放到图里面的,然后我 ...
- POJ 3786 dp-递推 Adjacent Bit Counts *
Adjacent Bit Counts Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 599 Accepted: 502 ...
- Node基础-CommonJS模块化规范
1.在本地项目中基于NPM/YARN安装第三方模块 第一步:在本地项目中创建一个"package.json"的文件 作用:把当前项目所有依赖的第三方模块信息(包含:模块名称以及版本 ...
- 【Offer】[3-2] 【不修改数组找出重复的数字】
题目描述 思路分析 Java代码 代码链接 题目描述 在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的. 请找出数组中任意一个重复的数字,但不能修改输入的数组. ...
- 洛谷 P1181数列分段Section I
星爆气流(弃疗)斩! ——<刀剑神域> 题目:https://www.luogu.org/proble ...