ASP.NET Core中为指定类添加WebApi服务功能

POCO Controller是 ASP.NET Core 中的一个特性,虽然在2015年刚发布的时候就有这个特性了,可是大多数开发者都只是按原有的方式去写,而没有用到这个特性。其实,如果利用这个特性进行稍微封装后,用在SOA架构中Service层的场景中是极其便利的。这篇文章主要就是说我最近在学习使用开源AOP库AspectCore写WebApi动态代理客户端的时候,实现为普通类无添加WebApi服务的过程。

POCO控制器简介

POCO控制器就是ASP.NET Core项目中所有带有Controller后缀的类、或者标记了[Controller]特性的类,虽然没有像模版项目中那样继承自Controller类,也会被识别为控制器,拥有跟普通控制器一样的功能,像下面这段代码中,两个类都会被识别成控制器:

  1. public class PocoController

  2. {

  3.    public IActionResult Index()

  4.    {

  5.        return new ContentResult() { Content = Hello from POCO controller! };

  6.    }

  7. }

  8. [Controller]

  9. public class Poco

  10. {

  11.    public IActionResult Index()

  12.    {

  13.        return new ContentResult() { Content = Hello from POCO controller! };

  14.    }

  15. }

POCO控制器原理

其实,在ASP.NET Core中,已经不像旧版本的 ASP.NET WebApi 那样,通过ControllerFactory来创建Controller,多亏于ASP.NET Core一脉相承的IoC框架 Microsoft.Extensions.DependencyInjection,ASP.NET Core中的内部实现变得更优雅。其中POCO控制器的核心原理就在IApplicationFeatureProvider<ControllerFeature>这个接口的实现ControllerFeatureProvider

通过aspnet/Mvc项目的Github源码仓库中查询得知,Mvc里把Controller、ViewComponent、TagHelper、Views等组件定义为特性(Feature),如ControllerFeature,特性里就存放了应用中被识别为相组件的类型的集合,如如ControllerFeature中就存放了所有Controller类型。IApplicationFeatureProvider<ControllerFeature>这个接口是用来给MVC框架提供控制器类型识别的接口,当把这个接口的实现注册到服务配置中,就能为其中识别的类型提供控制器功能。

ControllerFeatureProvider是这个接口的默认实现,其中有一个方法IsController(TypeInfo typeInfo)的功能就是判断某类型是否为控制器的。而接口方法PopulateFeature(IEnumerable<ApplicationPart> parts,ControllerFeature feature)则为把传入的 “Mvc应用部分(ApplicationPart,大概是指Mvc的作用程序集)”中的类型都一一判断,如果是控制器,那么就加入控制器特性对象中。

实现自定义判断规则

通过上面的剖析,我们就知道要实现自定义的控制器判断规则,只需要重写ControllerFeature类或者重新实现IApplicationFeatureProvider<ControllerFeature>接口,但是由于PopulateFeature不是虚方法或抽象方法,所以不能被重写,那么只能重新写一个类来实现IApplicationFeatureProvider<ControllerFeature>接口了。为了兼容原来规则,我把原来的规则照搬过来,复制了IsController的方法(开源的好处),并且在PopulateFeature中加入了自己的规则。先贴代码,避免篇幅过长,IsController方法的实现就直接链接到源码了:

  1. internal class ServiceControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>

  2. {

  3.    private const string ControllerTypeNameSuffix = "Controller";

  4.    private readonly IEnumerable<Type> ServiceTypes;

  5.    public ServiceControllerFeatureProvider(IEnumerable<Type> ServiceTypes)

  6.    {

  7.            this.ServiceTypes = ServiceTypes;

  8.    }

  9.    public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)

  10.    {

  11.        foreach (var type in Reflection.CurrentAssembiles.SelectMany(o => o.DefinedTypes))

  12.        {

  13.            if (IsController(type) || ServiceTypes.Any(o => type.IsClass && o.IsAssignableFrom(type)) && !feature.Controllers.Contains(type))

  14.            {

  15.                    feature.Controllers.Add(type);

  16.            }

  17.        }

  18.    }

  19.    protected bool IsController(TypeInfo typeInfo)

  20.    {

  21.        //...

  22.    }

  23. }

