ABP源碼分析三十五:ABP中動態WebAPI原理解析


動態WebAPI應該算是ABP中最Magic的功能之一了吧。開發人員無須定義繼承自ApiController的類,只須重用Application Service中的類就可以對外提供WebAPI的功能,這應該算是對DRY的最佳詮釋了. 如下圖所示,一行代碼就為所有實現了IApplicationService的類型,自動創建對應的動態WebAPI.

這么Magic的功能是如何實現的呢?

本文為你揭開其Magic的外表。你會發現,實現如此Magic的功能,最關鍵的代碼只有四行。

 


先思考一個問題:如果不從ASP.NET WebApi 的ApiController繼承,我們能實現ASP.NET WebAPi嗎?

答案:不可以. 從APIController繼承來實現我們自己的HttpController是實現ASP.NET WebApi的前提。

那么問題又來了。我們在使用ABP框架的時候,沒有創建任何從APIController繼承的類。那么從APIController繼承的類在哪里?和ApplicationService中的類又有怎樣的關系?

先給出答案:ABP框架自動給ApplicationService中的類創建了“HttpController”,他們從APIController繼承。

 

基於上面的分析,要實現WebApi有三個問題需要解決:如何定義HttpController?路由的規則如何設置?如何激活和調用HttpController中的Action?接下來逐一解答。


 

如何定義HttpController?

ABP中ApplicationService並不是從APIController繼承,或實現IHttpController接口。為解決HttpController類型缺失的問題,ABP首先為所有的ApplicationService動態的創建一個DynamicApiController<T> ,這個類繼承自AbpApiController,其中T是接口繼承自IApplicationService。

但是DynamicApiController<T> 是一個空的類,其沒有任何Action. 這樣的HttpController類顯然無用的。那么如何給這些動態生成的DynamicApiController<T>對象根據T(ApplicationService接口)中對應的方法添加相對應的Action?

看似復雜的問題,ABP以一種巧妙的辦法解決了,關鍵就在AbpWebApiModule中的4行代碼(下面59-62行)。這里簡單解釋一下ABP的做法:

1. 通過Castle創建DynamicApiController<T>的代理類,

2. 為代理類動態添加ApplicationService接口(這里就是指T,也就是讓代理類實現了接口T,這樣通過代理類就可以訪問接口T中定義的方法),

3. 同時為代理類添加攔截器。

這樣當ABP通過Castle獲取DynamicApiController<T>實例的時候,其實得到的是DynamicApiController<T>的代理類(關鍵)。 通過DynamicApiController<T>的代理類調用ApplicationService的接口中的定義的方法的時候(必須通過反射的方式調用,因為接口T中的方法對DynamicApiController<T>實例是不可見的。但實際上是可見的,因為你得到的是從T接口繼承的DynamicApiController<T>的代理類實例,而不是DynamicApiController<T>實例本身。),會被攔截器攔截。而攔截器則調用真正的ApplicationService對象來執行方法(這里也很關鍵,因為代理類中只要方法的聲明,沒有實現。所以這里需要攔截器將其方法調用攔截並路由到真正的ApplicationService對象上)。對這四行代碼不理解的話可先閱讀下文:http://www.cnblogs.com/1zhk/p/5399548.html

舉個例子:

假設有一個ApplicationService的接口是IFooAppication.

第59行,DynamicApiController<IFooAppication>被register到Castle容器中。

第60行,為DynamicApiController<IFooAppication>創建proxy代理,並為該代理添加接口IFooAppication。

第61行,為proxy代理添加攔截器AbpDynamicApiControllerInterceptor<IFooAppication>


 

路由的規則如何設置?

通過AbpWebApiModule的InitializeRoutes方法硬編碼在Abp.Web.Api的代碼中。很明顯這里路由使用了*通配符,也就是所有api/services/XXXX的請求都是有效的,都會進入WebApi的消息管道。

 

 

如何根據routedata激活和調用具體生成的DynamicApiController<T>對象?

