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