上面代码的原理,是按照我的框架的需求来改写的,构造方法传入的参数ServiceTypes是定义了服务方法的接口的类型,接口和对应实现类似于以下代码,这些代码可以写在一个.NET Core控制台项目中。

  1. public interface ITestService

  2. {

  3.    string Test(string name);

  4. }

  5. [Route("test")]

  6. public class TestService : ITestService

  7. {

  8.    [Route("{name}"), HttpGet]

  9.    public string Test(string name)

  10.    {

  11.        return "Hello " + name;

  12.    }

  13. }

其中TestService类就是会被识别为控制器的类,但是接口和实现是可以分开在不同程序集的。通过原本ControllerFeatureProvider类中PopulateFeature方法的parts参数中的类型是不包括除了引用了Mvc的程序集的其它程序集的,所以我这里用自己实现的类型扫描类Reflection中的CurrentAssembiles静态变量来获取当前应用程序的所有引用的(自己创建的项目)程序集的,具体实现的代码在我的框架[Shriek]的源码中。

配置自定义规则

现在,我们拥有了自定义控制器识别规则ServiceControllerFeatureProvider,那么,怎么配置到Mvc中呢?又要去翻源码了!在MvcCoreMvcBuilderExtensions.cs扩展类中,有一个IMvcBuilder的扩展方法ConfigureApplicationPartManagerIMvcCoreBuilder也有这样的扩展方法),它的参数是传入ApplicationPartManager参数的委托,而ApplicationPartManager中有一个FeatureProviders属性,用来存储所有IApplicationFeatureProvider实例,会在应用第一次运行的时候,循环这些“特性提供器”提供所有上面提到的MVC特性。所以,只要我们在这里添加我们自定义的控制器特性提供器,MVC框架内部就能识别我们的指定的类型为控制器,并为他们添加控制器的相关功能。

设计有点绕,那么我们用代码来实现:

  1. //示例直接new ServiceCollection对象,下面有完整的能运行的示例代码。

  2. var services = new ServiceCollection();

  3. services.AddMvcCore()

  4.        .ConfigureApplicationPartManager(manager =>

  5.        {

  6.            var featureProvider = new ServiceControllerFeatureProvider(typeof(ITestService));

  7.            manager.FeatureProviders.Add(featureProvider);

  8.        });

看看效果

现在,在TestService类所在项目文件中引入以下Nuget包(没错,运行一个webapi只需要两个Nuget包):

  1.    <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.0.0" />

  2.    <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.0.0" />

然后在控制台程序的入口文件Program.cs的Main方法中写入一下代码:

  1. internal class Program

  2. {

  3.    public static void Main(string[] args)

  4.    {

  5.       new WebHostBuilder()

  6.            .UseKestrel()

  7.            .UseUrls("http://localhost:8080")

  8.            .ConfigureServices(services =>

  9.            {

  10.                services.AddMvcCore()

  11.                .ConfigureApplicationPartManager(manager =>

  12.                {

  13.                    var featureProvider = new ServiceControllerFeatureProvider(typeof(ITestService));

  14.                    manager.FeatureProviders.Add(featureProvider);

  15.                });

  16.            })

  17.            .Configure(app => app.UseMvc())

  18.            .Build()

  19.            .Start();

  20.    }

  21. }

一切编译通过后,点击运行,在浏览器中访问”http://localhost:8080/test/elderjames”,如果看到返回了“Hello elderjames”,那么就大功告成啦!


总结

这篇文章中主要介绍了通过实现IApplicationFeatureProvider<ControllerFeature>接口实现设置指定类型为WebApi控制器的方法。