ABP通過AbpWebApiModule的InitializeAspNetServices方法使用自定義的對象替換了默認的IhttpControllerSelector對象,IHttpActionSelector對象,IHttpControllerActivatore對象。如果了解ASP.Net WebApi底層工作原理的開發人員一定對這三個接口應該很熟悉。如果不了解的同學要先做做功課,才能明白后文的內容。

至此,大概解釋了ABP的動態WebApi的工作原理。


以下是對與動態WebAPI相關的接口和對象逐一分析。這些接口和類都圍繞着兩個中心目標:為動態Controller創建可供ASP.NET WebApi使用的描述器和選擇器(Descriptor,Selector),以及構建和保存動態Controller的類型信息。

首先看看ApiController和Configuration

AbpApiController:繼承了MVC的ApiController,ABP 中的WebApi Controller直接或間接的都從AbpApiController繼承。第二張圖,顯示了AbpApiController引用了哪些ABP核心類庫中的功能模塊的對象。

IDynamicApiController:空接口,用於標識其實現是一個動態生成的ApiController。

DynamicApiController<T>:用作所有動態生成的ApiController的基類。

 

IAbpWebApiModuleConfiguration/AbpWebApiModuleConfiguration : 封裝了HttpConfiguration屬性,初始化為GlobalConfiguration.Configuration對象。 因為ASP.NET Web API在Web Host下通過ASP.Net的靜態類型GlobalConfiguration的Configuration屬性獲取到的用於配置請求處理管道的HttpConfiguration對象。ABP的動態WebApi本質上仍是ASP.NET Web API,所以這樣配置HttpConfiguration是必然的。

 


與Controller激活和調用相關的接口和類主要有下面這些。其實都是繼承自ASP.NET WebAPi中默認的使用的對象,並重載了一些方法以支持動態APiController的發現,激活和調用。

 

DynamicHttpControllerDescriptor : 繼承自asp.net Webapi系統的HttpControllerDescriptor,與ASP.NET WebAPI 中默認的HttpControllerDescriptor相比,其多了一個IFilter[]數組。這樣做的原因很簡單,因為ABP中的ApiController是動態生成的,是沒有標注Filter特性的。所以ABP通過下面這種方式給動態ApiController加上Filter。

 

DynamicHttpActionDescriptor : 繼承自asp.net Webapi系統的ReflectedHttpActionDescriptor,與ASP.NET WebAPI 中默認的HttpActionDescriptor相比,其多了一個IFilter[]數組。這樣做的原因和上面一致

 

AbpHttpControllerSelector : 繼承自asp.net Webapi系統的DefaultHttpControllerSelector。通過重寫SelectController來返回HttpControllerDescriptor, 這是ABP能動態創建APIController的關鍵。ASP.Net  WebAPI 中的IHttpControllerSelector對象負責根據HttpRouteData返回HttpControllerDescriptor。HttpControllerDescriptor中封裝了controller的類型等信息。這里ABP通過繼承DefaultHttpControllerSelector,並重寫SelectController方法來根據HttpRouteData中的數據創建HttpControllerDescriptor對象並返回

 

 

AbpApiControllerActivator:實現了IHttpControllerActivator接口,根據controllerType生成具體的controller. 由於ABP系統使用了Castle框架來管理對象。所以有必要實現自己的IHttpControllerActivator以替換ASP.Net系統默認的實現。

 

AbpApiControllerActionSelector : 繼承自ASP.Net WebAPI 的 ApiControllerActionSelector。 通過重寫SelectAction來返回HttpActionDescriptor的派生類DynamicHttpActionDescriptor的實例, 這是ABP能執行動態創建的APIController的Action方法的關鍵。AbpApiControllerActionSelector 通過調用DynamicApiServiceNameHelper的靜態方法(傳入routedata中的serviceNameWithAction)獲取action的那么

DynamicApiServiceNameHelper:靜態類,提供四個靜態方法。兩個方法用於校驗servicename是否合規,還有兩個方法用於servicename中獲取service和action的name。

 

AbpDynamicApiControllerInterceptor<T> : 實現了Castle的IInterceptor。作為動態生成的DynamicApiController<T>的攔截器,它攔截所有對action的調用,然后通過反射調用底層真實的IApplicationService對象的方法。

 


