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源码分析三十五:ABP中动态WebAPI原理解析的更多相关文章
- ABP源码分析三十:ABP.RedisCache
ABP 通过StackExchange.Redis类库来操作Redis数据库. AbpRedisCacheModule:完成ABP.RedisCache模块的初始化(完成常规的依赖注入) AbpRed ...
- ABP源码分析三十八: ABP.Web.Api.OData
如果对OData不熟悉的话可参考OData的初步认识一文以获取OData的一些初步知识. API.Odata 模块唯一用处就是提供了一个泛型版本的ODataController,实现了Controll ...
- ABP源码分析三十四:ABP.Web.Mvc
ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...
- ABP源码分析二十五:EventBus
IEventData/EventData: 封装了EventData信息,触发event的源对象和时间 IEventBus/EventBus: 定义和实现了了一系列注册,注销和触发事件处理函数的方法. ...
- ABP源码分析三十二:ABP.SignalR
Realtime Realtime是ABP底层模块提供的功能,用于管理在线用户.它是使用SignalR实现给在线用户发送通知的功能的前提 IOnlineClient/OnlineClient: 封装在 ...
- ABP源码分析三十六:ABP.Web.Api
这里的内容和ABP 动态webapi没有关系.除了动态webapi,ABP必然是支持使用传统的webApi.ABP.Web.Api模块中实现了一些同意的基础功能,以方便我们创建和使用asp.net w ...
- ABP源码分析三十九:ABP.Hangfire
ABP对HangFire的集成主要是通过实现IBackgroundJobManager接口的HangfireBackgroundJobManager类完成的. HangfireBackgroundJo ...
- ABP源码分析四十五:ABP ZERO中的EntityFramework模块
AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...
- ABP源码分析三十一:ABP.AutoMapper
这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttri ...
随机推荐
- C# 文章导航
1. C#相关文章 1.1 C# 基础(一) 访问修饰符.ref与out.标志枚举等等 1.2 C# 基础(二) 类与接口 1.3 C# DateTime日期格式化 1.4 C# DateTime与时 ...
- 【.net 深呼吸】细说CodeDom(3):命名空间
在上一篇文章中,老周介绍了表达式和语句,尽管老周没有把所有的内容都讲一遍,但相信大伙至少已经掌握基本用法.在本文中,咱们继续探讨 CodeDom 方面的奥秘,这一次咱们聊聊命名空间. 在开始之前,老周 ...
- 实时的.NET程序错误监控产品Exceptionless
Exceptionless可以对ASP.NET, Web API, WebForms, WPF, Console, 和 MVC 应用提供错误监控.上传.报表服务.使用时需要在Exceptionless ...
- JAVA回调机制(CallBack)详解
序言 最近学习java,接触到了回调机制(CallBack).初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBack做了一个定义.当然了,我在理解了回 ...
- Visual Studio 2012远程调试中遇到的问题
有的时候开发环境没问题的代码在生产环境中会某些开发环境无法重现的问题,或者需要对生产环境代码进行远程调试该怎么办? Vs已经提供给开发者远程调试的工具 下面简单讲讲该怎么用,前期准备:1.本地登录账户 ...
- 几个有趣的WEB设备API 前端提高B格必备(一)——电池状态&震动api
受到同事启发,突然发现了几个有趣又实用的web api,没想到前端还有这么多有趣的东西可以玩~~简直过分. 1.电池状态API navigator.getBattery():这个api返回的是一个pr ...
- 【绝对干货】仿微信QQ设置图形头像裁剪,让你的App从此炫起来~
最近在做毕业设计,想有一个功能和QQ一样可以裁剪头像并设置圆形头像,额,这是设计狮的一种潮流. 而纵观现在主流的APP,只要有用户系统这个功能,这个需求一般都是在(bu)劫(de)难(bu)逃(xue ...
- MongoDB集群配置
本文演示:(一个主服务器,一个备份服务器,三个仲裁服务器) 官方推荐副本集的成员数量为奇数,最多12个副本集节点,最多7个节点参与选举. 本文演示基于本机,用端口区分服务(每个服务器下新建db文件夹用 ...
- ESLint的使用笔记
原文地址:https://csspod.com/getting-started-with-eslint/?utm_source=tuicool&utm_medium=referral 在团队协 ...
- Android Studio-—使用OpenCV的配置方法和demo以及开发过程中遇到的问题解决
前提: 1.安装Android Studio(过程略) 2.官网下载OpenCV for Android 网址:http:opencv.org/downloads.html 我下载的是下图的版本 3. ...