在接下来的文章中,我会介绍如何从接口获取自定义特性标签,实现从接口获得mvc特性,使得接口和实现类都不依赖MVC库的方法,只要在接口中以标记特性的方式定义了路由和http方法,实现类的操作就都按照接口的路由和http方法去提供WebApi服务,最后还要介绍使用功能强大的.NTE Core开源AOP框架AspectCore实现的动态代理客户端,注册以上所说的接口,即可获得可以调用对应的WebApi服务。这些工作的源码可以在我的框架示例项目中运行,大家有兴趣可以看看效果。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323643.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

elementui解决el-dialog不清空内容的问题,el-dialog关闭时销毁子组件

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 前言 现在是2022年5月5日22:48:21&#xff01; 今天在使用element-ui中的el-dialog的时候遇到了个这样的问题&#xff1a;页面上点击添加的按钮&#xff0c;弹出el-dialog对话框&#xff…

MySQL 中的重做日志,回滚日志以及二进制日志的简单总结

转载自 MySQL 中的重做日志&#xff0c;回滚日志以及二进制日志的简单总结 MySQL中有六种日志文件&#xff0c;分别是&#xff1a;重做日志&#xff08;redo log&#xff09;、回滚日志&#xff08;undo log&#xff09;、二进制日志&#xff08;binlog&#xff09;、错误日志…

c语言分离三位数

#include<stdio.h> main(){ int k,l,m,n;printf("请输入一个三位数"); scanf("%d",&k);lk/100;mk/10%10;nk%10;printf("这个三位数的百位是:%d\n",l);printf("这个三位数的十位是:%d\n",m);printf("这个三位数的个位是…

POJ1821-Fence【单调队列,dp】

正题 题目链接:http://poj.org/problem?id1821 题目大意 有n个木板,m个工人&#xff0c;每个木板只能被粉刷一次&#xff0c;第i个工人如果刷的话必须刷木板SiSi&#xff0c;连续的不超过LiLi的&#xff0c;没一块PiPi。 解题思路 用fi,jfi,j表示前j块木板&#xff0c;前i个…

分布式ID自增算法 Snowflake

近在尝试EF的多数据库移植&#xff0c;但是原始项目中主键用的Sqlserver的GUID。MySQL没法移植了。 其实发现GUID也没法保证数据的递增性&#xff0c;又不太想使用int递增主键&#xff0c;就开始探索别的ID形式。 后来发现twitter的Snowflake算法。 一开始我尝试过直接引用N…

java中,根据指定日期显示出前n天的日期

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 今天分享的是&#xff1a;在java中&#xff0c;根据指定日期显示出前n天的日期 效果如下&#xff1a; 大家注意观察上面的时间&#xff0c;我传入的时间是&#xff1a;2022年5月9日21:28:…

你真的很熟分布式和事务吗?

转载自 你真的很熟分布式和事务吗&#xff1f; 微吐槽 hello,world. 不想了&#xff0c;我等码农&#xff0c;还是看看怎么来处理分布式系统中的事务这个老大难吧&#xff01; 本文略长&#xff0c;读者需要有一定耐心&#xff0c;如果你是高级码农或者架构师级别&#xf…

线程1