在傳統的asp.net webapi應用中,系統會根據路由信息,通過反射到程序集中去匹配對應的controller的類型信息。而在ABP中,controller的類型信息是初始化的時候直接添加到一個Dictionary集合中的。本文第一幅圖中的代碼干的就是這件事。完成這個功能模塊所涉及的接口和類主要有以下這些。

 

上圖代碼中所示,構建DynamicHttpControllerDescriptor 的數據來源於一個DynamicApiControllerInfo對象。那么DynamicApiControllerInfo對象又是在什么時候怎么構建的呢?下圖是ABP關於構建applicationService的DynamicApiControllerInfo對象所涉及的類型和接口。

 

DynamicApiControllerInfo:ABP用於封裝ApiController的信息,下圖顯示了其所有的屬性。其中最關鍵的屬性就是ApiControllerType.其實就是一個DynamicApiController<T>類型,其中的T就是具體的ApplicationService接口的類型。

 

 

DynamicApiActionInfo:用於封裝動態生成的ApiController的Action的信息:actionName,Filters, methondinfo和httpVerb。DynamicApiControllerInfo封裝了一個DynamicApiActionInfo的字典對象,用以表示這個Controller可支持的Action列表。

 

 

DynamicApiControllerManager:提供了一個Dictionary容器管理所有的DynamicApiControllerInfo對象。共有三個方法:Register方法用於將DynamicApiControllerInfo添加到Dictionary容器中,另外兩個方法用於返回DynamicApiControllerInfo。

 

DynamicApiControllerBuilder:提供兩個方法,一個For<T>方法通過ApiControllerBuilder為某一個application service類創建DynamicApiControllerInfo。另一個ForAll<T>方法通過BatchApiControllerBuilder為某一類application service類(這一類application service會有個共同的接口)創建DynamicApiControllerInfo。

 

IApiControllerBuilder<T>/ApiControllerBuilder<T>:其內部封裝了一個字典對象IDictionary<string, ApiControllerActionBuilder<T>>用於存放T的每個方法對應的ApiControllerActionBuilder對象。最后通過調用Build()方法生成完整的DynamicApiControllerInfo對象。這里注意觀察IApiControllerBuilder的代碼,他是支持鏈式編程的,可以通過WithFilters的方法給這個Application Service的API controller添加filter

 

 

IBatchApiControllerBuilder<T>/BatchApiControllerBuilder<T> : 為assembly中符合命名規范的接口批量生成DynamicApiControllerInfo。其最后仍然是通過ApiControllerBuilder逐個為各個application service接口創建DynamicApiControllerInfo.

 

 

 

如下圖,ApiControllerBuilder在構建DynamicApiControllerInfo過程中,需要調用ApiControllerActionBuilder對象去構建該DynamicApiControllerInfo所包含的DynamicApiActionInfo

 

 

 

DynamicApiControllerActionHelper:靜態類,用於獲取一個type的所有方法(property除外,object的原生方法除外,ApplicationService除外)的列表。

 

 

 

DynamicApiVerbHelper:根據方法名按照約定返回httpVerb。

 

 

IApiControllerActionBuilder/ApiControllerActionBuilder:用於構建DynamicApiActionInfo對象的生成器。這里有一個注意點:如上圖,如果action的methodName是以get開頭的,默認ABP會標注其httpVerb為Get, 但是有一個例外,如果方法的參數不為primitive 類型和不可為空類型時,ABP會標注其httpVerb為Post。


 

AbiApiExplorer:繼承自ApiExplorer類,實現了IApiExplorer接口。其ApiDescriptions屬性既包括你自己編寫的webApi (39-44行)又包括ABP動態生成的WebApi(47 -)。

ABP通過遍歷DynamicApiControllerManager中的DynamicControllerInfo,然后在遍歷DynamicControllerInfo的DynamicApiActionInfo,為他們逐個構建ApiDescription實例。

 

 返回ABP源碼分析系列文章目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM