ASP.NET Web API的消息处理管道: HttpRoutingDispatcher

认情况下,作为消息处理管道“龙头”的HttpServer的Dispatcher属性返回一个HttpRoutingDispatcher对象,该对象可以视为这个消息处理管道的最后一个非DelegatingHandler类型的HttpMessageHandler。用户的调用请求一般都是针对定义在某个HttpController中的某个Action方法,所以消息处理管道最终需要激活相应的HttpController并执行对应的Action方法,而这些是通过HttpRoutingDispatcher来完成的。[本文已经同步到《How ASP.NET Web API Works?》]

如下面的代码片断所示,HttpRoutingDispatcher并不是DelegatingHandler的继承者,而是直接继承自HttpMessageHandler。我们在构建一个HttpRoutingDispatcher对象的时候需要指定一个HttpConfiguration对象,而通过参数defaultHandler指定的HttpMessageHandler对于创建的HttpRoutingDispatcher对象来说具有重要的意义,HttpController的激活、Action方法的选择与执行等后续操作实际上是由它来完成的。

   1: public class HttpRoutingDispatcher : HttpMessageHandler
   2: {   
   3:     public HttpRoutingDispatcher(HttpConfiguration configuration);
   4:     public HttpRoutingDispatcher(HttpConfiguration configuration, HttpMessageHandler defaultHandler);
   5:    
   6:     protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
   7: }

ASP.NET Web API消息处理管道不具有一个类似于HttpContext的对象来表示基于当前请求的上下文,但是表示请求消息的HttpRequestMessage对象具有一个通过Properties属性表示的属性字典,我们可以将基于当前上下文的数据或对象添加到当前请求消息的属性字典中。通过对HttpServer的介绍我们知道它会将当前SynchronizationContext和HttpConfiguration添加当前请求消息的属性字典,通过ASP.NET Web API路由系统生成的HttpRouteData或者通过ASP.NET路由系统生成的RouteData转换而成的HttRouteData(针对Web Host寄宿模式)同样以相同的方式保存在当前请求消息中。

保存HttpRouteData对应属性条目的Key为“MS_HttpRouteData”,我们可以通过类型HttpPropertyKeys的静态字段HttpRouteDataKey得到这个Key,也可以直接调用HttpRequestMessage的如下两个扩展方法GetRouteData和SetRouteData进行RouteData的获取和设置。

   1: public static class HttpPropertyKeys
   2: {
   3:     //其他成员
   4:     public static readonly string HttpRouteDataKey;
   5: }
   6:  
   7: public static class HttpRequestMessageExtensions
   8: {
   9:     //其他成员
  10:     public static IHttpRouteData GetRouteData(this HttpRequestMessage request);
  11:     public static void SetRouteData(this HttpRequestMessage request, IHttpRouteData routeData);
  12: }

当HttpRoutingDispatcher的SendAsync方法被执行的时候,它会先判断作为参数的HttpRequestMessage对象的属性字典中的HttpRouteData是否已经存在,如果存在则直接将请求交付给创建时指定的HttpMessageHandler进行处理。如果在通过预定义的Key在请求消息的属性列表中找不到HttpRouteData对象,则直接通过指定的HttpConfiguration的Routes属性得到但前路由表,并将但前请求作为参数调用其GetRouteData方法。如果方法返回一个具体的HttpRouteData对象,则将请求交付给创建时指定的HttpMessageHandler进行后续处理,否则直接返回一个“404, Not Found”响应。

HttpControllerDispatcher

我们从命名可以看出HttpRoutingDispatcher具有两个基本的职能,即“路由(Routing)”和“消息分发(Dispatching)”。对于前者,它会调用当前路由表对请求消息进行解析进而生成用于封装路由数据的HttpRouteData(如果这样的HttpRouteData不存在于当前请求的属性字典中)。对于后者,它会将请求直接分发给在创建时指定的HttpMessageHandler来完成进一步处理。

在默认的情况下(在创建HttpRoutingDispatcher对象的时候在构造函数中没有通过参数具体指定这个HttpMessageHandler)这个从HttpRoutingDispatcher手中接管请求的HttpMessageHandler是一个HttpControllerDispatcher的对象,其成员定义入下所示。HttpControllerDispatcher在整个消息处理管道中显得尤为重要,HttpController的激活、Action方法的执行以及响应消息的生成均是由HttpControllerDispatcher来完成的。

   1: public class HttpControllerDispatcher : HttpMessageHandler
   2: {
   3:     public HttpControllerDispatcher(HttpConfiguration configuration);
   4:     protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
   5:  
   6:     public HttpConfiguration Configuration { get; }
   7: }

