POCO Controller是? ASP.NET Core ? 中的一個(gè)特性,雖然在2015年剛發(fā)布的時(shí)候就有這個(gè)特性了,可是大多數(shù)開發(fā)者都只是按原有的方式去寫,而沒有用到這個(gè)特性。其實(shí),如果利用這個(gè)特性進(jìn)行稍微封裝后,用在SOA架構(gòu)中Service層的場景中是極其便利的。這篇文章主要就是說我最近在學(xué)習(xí)使用開源AOP庫AspectCore寫WebApi動(dòng)態(tài)代理客戶端的時(shí)候,實(shí)現(xiàn)為普通類無添加WebApi服務(wù)的過程。
POCO控制器簡介
POCO控制器就是ASP.NET Core項(xiàng)目中所有帶有Controller后綴的類、或者標(biāo)記了[Controller]特性的類,雖然沒有像模版項(xiàng)目中那樣繼承自Controller類 ,也會(huì)被識(shí)別為控制器,擁有跟普通控制器一樣的功能,像下面這段代碼中,兩個(gè)類都會(huì)被識(shí)別成控制器:
public class PocoController
{
? ? public IActionResult Index ( )
? ? {
? ? ? ? return new ContentResult ( ) { Content = “ Hello from POCO controller ! ” } ;
? ? }
}
[ Controller ]
public class Poco
{
? ? public IActionResult Index ( )
? ? {
? ? ? ? return new ContentResult ( ) { Content = “ Hello from POCO controller ! ” } ;
? ? }
}
POCO控制器原理
其實(shí),在ASP.NET Core中,已經(jīng)不像舊版本的 ASP.NET WebApi 那樣,通過ControllerFactory來創(chuàng)建Controller,多虧于ASP.NET Core一脈相承的IoC框架? Microsoft.Extensions.DependencyInjection,ASP.NET Core中的內(nèi)部實(shí)現(xiàn)變得更優(yōu)雅。其中POCO控制器的核心原理就在IApplicationFeatureProvider<ControllerFeature>這個(gè)接口的實(shí)現(xiàn)ControllerFeatureProvider。
通過aspnet/Mvc項(xiàng)目的Github源碼倉庫中查詢得知,Mvc里把Controller、ViewComponent、TagHelper、Views等組件定義為特性(Feature),如ControllerFeature,特性里就存放了應(yīng)用中被識(shí)別為相組件的類型的集合,如如ControllerFeature中就存放了所有Controller類型。IApplicationFeatureProvider<ControllerFeature>這個(gè)接口是用來給MVC框架提供控制器類型識(shí)別的接口,當(dāng)把這個(gè)接口的實(shí)現(xiàn)注冊(cè)到服務(wù)配置中,就能為其中識(shí)別的類型提供控制器功能。
ControllerFeatureProvider是這個(gè)接口的默認(rèn)實(shí)現(xiàn),其中有一個(gè)方法IsController(TypeInfo typeInfo)的功能就是判斷某類型是否為控制器的。而接口方法PopulateFeature(IEnumerable<ApplicationPart> parts,ControllerFeature feature)則為把傳入的 “Mvc應(yīng)用部分(ApplicationPart,大概是指Mvc的作用程序集)”中的類型都一一判斷,如果是控制器,那么就加入控制器特性對(duì)象中。
實(shí)現(xiàn)自定義判斷規(guī)則
通過上面的剖析,我們就知道要實(shí)現(xiàn)自定義的控制器判斷規(guī)則,只需要重寫ControllerFeature類或者重新實(shí)現(xiàn)IApplicationFeatureProvider<ControllerFeature>接口,但是由于PopulateFeature不是虛方法或抽象方法,所以不能被重寫,那么只能重新寫一個(gè)類來實(shí)現(xiàn)IApplicationFeatureProvider<ControllerFeature>接口了。為了兼容原來規(guī)則,我把原來的規(guī)則照搬過來,復(fù)制了IsController的方法(開源的好處),并且在PopulateFeature中加入了自己的規(guī)則。先貼代碼,避免篇幅過長,IsController方法的實(shí)現(xiàn)就直接鏈接到源碼了:
internal class ServiceControllerFeatureProvider : IApplicationFeatureProvider < ControllerFeature >
{
? ? private const string ControllerTypeNameSuffix = "Controller" ;
? ? private readonly IEnumerable < Type > ServiceTypes ;
? ? public ServiceControllerFeatureProvider ( IEnumerable < Type > ServiceTypes )
? ? {
? ? ? ? ? ? this . ServiceTypes = ServiceTypes ;
? ? }
? ? public void PopulateFeature ( IEnumerable < ApplicationPart > parts , ControllerFeature feature )
? ? {
? ? ? ? foreach ( var type in Reflection . CurrentAssembiles . SelectMany ( o = > o . DefinedTypes ) )
? ? ? ? {
? ? ? ? ? ? if ( IsController ( type ) || ServiceTypes . Any ( o = > type . IsClass && o . IsAssignableFrom ( type ) ) && ! feature . Controllers . Contains ( type ) )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ?feature . Controllers . Add ( type ) ;
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? protected bool IsController ( TypeInfo typeInfo )
? ? {
? ? ? ?
? ? }
}
上面代碼的原理,是按照我的框架的需求來改寫的,構(gòu)造方法傳入的參數(shù)ServiceTypes是定義了服務(wù)方法的接口的類型,接口和對(duì)應(yīng)實(shí)現(xiàn)類似于以下代碼,這些代碼可以寫在一個(gè).NET Core控制臺(tái)項(xiàng)目中。
public interface ITestService
{
? ? string Test ( string name ) ;
}
[ Route ( "test" ) ]
public class TestService : ITestService
{
? ? [ Route ( "{name}" ) , HttpGet ]
? ? public string Test ( string name )
? ? {
? ? ? ? return "Hello " + name ;
? ? }
}
其中TestService類就是會(huì)被識(shí)別為控制器的類,但是接口和實(shí)現(xiàn)是可以分開在不同程序集的。通過原本ControllerFeatureProvider類中PopulateFeature方法的parts參數(shù)中的類型是不包括除了引用了Mvc的程序集的其它程序集的,所以我這里用自己實(shí)現(xiàn)的類型掃描類Reflection中的CurrentAssembiles靜態(tài)變量來獲取當(dāng)前應(yīng)用程序的所有引用的(自己創(chuàng)建的項(xiàng)目)程序集的,具體實(shí)現(xiàn)的代碼在我的框架[Shriek]的源碼中。
配置自定義規(guī)則
現(xiàn)在,我們擁有了自定義控制器識(shí)別規(guī)則ServiceControllerFeatureProvider,那么,怎么配置到Mvc中呢?又要去翻源碼了!在MvcCoreMvcBuilderExtensions.cs擴(kuò)展類中,有一個(gè)IMvcBuilder的擴(kuò)展方法ConfigureApplicationPartManager(IMvcCoreBuilder也有這樣的擴(kuò)展方法),它的參數(shù)是傳入ApplicationPartManager參數(shù)的委托,而ApplicationPartManager中有一個(gè)FeatureProviders屬性,用來存儲(chǔ)所有IApplicationFeatureProvider實(shí)例,會(huì)在應(yīng)用第一次運(yùn)行的時(shí)候,循環(huán)這些“特性提供器”提供所有上面提到的MVC特性。所以,只要我們?cè)谶@里添加我們自定義的控制器特性提供器,MVC框架內(nèi)部就能識(shí)別我們的指定的類型為控制器,并為他們添加控制器的相關(guān)功能。
設(shè)計(jì)有點(diǎn)繞,那么我們用代碼來實(shí)現(xiàn):
var services = new ServiceCollection ( ) ;
services . AddMvcCore ( )
? ? ? ? . ConfigureApplicationPartManager ( manager = >
? ? ? ? {
? ? ? ? ? ? var featureProvider = new ServiceControllerFeatureProvider ( typeof ( ITestService ) ) ;
? ? ? ? ? ?manager . FeatureProviders . Add ( featureProvider ) ;
? ? ? ? } ) ;
看看效果
現(xiàn)在,在TestService類所在項(xiàng)目文件中引入以下Nuget包(沒錯(cuò),運(yùn)行一個(gè)webapi只需要兩個(gè)Nuget包):
? ? < PackageReference Include = " Microsoft.AspNetCore.Hosting " Version = " 2.0.0 " />
? ? < PackageReference Include = " Microsoft.AspNetCore.Mvc.Core " Version = " 2.0.0 " />
然后在控制臺(tái)程序的入口文件Program.cs的Main方法中寫入一下代碼:
internal class Program
{
? ? public static void Main ( string [ ] args )
? ? {
? ? ? new WebHostBuilder ( )
? ? ? ? ? ? . UseKestrel ( )
? ? ? ? ? ? . UseUrls ( "http://localhost:8080" )
? ? ? ? ? ? . ConfigureServices ( services = >
? ? ? ? ? ? {
? ? ? ? ? ? ? ?services . AddMvcCore ( )
? ? ? ? ? ? ? ? . ConfigureApplicationPartManager ( manager = >
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? var featureProvider = new ServiceControllerFeatureProvider ( typeof ( ITestService ) ) ;
? ? ? ? ? ? ? ? ? ?manager . FeatureProviders . Add ( featureProvider ) ;
? ? ? ? ? ? ? ? } ) ;
? ? ? ? ? ? } )
? ? ? ? ? ? . Configure ( app = > app . UseMvc ( ) )
? ? ? ? ? ? . Build ( )
? ? ? ? ? ? . Start ( ) ;
? ? }
}
一切編譯通過后,點(diǎn)擊運(yùn)行,在瀏覽器中訪問”http://localhost:8080/test/elderjames”,如果看到返回了“Hello elderjames”,那么就大功告成啦!
總結(jié)
這篇文章中主要介紹了通過實(shí)現(xiàn)IApplicationFeatureProvider<ControllerFeature>接口實(shí)現(xiàn)設(shè)置指定類型為WebApi控制器的方法。
在接下來的文章中,我會(huì)介紹如何從接口獲取自定義特性標(biāo)簽,實(shí)現(xiàn)從接口獲得mvc特性,使得接口和實(shí)現(xiàn)類都不依賴MVC庫的方法,只要在接口中以標(biāo)記特性的方式定義了路由和http方法,實(shí)現(xiàn)類的操作就都按照接口的路由和http方法去提供WebApi服務(wù),最后還要介紹使用功能強(qiáng)大的.NTE Core開源AOP框架AspectCore 實(shí)現(xiàn)的動(dòng)態(tài)代理客戶端,注冊(cè)以上所說的接口,即可獲得可以調(diào)用對(duì)應(yīng)的WebApi服務(wù)。這些工作的源碼可以在我的框架示例項(xiàng)目中運(yùn)行,大家有興趣可以看看效果。
總結(jié)
以上是生活随笔 為你收集整理的ASP.NET Core中为指定类添加WebApi服务功能 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。