【转】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源码分析系列文章目录

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

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

相关文章

jstl处理栏目与子栏目_Detelogy智能前处理设备微展厅P2:再添新品

时隔小半年&#xff0c;新一轮Detelogy产品总览展示栏目再与大家见面。事不宜迟&#xff0c;点击视频即刻进入我们的智能前处理设备微展厅。 知乎视频​www.zhihu.com01 高效多样品前处理系统02 智能浓缩设备全系列03 智能湿法消解设备应用领域农残、兽残检测有机磷类、有机氯类…

【转】ABP源码分析三十六:ABP.Web.Api

这里的内容和ABP 动态webapi没有关系。除了动态webapi&#xff0c;ABP必然是支持使用传统的webApi。ABP.Web.Api模块中实现了一些同意的基础功能&#xff0c;以方便我们创建和使用asp.net webApi。 AbpApiController&#xff1a;这是一个抽象基类&#xff0c;继承自ApiControl…

408最后计算机网络题库,2021考研计算机统考408专业基础综合题库

**部分为历年考研真题。提供2009&#xff5e;2018年考研真题及参考答案&#xff0c;其中2009&#xff5e;2015年每道真题均提供详细答案解析&#xff0c;通过对真题的演练和分析&#xff0c;可以帮助学员牢牢抓住计算机学科专业基础综合考试的命题特点&#xff0c;提高复习效率…

Unity-Shader-渲染队列,ZTest,ZWrite

Unity-Shader-渲染队列&#xff0c;ZTest&#xff0c;ZWrite ZTest&#xff08;深度测试&#xff09;和ZWrite&#xff08;深度写入&#xff09;ZTest Less&#xff08;深度小于当前缓存则通过&#xff09;ZTest Greater&#xff08;深度大于当前缓存则通过&#xff09;ZTest L…

如何做好职业规划(乾卦)

职业一般来说会占用我们每个人生命中的大部分时间&#xff0c;而职业发展中变化莫测的因数常常令我们束手无策、捉襟见肘。在面对变化多端的情况时&#xff0c;中国人的传统智慧就发挥出最大的优势了。如何将快速变化 、不好掌控的事情处理得井井有条&#xff0c;这需要我们懂得…

850是什么意思_沃尔沃为什么不是一线豪华品牌?

1927年诞生的沃尔沃&#xff0c;它造车的历史比宝马还要略早几年。但经过近百年的发展&#xff0c;宝马品牌无论在国际上还是我们国内&#xff0c;以销量还是品牌含金量来看&#xff0c;都是众所公认的豪华品牌第一阵营成员&#xff08;奔驰、宝马&#xff0c;奥迪&#xff09;…

【转】ABP源码分析三十七:ABP.Web.Api Script Proxy API

ABP提供Script Proxy WebApi为所有的Dynamic WebApi生成访问这些WebApi的JQuery代理&#xff0c;AngularJs代理以及TypeScriptor代理。这些个代理就是javascript脚本,通过这些代理可以简单的访问Dynamic webApi。 如下实例演示一个最基本的应用场景。首先通过Script Proxy Web…

word2003如何设置护眼模式_手机屏幕的护眼模式是如何保护你的眼睛?

公司业务包括&#xff1a;二手机回收&#xff0c;二手机销售&#xff0c;配件批发&#xff0c;以及手机维修等业务&#xff0c;目前公司业务覆盖山西全境以及周边省市&#xff0c;在同行业有较高知名度。 随着智能手机的普及&#xff0c;大多数人也逐渐开始沉迷于每天的刷手…

WinCE系统的编译过程

作者&#xff1a;ARM-WinCE 在WinCE系统中&#xff0c;当我们完成了相关的开发和系统定制工作以后&#xff0c;会编译WinCE系统&#xff0c;最后生成NK.bin和NK.nb0。我现在用WinCE6.0在自己的PC上面编译一次用时19分16秒(有一天无聊&#xff0c;就测了一下)。下面介绍一下WinC…

【转】ABP源码分析三十八: ABP.Web.Api.OData

如果对OData不熟悉的话可参考OData的初步认识一文以获取OData的一些初步知识。 API.Odata 模块唯一用处就是提供了一个泛型版本的ODataController&#xff0c;实现了Controller代码的常用。 AbpODataEntityController<TEntity, TPrimaryKey>&#xff1a;使用ABP的repos…

