ASP.NET Web API是如何根据请求选择Action的?[上篇]

Web API的调用请求总是针对定义在某个HttpController中的某个Action方法,请求响应的内容来源于调用目标Action方法的执行结果。当ASP.NET Web API为当前请求成功激活目标HttpController之后,后续的操作就是为请求在该HttpController中选择出对应的Action方法。[本文已经同步到《How ASP.NET Web API Works?》]

HttpActionSelector

在对用于描述定义在HttpController中的Action方法的HttpActionDescriptor对象具有充分了解之后,我们开始正式介绍真正用于选择目标Action方法的HttpActionSelector对象。ASP.NET Web API中的HttpActionSelector均实现了IHttpActionSelector接口。

   1: public interface IHttpActionSelector
   2: {
   3:     ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor);
   4:     HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);
   5: }

如上面的代码片断所示,IHttpActionSelector接口中定义了两个方法。GetActionMapping方法返回定义在指定HttpController中所有Action与其名称的映射。该方法唯一的参数表示用于描述目标HttpController的HttpControllerDescriptor对象,返回类型ILookup<string, HttpActionDescriptor>,其Key和Element分别表示Action的名称和用于描述Action方法的HttpActionDescriptor对象。

由于同一个HttpController类型中可以定义多个同名的Action方法重载,我们也可以通过ActionNameAttribute特性为多个Action方法指定相同的Action名称,所以多个Action方法可以共享相同的名称,所以GetActionMapping方法才会返回一个ILookup<string, HttpActionDescriptor>对象。

针对请求对目标Action的选择实现在SelectAction方法中,作为该方法唯一参数的是表示当前HttpController上下文的HttpControllerContext对象,我们可以从中获取表示当前请求的HttpRequestMessage对象和通过ASP.NET Web API路由系统生成的HttpRouteData。该方法返回的HttpActionDescriptor正是对最终用于处理当前请求的Action方法的描述。

ApiControllerActionSelector

与我们在前面介绍的众多“标准化组件”一样,ASP.NET Web API默认用于选择目标Action的HttpActionSelector也是注册在当前的ServicesContainer中,我们可以直接通过ServicesContainer具有如下定义的扩展方法GetActionSelector得到这个注册的HttpActionSelector。

   1: public static class ServicesExtensions
   2: {
   3:     //其他成员
   4:     public static IHttpActionSelector GetActionSelector(this ServicesContainer services);
   5: }

通过分析如下所示的DefaultServices构造函数的定义我们知道默认使用的HttpActionSelector是一个类型为ApiControllerActionSelector的对象。

   1: public class DefaultServices : ServicesContainer
   2: {
   3:     // 其他成员
   4:     public DefaultServices(HttpConfiguration configuration)
   5:     {
   6:         //其他操作
   7:         this.SetSingle<IHttpActionSelector>(new ApiControllerActionSelector());
   8:     }
   9: }

ApiControllerActionSelector定义在“System.Web.Http.Controllers”命名空间下,基本的成员定义如下所示。

   1: public class ApiControllerActionSelector : IHttpActionSelector
   2: {   
   3:     public ApiControllerActionSelector();
   4:     public virtual ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor);
   5:     public virtual HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);
   6: }

有效的Action方法

现在我们来着重讨论ApiControllerActionSelector的GetActionMapping方法的实现。实现逻辑其实很简单:它通过作为参数的HttpControllerDescriptor得到HttpController的真实类型,并调用GetMethods方法获得描述所有方法成员MethodInfo列表,然后从中筛选出所有“有效”的Action方法并以此创建ReflectedHttpActionDescriptor对象。那么一个有效的Action方法具有怎样的“资质”呢?

对于一个实现了IHttpController接口的HttpController类型来说,其拥有的所有MethodInfo必须同时满足如下的条件才会被视为有效的Action方法并被用于创建对应的HttpActionDescriptor对象:

  • 必须是公有的实例方法。
  • MethodInfo的IsSpecialName属性值为False(表示属性成员Getter和Setter的MethodInfo不会被用于创建HttpActionDescriptor)。
  • 从ApiController类型中继承的方法不是有效的Action方法

所有有效的MethodInfo被选择出来并连同指定的HttpControllerDescriptor一起生成一组ReflectedHttpActionDescriptor对象集合,它被转换成一个ILookup<string, HttpActionDescriptor>对象后直接作为GetActionMapping方法的返回值。如下的代码片断基本反映了GetActionMapping方法中最初生成HttpActionDescriptor与其Action名称映射的逻辑。

   1: public class ApiControllerActionSelector : IHttpActionSelector
   2: {
   3:     //其他成员
   4:     public ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor)
   5:     {
   6:         IEnumerable<ReflectedHttpActionDescriptor> actionDescriptor = 
   7:             from method in controllerDescriptor.ControllerType.GetMethods()
   8:             where method.IsPublic && !method.IsStatic && !method.IsSpecialName&& !method.DeclaringType.IsAssignableFrom(typeof(ApiController))
   9:             select new ReflectedHttpActionDescriptor(controllerDescriptor,method);
  10:         return actionDescriptor.ToLookup(action => action.ActionName, action => (HttpActionDescriptor)action);
  11:     }    
  12: }

为了避免频繁、重复地对类型的方法成员进行反射而影响性能,定义在上面代码片断的操作仅仅会在第一次调用GetActionMapping方法时被执行。生成的ReflectedHttpActionDescriptor会被缓存起来以服务于后续调用。

ActionMethodSelector

HttpActionSelector的终极目标是根据当前请求从目标HttpController中选择出正确的Action方法,这个目标实现在它的SelectAction方法中。在系统介绍实现在ApiControllerActionSelector的SelectAction方法中的Action选择机制之前,我们还需要了解另一个与之相关的对象:ActionMethodSelector。

ActionMethodSelector的目的在于判断某个Action方法是否与当前请求相匹配,它们均实现了具有如下定义的IActionMethodSelector接口。IActionMethodSelector仅仅是定义在程序集System.Web.Http.dll中的一个内部接口而已,它定义了一个唯一的方法IsValidForRequest,方法的两个参数分别是表示当前 HttpController上下文的HttpControllerContext对象和代码目标Action方法的MethodInfo。执行该方法得到的布尔值表明目标Action方法是否能够用于处理当前请求。

   1: internal interface IActionMethodSelector
   2: {
   3:     bool IsValidForRequest(HttpControllerContext controllerContext, MethodInfo methodInfo);
   4: }

ASP.NET Web API仅仅定义了唯一一个实现了这个IActionMethodSelector接口的类型,它就是具有如下定义的NonActionAttribute。顾名思义,如果在某个方法上应用了这个特性,意味着目标方法不是一个合法的Action方法。从给出的代码片断可以看出NonActionAttribute直接在实现的IsValidForRequest方法中返回False。

   1: [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)]
   2: public sealed class NonActionAttribute : Attribute, IActionMethodSelector
   3: {
   4:     bool IActionMethodSelector.IsValidForRequest(HttpControllerContext controllerContext, MethodInfo methodInfo)
   5:     {
   6:         return false;
   7:     }
   8: }

对于NonActionAttribute特性来说,有一点值得强调:虽然应用了该特性的方法被认为不再是一个合法的Action方法,但是ApiControllerActionSelector的GetActionMapping方法并不会使用它来判断Action方法的有效性。

在《下篇》中我们将以实例的形式讨论该主题最为核心的内容:ASP.NET Web API如何利用HttpActionSelector在目标HttpController成功激活之后如何从中选择出匹配的Action方法来处理当前的请求的。

作者:Artech
出处:http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 