当我们将HttpControllerDispatcher引入后,整个消息处理管道具有了如右图所示的结构。从这个结构来看,貌似HttpControllerDispatcher才是整个消息处理管道的最后一个HttpMessageHandler。这种说法没有错,但我们个人还是倾向于将HttpControllerDispatcher视为“隶属于” HttpRoutingDispatcher的“内部”HttpMessageHandler,所以仍将这个“包含” HttpRoutingDispatcher的HttpRoutingDispatcher视为组成消息处理管道的最后一个HttpMessageHandler。除此之外,“N个DelegagingHandler + 1个HttpMessageHander”这样的链式结构也刚好与基于DelegagingHandler的委托链相匹配。对于读者朋友来说,具体倾向于那种说法并不重要,重要的是能够深刻了解整个消息处理管道的真正构成。

实例演示:揭示HttpRoutingDispatcher的路由功能

为了让读者对实现在HttpRoutingDispatcher中的路由功能(即通过调用路由表对请求消息进行匹配解析并生成HttpRouteData,并将其放入请求消息的属性字典)具有更加深刻的影响,我们来进行一个简单的实例演示来揭示其路由功能的存在。

我们在一个空的ASP.NET MVC应用中定义如下一个MyHttpRoutingDispatcher。MyHttpRoutingDispatcher直接继承自HttpRoutingDispatcher,其SendAsync方法的目的在于将受保护的同名方法“转换”成公有方法以方便后续调用。

   1: public class MyHttpRoutingDispatcher : HttpRoutingDispatcher
   2: {
   3:     public MyHttpRoutingDispatcher(HttpConfiguration configuration)
   4:         : base(configuration)
   5:     { }
   6:  
   7:     public new Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   8:     {
   9:         return base.SendAsync(request, cancellationToken);
  10:     }
  11: }

我们创建了一个具有如下定义的HomeController。在默认的Action方法Index中,我们创建了一个HttpConfiguration,并在其路由表中注册了一个URL模板为“wheather/{areaCode}/{days}”的HttpRoute。然后为创建了一个基于HTTP-GET的HttpRequestMessage,其访问地址(“http://www.artech.com/wheather/010/2”)与注册的URL模板相匹配。接下来我们针对这个HttpConfiguration创建了一个MyHttpRoutingDispatcher对象,并将该HttpRequestMessage对象作为参数调用其SendAsync方法。最后我们直接调用HttpRequestMessage对象的扩展方法GetRouteData获取添加到它属性字典中的HttpRouteData对象,并将保存在Values属性中的路由变量的名称和值输出来。

   1: public class HomeController : Controller
   2: {
   3:     public void Index()
   4:     {
   5:         HttpConfiguration configuration = new HttpConfiguration();
   6:         configuration.Routes.MapHttpRoute("default", "wheather/{areaCode}/{days}");
   7:         HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://www.artech.com/wheather/010/2");
   8:         MyHttpRoutingDispatcher dispatcher = new MyHttpRoutingDispatcher(configuration);
   9:         dispatcher.SendAsync(request, new CancellationToken(false));
  10:         IHttpRouteData routeData = request.GetRouteData();
  11:         foreach (var item in routeData.Values)
  12:         {
  13:             Response.Write(string.Format("{0}: {1}<br/>", item.Key, item.Value));
  14:         }
  15:     }
  16: }

该程序运行之后会在浏览器中呈现出如右图所示的输出结果,可以看出我们期望的两个路由变量(areaCode和days)被正常输出,而它们正是在HttpRoutingDispatcher的SendAsync方法被执行的时候生成并添加到请求消息之中的。

缺省路由变量的删除

我们在进行路由注册的时候可以为某个路由变量设置一个默认值,这个默认值可以是一个具体的变量值,也可以是通过RouteParameter具有如下定义的静态只读字段Optional返回的一个RouteParameter对象,我们具有这种默认值的路由变量成为缺省路由变量。

   1: public sealed class RouteParameter
   2: {
   3:     public static readonly RouteParameter Optional;
   4: }

虽然同是具有默认值的路由变量,但是缺省路由变量具有不同之处:如果请求URL中没有提供对应变量的值,普通具有默认值的路由变量依然会出现在最终HttpRouteData的Values属性中,但是缺省路由变量则不会。实际上缺省路由变量的删除是由HttpRoutingDispatcher实现的。在执行SendAsync方法的时候,如果得到的HttpRouteData(可能是预先添加到HttpRequestMessage的属性字典中,也可以是HttpRoutingDispatcher利用ASP.NET Web API路由系统对请求进行解析获得)的Values属性具有值为RouteParameter. Optional的路由变量,它会被直接剔除出去。

ASP.NET Web API的消息处理管道: HttpRoutingDispatcher的更多相关文章

  1. ASP.NET Web API的消息处理管道:"龙头"HttpServer

    ASP.NET Web API的消息处理管道:"龙头"HttpServer 一般来说,对于构成ASP.NET Web API消息处理管道的所有HttpMessageHandler来 ...

  2. ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇]

    ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇] ASP.NET Web API服务端框架核心是一个独立于具体寄宿环境的消息处理管道,它不关心请求消息来源于何 ...

  3. ASP.NET Web API 2 消息处理管道

    Ø  前言 ASP.NET 的应用程序都会有自己的消息处理管道和生命周期,比如:ASP.NET Web 应用程序(Web Form).ASP.NET MVC,还有本文将讨论的 ASP.NET Web ...

  4. ASP.NET Web API标准的“管道式”设计

    ASP.NET Web API的核心框架是一个消息处理管道,这个管道是一组HttpMessageHandler的有序组合.这是一个双工管道,请求消息从一端流入并依次经过所有HttpMessageHan ...

  5. Web API之消息处理管道

    Web API之消息处理管道 前言 MVC有一套请求处理的机制,当然Web API也有自己的一套消息处理管道,该消息处理管道贯穿始终都是通过HttpMessageHandler来完成.我们知道请求信息 ...

  6. ASP.NET Web API的安全管道

    本篇体验ASP.NET Web API的安全管道.这里的安全管道是指在请求和响应过程中所经历的各个组件或进程,比如有IIS,HttpModule,OWIN,WebAPI,等等.在这个管道中大致分两个阶 ...

  7. Web APi之消息处理管道(五)

    前言 MVC有一套请求处理的机制,当然Web API也有自己的一套消息处理管道,该消息处理管道贯穿始终都是通过HttpMessageHandler来完成.我们知道请求信息存在 RequestMessa ...

  8. ASP.NET Web API标准的“管道式”设计

    详见:http://www.cnblogs.com/artech/p/asp-net-web-api-pipeline.html http://www.codeproject.com/Articles ...

  9. 目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建

    目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建 通过上面的介绍我们知道利用HttpControllerSelector可以根据 ...