饭卡可以用水冲洗吗_薄壁不锈钢水管真的可以满足大众用水健康管道的要求吗?...

我们都认识现如今的自来水的出厂干净程度达到安全、卫生&#xff0c;干净标准。但是&#xff0c;很是收到不少市民的投诉&#xff0c;说日常饮用的自来水有异味、浑浊、杂质&#xff0c;为何会出现这番现象呢&#xff0c;为此&#xff0c;我国也针对此研究调查&#xff0c;最后…

WinCE的快速启动

PXA270上的双启动功能已经实现了&#xff0c;但又引入了一个新问题&#xff0c;WinCE的启动太慢&#xff0c;大概需要2分钟&#xff01;这显然是不能忍受的。通过两天的努力&#xff0c;现在已经缩短到20秒&#xff0c;虽然还是有点长&#xff0c;但已改善不少了。先整理一下&a…

【转】ABP源码分析三十九:ABP.Hangfire

ABP对HangFire的集成主要是通过实现IBackgroundJobManager接口的HangfireBackgroundJobManager类完成的。 HangfireBackgroundJobManager:实现了接口IBackgroundJobManager中的方法EnqueueAsync&#xff0c;通过HangfireBackgroundJob完成Enqueue。重写了BackgroundWorkerBase…

计算机无法显示硬盘盘符,电脑硬盘不显示盘符怎么办 移动硬盘不显示盘符的原因...

电脑上是目前我们经常会使用到电子设备&#xff0c;其中电脑硬盘是电脑重要的零部件&#xff0c;它可以为电脑存储和运行各种文件&#xff0c;是电脑不可或缺的组件&#xff0c;然后用户在使用电脑的时候突然发现电脑硬盘不显示盘符该怎么办&#xff0c;移动硬盘显不示盘符的有…

百度云cdn设置州五年制大专_图说云原生:让云原生转型变得像种白菜一样简单...

序有一天&#xff0c;女朋友突然问我&#xff1a;“能不能跟我讲讲&#xff0c;你每天在开发的这些“天合”云原生平台、容器引擎、微服务&#xff0c;都是做什么的呀&#xff1f;”我说&#xff1a;“可以啊。简单来讲&#xff0c;我们的产品就是帮助客户实现云原生应用的上云…

【转】ABP源码分析四十:ZERO的Application和Tenant

ABP的Zero模块以数据库为数据源实现了ABP框架中的tenant management (multi-tenancy), role management, user management, session, authorization (permission management), setting management, language management, audit logging等核心功能。ABP中的这些功能具体实现都依…

allegro标注尺寸设置_标注新升级 | SOLIDWORKS 2020新功能揭秘

在实际设计过程中&#xff0c;尺寸链在工程图尺寸标注中使用率是非常高的。尺寸链是由互相联系的尺寸按一定顺序&#xff0c;首尾相接排列而成的封闭尺寸组。常规设置方法在SOLIDWORKS之前的版本中&#xff0c;我们可以使用&#xff1a;基准尺寸、尺寸链(水平&竖直尺寸链)完…

如何选购计算机主板电池,电脑主板电池耗电太快什么原因?如何判断问题并解决?...

一般来说&#xff0c;主板其实并不是非常容易损坏的&#xff0c;但用的时间长了也会出现些小问题&#xff0c;下面小编为大家介绍电脑主板电池耗电太快什么原因&#xff1f;如何判断问题并解决&#xff1f;电脑主板电池耗电太快什么原因CMOS电池耗电太快&#xff0c;有可能是主…

【转】ABP源码分析四十一:ZERO的Audit,Setting,Background Job

AuditLog: 继承自Entity<long>的实体类。封装AuditLog的信息。 AuditingStore: 实现了IAuditingStore接口&#xff0c;实现了将AuditLog的信息保存到数据库的功能。其通过IRepository<AuditLog, long>实例完成对数据库的操作。 BackgroundJobStore : 实现了IBack…

翻译记忆软件:Trados 7/2006,兼容性和基本用法讨论

兼容性 我下载了Trados7&#xff0c;但是发现和Word2003和Word2007均没有加载出来&#xff0c;不能工作。 后来下载Trados2006&#xff08;7.5&#xff09;&#xff0c;安装之后&#xff0c;发现可以在Word2003下使用了&#xff0c;在Word2007下虽然加载项能够出来&#xff0c…