ASP.NET Web API是如何根据请求选择Action的?[上篇]的更多相关文章

  1. ASP.NET Web API是如何根据请求选择Action的?[下篇]

    ASP.NET Web API是如何根据请求选择Action的?[下篇] 再<上篇>中我们简单介绍了用于实现Action选择机制的HttpActionSelector,接下来我们来讨论本章 ...

  2. ASP.NET Web API是如何根据请求选择Action的?[上篇] 【转】

    http://www.cnblogs.com/leo_wl/p/3316548.html ASP.NET Web API是如何根据请求选择Action的?[上篇] Web API的调用请求总是针对定义 ...

  3. ASP.NET Web API是如何根据请求选择Action的?[下篇] 【转】

    再<上篇>中我们简单介绍了用于实现Action选择机制的HttpActionSelector,接下来我们来讨论本章最为核心的内 容:ASP.NET Web API如何利用HttpActio ...

  4. Self Host模式下的ASP. NET Web API是如何进行请求的监听与处理的?

    构成ASP.NET Web API核心框架的消息处理管道既不关心请求消息来源于何处,也不需要考虑响应消息归于何方.当我们采用Web Host模式将一个ASP.NET应用作为目标Web API的宿主时, ...

  5. 【转】WCF和ASP.NET Web API在应用上的选择

    文章出处:http://www.cnblogs.com/shanyou/archive/2012/09/26/2704814.html 在最近发布的Visual Studio 2012及.NET 4. ...

  6. WCF和ASP.NET Web API在应用上的选择

    小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/shareto ...

  7. [转载]WCF和ASP.NET Web API在应用上的选择

    http://www.cnblogs.com/shanyou/archive/2012/09/26/2704814.html http://msdn.microsoft.com/en-us/libra ...

  8. WCF和ASP.NET Web API在应用上的选择(转)

    出处:http://www.cnblogs.com/shanyou/archive/2012/09/26/2704814.html 在最近发布的Visual Studio 2012及.NET 4.5中 ...

  9. ASP.NET Web API 2.0新特性:Attribute Routing1

    ASP.NET Web API 2.0新特性:Attribute Routing[上篇] 对于一个针对ASP.NET Web API的调用请求来说,请求的URL和对应的HTTP方法的组合最终决定了目标 ...

随机推荐

  1. windows编ffmpeg2.2.4和插件h265

    0.前言 据说新出来了h265的视频,在迅雷看看上面看到的.网上查看了一下简单介绍,貌似h265的视频比h264的视频压缩率要高.并且能做4K的视频. 同一时候看到网上有人试过ffmpeg在编译的时候 ...

  2. IntelliJ IDEA 开发scala

    1.下载安装IntelliJ IDEA,并安装scala插件 我下载的是linux的13版本,linux版本是绿色版本,有一个启动的脚本,运行就可以了,也可以在linux建立快捷方式.windows的 ...

  3. 【百度地图API】如何给自定义覆盖物添加事件

    原文:[百度地图API]如何给自定义覆盖物添加事件 摘要: 给marker.lable.circle等Overlay添加事件很简单,直接addEventListener即可.那么,自定义覆盖物的事件应 ...

  4. NEFUOJ 500 二分法+最大流量

    http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=500 description 在这个信息化的时代.网购成为了最流行的购物方 ...

  5. Swift # 柯里化函数

    前言 此次文章,讲述的是Swift的一个新特性(柯里化函数),可能很多iOS开发人员是第一次听这个词汇,包括我自己也是,自己也用了几天时间才总结出来,希望能帮助到各位咯,个人感觉偏向有开发经验的码友, ...

  6. [CLR via C#]4. 类型基础及类型、对象、栈和堆运行时的相互联系

    原文:[CLR via C#]4. 类型基础及类型.对象.栈和堆运行时的相互联系 CLR要求所有类型最终都要从System.Object派生.也就是所,下面的两个定义是完全相同的, //隐式派生自Sy ...

  7. 在Eclipse下导入vlc-android并编译

    在Ubuntu14.04下载好了VLC的源代码后,VLC的Eclipseproject存放在"vlc-android"文件夹 root@dzt-VirtualBox:/home/d ...

  8. 为什么使用Hystrix?

    分布式服务弹性框架“Hystrix”实践与源码研究(一)   文章初衷 为了应对将来在线(特别是无线端)业务量的成倍增长,后端服务的分布式化程度需要不断提高,对于服务的延迟和容错管理将面临更大挑战,公 ...

  9. Linux 经常使用 性能 检测 命令 说明

    1.uptime [root@smgsim02 ~]# uptime  15:08:15 up 98 days,  4:19,  2 users,  load average: 0.07, 0.29, ...

  10. Windows系统服务的编写。

    实验资源下载地址:点击打开链接 只是不知道能不能从服务向桌面进程传递消息,,就像两个桌面进程之间用Sendmessage似的..希望有知道的大神可以指点一下..不胜感激.. 因为微软在Vista之后, ...