【转】ABP源码分析三十五:ABP中动态WebAPI原理解析
動(dòng)態(tài)WebAPI應(yīng)該算是ABP中最Magic的功能之一了吧。開發(fā)人員無(wú)須定義繼承自ApiController的類,只須重用Application Service中的類就可以對(duì)外提供WebAPI的功能,這應(yīng)該算是對(duì)DRY的最佳詮釋了. 如下圖所示,一行代碼就為所有實(shí)現(xiàn)了IApplicationService的類型,自動(dòng)創(chuàng)建對(duì)應(yīng)的動(dòng)態(tài)WebAPI.
這么Magic的功能是如何實(shí)現(xiàn)的呢?
本文為你揭開其Magic的外表。你會(huì)發(fā)現(xiàn),實(shí)現(xiàn)如此Magic的功能,最關(guān)鍵的代碼只有四行。
?
先思考一個(gè)問(wèn)題:如果不從ASP.NET WebApi 的ApiController繼承,我們能實(shí)現(xiàn)ASP.NET WebAPi嗎?
答案:不可以. 從APIController繼承來(lái)實(shí)現(xiàn)我們自己的HttpController是實(shí)現(xiàn)ASP.NET WebApi的前提。
那么問(wèn)題又來(lái)了。我們?cè)谑褂肁BP框架的時(shí)候,沒(méi)有創(chuàng)建任何從APIController繼承的類。那么從APIController繼承的類在哪里?和ApplicationService中的類又有怎樣的關(guān)系?
先給出答案:ABP框架自動(dòng)給ApplicationService中的類創(chuàng)建了“HttpController”,他們從APIController繼承。
?
基于上面的分析,要實(shí)現(xiàn)WebApi有三個(gè)問(wèn)題需要解決:如何定義HttpController?路由的規(guī)則如何設(shè)置?如何激活和調(diào)用HttpController中的Action?接下來(lái)逐一解答。
?
如何定義HttpController?
ABP中ApplicationService并不是從APIController繼承,或?qū)崿F(xiàn)IHttpController接口。為解決HttpController類型缺失的問(wèn)題,ABP首先為所有的ApplicationService動(dòng)態(tài)的創(chuàng)建一個(gè)DynamicApiController<T>?,這個(gè)類繼承自AbpApiController,其中T是接口繼承自IApplicationService。
但是DynamicApiController<T> 是一個(gè)空的類,其沒(méi)有任何Action. 這樣的HttpController類顯然無(wú)用的。那么如何給這些動(dòng)態(tài)生成的DynamicApiController<T>對(duì)象根據(jù)T(ApplicationService接口)中對(duì)應(yīng)的方法添加相對(duì)應(yīng)的Action?
看似復(fù)雜的問(wèn)題,ABP以一種巧妙的辦法解決了,關(guān)鍵就在AbpWebApiModule中的4行代碼(下面59-62行)。這里簡(jiǎn)單解釋一下ABP的做法:
1. 通過(guò)Castle創(chuàng)建DynamicApiController<T>的代理類,
2. 為代理類動(dòng)態(tài)添加ApplicationService接口(這里就是指T,也就是讓代理類實(shí)現(xiàn)了接口T,這樣通過(guò)代理類就可以訪問(wèn)接口T中定義的方法),
3. 同時(shí)為代理類添加攔截器。
這樣當(dāng)ABP通過(guò)Castle獲取DynamicApiController<T>實(shí)例的時(shí)候,其實(shí)得到的是DynamicApiController<T>的代理類(關(guān)鍵)。?通過(guò)DynamicApiController<T>的代理類調(diào)用ApplicationService的接口中的定義的方法的時(shí)候(必須通過(guò)反射的方式調(diào)用,因?yàn)榻涌赥中的方法對(duì)DynamicApiController<T>實(shí)例是不可見(jiàn)的。但實(shí)際上是可見(jiàn)的,因?yàn)槟愕玫降氖菑腡接口繼承的DynamicApiController<T>的代理類實(shí)例,而不是DynamicApiController<T>實(shí)例本身。),會(huì)被攔截器攔截。而攔截器則調(diào)用真正的ApplicationService對(duì)象來(lái)執(zhí)行方法(這里也很關(guān)鍵,因?yàn)榇眍愔兄灰椒ǖ穆暶?#xff0c;沒(méi)有實(shí)現(xiàn)。所以這里需要攔截器將其方法調(diào)用攔截并路由到真正的ApplicationService對(duì)象上)。對(duì)這四行代碼不理解的話可先閱讀下文:http://www.cnblogs.com/1zhk/p/5399548.html?。
舉個(gè)例子:
假設(shè)有一個(gè)ApplicationService的接口是IFooAppication.
第59行,DynamicApiController<IFooAppication>被register到Castle容器中。
第60行,為DynamicApiController<IFooAppication>創(chuàng)建proxy代理,并為該代理添加接口IFooAppication。
第61行,為proxy代理添加攔截器AbpDynamicApiControllerInterceptor<IFooAppication>
?
路由的規(guī)則如何設(shè)置?
通過(guò)AbpWebApiModule的InitializeRoutes方法硬編碼在Abp.Web.Api的代碼中。很明顯這里路由使用了*通配符,也就是所有api/services/XXXX的請(qǐng)求都是有效的,都會(huì)進(jìn)入WebApi的消息管道。
?
?
如何根據(jù)routedata激活和調(diào)用具體生成的DynamicApiController<T>對(duì)象?
ABP通過(guò)AbpWebApiModule的InitializeAspNetServices方法使用自定義的對(duì)象替換了默認(rèn)的IhttpControllerSelector對(duì)象,IHttpActionSelector對(duì)象,IHttpControllerActivatore對(duì)象。如果了解ASP.Net WebApi底層工作原理的開發(fā)人員一定對(duì)這三個(gè)接口應(yīng)該很熟悉。如果不了解的同學(xué)要先做做功課,才能明白后文的內(nèi)容。
至此,大概解釋了ABP的動(dòng)態(tài)WebApi的工作原理。
以下是對(duì)與動(dòng)態(tài)WebAPI相關(guān)的接口和對(duì)象逐一分析。這些接口和類都圍繞著兩個(gè)中心目標(biāo):為動(dòng)態(tài)Controller創(chuàng)建可供ASP.NET WebApi使用的描述器和選擇器(Descriptor,Selector),以及構(gòu)建和保存動(dòng)態(tài)Controller的類型信息。
首先看看ApiController和Configuration
AbpApiController:繼承了MVC的ApiController,ABP 中的WebApi Controller直接或間接的都從AbpApiController繼承。第二張圖,顯示了AbpApiController引用了哪些ABP核心類庫(kù)中的功能模塊的對(duì)象。
IDynamicApiController:空接口,用于標(biāo)識(shí)其實(shí)現(xiàn)是一個(gè)動(dòng)態(tài)生成的ApiController。
DynamicApiController<T>:用作所有動(dòng)態(tài)生成的ApiController的基類。
?
IAbpWebApiModuleConfiguration/AbpWebApiModuleConfiguration?: 封裝了HttpConfiguration屬性,初始化為GlobalConfiguration.Configuration對(duì)象。 因?yàn)锳SP.NET Web API在Web Host下通過(guò)ASP.Net的靜態(tài)類型GlobalConfiguration的Configuration屬性獲取到的用于配置請(qǐng)求處理管道的HttpConfiguration對(duì)象。ABP的動(dòng)態(tài)WebApi本質(zhì)上仍是ASP.NET Web API,所以這樣配置HttpConfiguration是必然的。
?
與Controller激活和調(diào)用相關(guān)的接口和類主要有下面這些。其實(shí)都是繼承自ASP.NET WebAPi中默認(rèn)的使用的對(duì)象,并重載了一些方法以支持動(dòng)態(tài)APiController的發(fā)現(xiàn),激活和調(diào)用。
?
DynamicHttpControllerDescriptor?: 繼承自asp.net Webapi系統(tǒng)的HttpControllerDescriptor,與ASP.NET WebAPI 中默認(rèn)的HttpControllerDescriptor相比,其多了一個(gè)IFilter[]數(shù)組。這樣做的原因很簡(jiǎn)單,因?yàn)锳BP中的ApiController是動(dòng)態(tài)生成的,是沒(méi)有標(biāo)注Filter特性的。所以ABP通過(guò)下面這種方式給動(dòng)態(tài)ApiController加上Filter。
?
DynamicHttpActionDescriptor?: 繼承自asp.net Webapi系統(tǒng)的ReflectedHttpActionDescriptor,與ASP.NET WebAPI 中默認(rèn)的HttpActionDescriptor相比,其多了一個(gè)IFilter[]數(shù)組。這樣做的原因和上面一致
?
AbpHttpControllerSelector?: 繼承自asp.net Webapi系統(tǒng)的DefaultHttpControllerSelector。通過(guò)重寫SelectController來(lái)返回HttpControllerDescriptor, 這是ABP能動(dòng)態(tài)創(chuàng)建APIController的關(guān)鍵。ASP.Net? WebAPI 中的IHttpControllerSelector對(duì)象負(fù)責(zé)根據(jù)HttpRouteData返回HttpControllerDescriptor。HttpControllerDescriptor中封裝了controller的類型等信息。這里ABP通過(guò)繼承DefaultHttpControllerSelector,并重寫SelectController方法來(lái)根據(jù)HttpRouteData中的數(shù)據(jù)創(chuàng)建HttpControllerDescriptor對(duì)象并返回
?
?
AbpApiControllerActivator:實(shí)現(xiàn)了IHttpControllerActivator接口,根據(jù)controllerType生成具體的controller. 由于ABP系統(tǒng)使用了Castle框架來(lái)管理對(duì)象。所以有必要實(shí)現(xiàn)自己的IHttpControllerActivator以替換ASP.Net系統(tǒng)默認(rèn)的實(shí)現(xiàn)。
?
AbpApiControllerActionSelector?: 繼承自ASP.Net WebAPI 的 ApiControllerActionSelector。 通過(guò)重寫SelectAction來(lái)返回HttpActionDescriptor的派生類DynamicHttpActionDescriptor的實(shí)例, 這是ABP能執(zhí)行動(dòng)態(tài)創(chuàng)建的APIController的Action方法的關(guān)鍵。AbpApiControllerActionSelector 通過(guò)調(diào)用DynamicApiServiceNameHelper的靜態(tài)方法(傳入routedata中的serviceNameWithAction)獲取action的那么
DynamicApiServiceNameHelper:靜態(tài)類,提供四個(gè)靜態(tài)方法。兩個(gè)方法用于校驗(yàn)servicename是否合規(guī),還有兩個(gè)方法用于servicename中獲取service和action的name。
?
AbpDynamicApiControllerInterceptor<T>?: 實(shí)現(xiàn)了Castle的IInterceptor。作為動(dòng)態(tài)生成的DynamicApiController<T>的攔截器,它攔截所有對(duì)action的調(diào)用,然后通過(guò)反射調(diào)用底層真實(shí)的IApplicationService對(duì)象的方法。
?
在傳統(tǒng)的asp.net webapi應(yīng)用中,系統(tǒng)會(huì)根據(jù)路由信息,通過(guò)反射到程序集中去匹配對(duì)應(yīng)的controller的類型信息。而在ABP中,controller的類型信息是初始化的時(shí)候直接添加到一個(gè)Dictionary集合中的。本文第一幅圖中的代碼干的就是這件事。完成這個(gè)功能模塊所涉及的接口和類主要有以下這些。
?
上圖代碼中所示,構(gòu)建DynamicHttpControllerDescriptor 的數(shù)據(jù)來(lái)源于一個(gè)DynamicApiControllerInfo對(duì)象。那么DynamicApiControllerInfo對(duì)象又是在什么時(shí)候怎么構(gòu)建的呢?下圖是ABP關(guān)于構(gòu)建applicationService的DynamicApiControllerInfo對(duì)象所涉及的類型和接口。
?
DynamicApiControllerInfo:ABP用于封裝ApiController的信息,下圖顯示了其所有的屬性。其中最關(guān)鍵的屬性就是ApiControllerType.其實(shí)就是一個(gè)DynamicApiController<T>類型,其中的T就是具體的ApplicationService接口的類型。
?
?
DynamicApiActionInfo:用于封裝動(dòng)態(tài)生成的ApiController的Action的信息:actionName,Filters, methondinfo和httpVerb。DynamicApiControllerInfo封裝了一個(gè)DynamicApiActionInfo的字典對(duì)象,用以表示這個(gè)Controller可支持的Action列表。
?
?
DynamicApiControllerManager:提供了一個(gè)Dictionary容器管理所有的DynamicApiControllerInfo對(duì)象。共有三個(gè)方法:Register方法用于將DynamicApiControllerInfo添加到Dictionary容器中,另外兩個(gè)方法用于返回DynamicApiControllerInfo。
?
DynamicApiControllerBuilder:提供兩個(gè)方法,一個(gè)For<T>方法通過(guò)ApiControllerBuilder為某一個(gè)application service類創(chuàng)建DynamicApiControllerInfo。另一個(gè)ForAll<T>方法通過(guò)BatchApiControllerBuilder為某一類application service類(這一類application service會(huì)有個(gè)共同的接口)創(chuàng)建DynamicApiControllerInfo。
?
IApiControllerBuilder<T>/ApiControllerBuilder<T>:其內(nèi)部封裝了一個(gè)字典對(duì)象IDictionary<string, ApiControllerActionBuilder<T>>用于存放T的每個(gè)方法對(duì)應(yīng)的ApiControllerActionBuilder對(duì)象。最后通過(guò)調(diào)用Build()方法生成完整的DynamicApiControllerInfo對(duì)象。這里注意觀察IApiControllerBuilder的代碼,他是支持鏈?zhǔn)骄幊痰?#xff0c;可以通過(guò)WithFilters的方法給這個(gè)Application Service的API controller添加filter
?
?
IBatchApiControllerBuilder<T>/BatchApiControllerBuilder<T>?: 為assembly中符合命名規(guī)范的接口批量生成DynamicApiControllerInfo。其最后仍然是通過(guò)ApiControllerBuilder逐個(gè)為各個(gè)application service接口創(chuàng)建DynamicApiControllerInfo.
?
?
?
如下圖,ApiControllerBuilder在構(gòu)建DynamicApiControllerInfo過(guò)程中,需要調(diào)用ApiControllerActionBuilder對(duì)象去構(gòu)建該DynamicApiControllerInfo所包含的DynamicApiActionInfo
?
?
?
DynamicApiControllerActionHelper:靜態(tài)類,用于獲取一個(gè)type的所有方法(property除外,object的原生方法除外,ApplicationService除外)的列表。
?
?
?
DynamicApiVerbHelper:根據(jù)方法名按照約定返回httpVerb。
?
?
IApiControllerActionBuilder/ApiControllerActionBuilder:用于構(gòu)建DynamicApiActionInfo對(duì)象的生成器。這里有一個(gè)注意點(diǎn):如上圖,如果action的methodName是以get開頭的,默認(rèn)ABP會(huì)標(biāo)注其httpVerb為Get, 但是有一個(gè)例外,如果方法的參數(shù)不為primitive 類型和不可為空類型時(shí),ABP會(huì)標(biāo)注其httpVerb為Post。
?
AbiApiExplorer:繼承自ApiExplorer類,實(shí)現(xiàn)了IApiExplorer接口。其ApiDescriptions屬性既包括你自己編寫的webApi (39-44行)又包括ABP動(dòng)態(tài)生成的WebApi(47 -)。
ABP通過(guò)遍歷DynamicApiControllerManager中的DynamicControllerInfo,然后在遍歷DynamicControllerInfo的DynamicApiActionInfo,為他們逐個(gè)構(gòu)建ApiDescription實(shí)例。
?
?返回ABP源碼分析系列文章目錄
總結(jié)
以上是生活随笔為你收集整理的【转】ABP源码分析三十五:ABP中动态WebAPI原理解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【转】C# 温故而知新:Stream篇(
- 下一篇: 一年新增23.9万台 被“嫌弃”的慢充桩