模拟龟兔赛跑 线程1 package test;/*** 模拟龟兔赛跑* author Administrator**/ class Rab extends Thread{public void run() {for (int i 0; i < 100; i) {System.out.println("兔子跑了"i"步");}} }class array1 extends Thread{public void run(…

jdbc实现批量给多个表中更新数据(解析Excel表数据插入到数据库中)

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 前言 现在是&#xff1a;2022年5月19日08:01:51 今天遇到了个这样的需求&#xff0c;解析excel表中的数据&#xff0c;以JDBC的方式&#xff0c;将数据批量更新至不同的数据表中。注意&am…

CF559C-Gerald and Giant Chess【计数类dp】

正题 上不了Codeforces&#xff0c;就用洛谷了 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidCF559C 题目大意 H∗WH∗W的棋盘上有一个卒从(1,1)走到(H,W)&#xff0c;有些点不能走&#xff0c;求方案总数。 解题思路 首先如果没有障碍走到(i,j)方案数…

.Net Core 全局配置读取管理方法 ConfigurationManager

最近在学习.Net Core的过程中&#xff0c;发现.Net Framework中常用的ConfigurationManager在Core中竟然被干掉了。 也能理解。Core中使用的配置文件全是Json&#xff0c;不像Framework使用的XML&#xff0c;暂时不支持也是能理解的&#xff0c;但是毕竟全局配置文件这种东西还…

Http 持久连接与 HttpClient 连接池

转载自 Http 持久连接与 HttpClient 连接池 一、背景 HTTP协议是无状态的协议&#xff0c;即每一次请求都是互相独立的。因此它的最初实现是&#xff0c;每一个http请求都会打开一个tcp socket连接&#xff0c;当交互完毕后会关闭这个连接。 HTTP协议是全双工的协议&#x…

jdbc解析excel文件,批量插入数据至库中

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂”前言现在是&#xff1a;2022年5月20日09:32:38今天遇到了个这样的需求&#xff0c;解析excel表中的数据&#xff0c;以JDBC的方式&#xff0c;将数据批量更新至不同的数据表中。注意&…

线程2

public class test{/*** 测试延迟继承* param args*/public static void main(String[] args) {Cat catnew Cat();cat.start();//启动线程&#xff0c;会导致run函数的运行Dog dognew Dog();//创建一个线程对象Thread tnew Thread(dog);t.start();}} //继承Thread创建线程 clas…

.NET及.NET Core系统架构

.NET 官方架构指南 Microservices and Docker Containers Web Applications with ASP.NET 官网地址&#xff1a;https://www.microsoft.com/net/learn/architecture 三层及多层架构 Multitier Architecture ASP.NET N-Tier Architecture Schema Visual Studio N-Tier Examp…

POJ3208-Apocalypse Someday【数位dp】

正题 题目链接:http://poj.org/problem?id3208 题目大意 求第X个有3个连续的6的数。 解题思路 用fi,j(j<3)fi,j(j<3)表示i位&#xff0c;已经有j个6的方案总数。然后fi,3fi,3表示i位的魔鬼数的总数。 然后动态转移方程。 fi,09∗(fi−1,0fi−1,1fi−1,2)fi,09∗(fi…

Spring Boot 自动配置的 “魔法” 是如何实现的?

转载自 Spring Boot 自动配置的 “魔法” 是如何实现的&#xff1f; Spring Boot是Spring旗下众多的子项目之一&#xff0c;其理念是约定优于配置&#xff0c;它通过实现了自动配置&#xff08;大多数用户平时习惯设置的配置作为默认配置&#xff09;的功能来为用户快速构建出…

for循环(一)

利用for循环打出一百句HelloWorld #include<stdio.h>main(){int i;for(i0;i<100;i){printf("HelloWorld\n");} } ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190324224126588.png?x-oss-processimage/watermark,type_ZmFuZ3poZW5naGVpdGk,sha…

解决vue登录信息不及时更新问题

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 前言 现在是&#xff1a;2022年5月20日09:59:34 前面写过一篇文章&#xff0c;基于bladex框架实现的模拟登录&#xff0c;后来在测试的过程中发现了个问题&#xff0c;即A系统在跳转到本系…

学习ASP.NET Core,怎能不了解请求处理管道[2]: 服务器在管道中的“龙头”地位

ASP.NET Core管道由注册的服务器和一系列中间件构成。我们在上一篇中深入剖析了中间件&#xff0c;现在我们来了解一下服务器。服务器是ASP .NET Core管道的第一个节点&#xff0c;它负责完整请求的监听和接收&#xff0c;最终对请求的响应同样也由它完成。[本文已经同步到《AS…