随机推荐

  1. OC第四课

    主要内容:NSString.NSArray.NSNumber 一.苹果公司的帮助文档(API) 学会查看API对于后续的编程有很好的帮助 进入方法: Xcode ->Help -> Doc ...

  2. Responsive Design in 3 Steps

    Responsive web design is no doubt a big thing now. If you still not familiar with responsive design, ...

  3. 第39届ACM亚洲区域赛牡丹江赛区赛后总结

    2014年10月10日,周五,早晨匆匆忙忙的出了寝室,直奔复印社去打了两份模板,然后直接就去上课了.第三节课下课,直接跟老师讲了一声,就去实验室跟学长们汇合了.12点半,踏上了开往牡丹江的列车,我们那 ...

  4. MyEclipse 设置全部jsp的编码为UFT-8 的方法

  5. CSS学习笔记:溢出文本省略(text-overflow)

    原文:CSS学习笔记:溢出文本省略(text-overflow) 在CSS3中,text-overflow属性的基本语法如下: clip:表示不显示省略文本,简单的裁切. ellipsis:表示对象文 ...

  6. Win8.1系统下配置搭建IIS8.5+PHP5.5.4运行环境

    原文 Win8.1系统下配置搭建IIS8.5+PHP5.5.4运行环境 很多人喜欢用linux搭建php网页语言运行环境,但由于linux高度自定义化,经常需要root运行命令,略显高端,相对应的微软 ...

  7. Spring IOC之容器扩展点

    一般来说,一个应用开发者不需要继承ApplicationContext实现类.取而代之的是,Spring IoC容器可以通过插入特殊的整合接口的实现来进行扩展.下面的几点将要讲述这些整合的接口. 1. ...

  8. 警惕使用WebClient.DownloadFile(string uri,string filePath)方法

    原文:警惕使用WebClient.DownloadFile(string uri,string filePath)方法 WebClient.DownloadFile(string uri,string ...

  9. uml系列(四)——类图

    类图是uml的核心.学习类图,总共须要掌握三个部分:类:类之间的关系:类图怎么画. 首先,类.老规矩,先来张图. 类是什么:举个简单的样例:猫.狗.猪三个都是动物.这里面的"动物" ...

  10. SQL Server 数据库定时自动备份

    原文:SQL Server 数据库定时自动备份 SQL Server 数据库定时自动备份——每天定时备份,保留前8天的备份 利用SQL Server代理新建作业来定期备份 1)在数据库库的[SQL S ...