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. 【百度地图API】如何制作一张魔兽地图!!——CS地图也可以,哈哈哈

    原文:[百度地图API]如何制作一张魔兽地图!!--CS地图也可以,哈哈哈 摘要: 你玩魔兽不?你知道如何做一张魔兽地图不?! 快来看此文吧! ---------------------------- ...

  2. 浅谈 js 字符串 trim 方法之正则篇

    原文:浅谈 js 字符串 trim 方法之正则篇 关于 trim 其实没啥好说的,无非就是去除首位空格,对于现代浏览器来说只是简单的正则 /^\s+|\s+$/ 就可以搞定了.而且支持中文空格   等 ...

  3. 关于tasklet的一点小小的解释

    大概有一些同学对tasklet的串行化还有点困惑,其实在单处理器上最好理解,所以本帖主要讨论多处理器上tasklet如何实现串行化:同一个tasklet对象同一时刻只能在一个处理器上运行. 在 驱动程 ...

  4. 使用微软 URL Rewrite Module 开启IIS伪静态

    原文 使用微软 URL Rewrite Module 开启IIS伪静态 在IIS5和IIS6时代,我们使用URL REWRITING可实现URL重写,使得WEB程序实现伪静态,但默认情况下只能实现.A ...

  5. 解决Win7下一个VC++6.0您不能直接打开多个project问题

    于Win7操作系统,只需双击打开多个VC ++6.0的project当文件,前方和后方的工作区将关闭工作区,这项.VC++6.0仅仅通过文件-->开放式工作区-->找到磁盘project档 ...

  6. webkit的几个属性

    -webkit-text-size-adjust 1.当样式表里font-size<12px时,中文版chrome浏览器里字体显示仍为12px,这时可以用 html{-webkit-text-s ...

  7. 是男人就下100层【第四层】——Crazy贪吃蛇(1)

    贪吃蛇是一款非常经典的游戏,这些经典游戏给我们的童年添加了不少乐趣,今天開始我们来一步一步的在Android设备上实现一款贪吃蛇游戏,我也是第一次写这个游戏,有可能会写错,或者走弯路,可是终于希望能有 ...

  8. 纯CSS3打造七巧板

    原文:纯CSS3打造七巧板 最近项目上要制作一个七巧板,脑子里瞬间闪现,什么...七巧板不是小时候玩的吗... 七巧板的由来 先来个科普吧,是我在查资料过程中看到的,感觉很有意思. 宋朝有个叫黄伯思的 ...

  9. [翻译]初识SQL Server 2005 Reporting Services Part 3

    原文:[翻译]初识SQL Server 2005 Reporting Services Part 3 这是关于SSRS文章中四部分的第三部分.Part 1提供了一个创建基本报表的递阶教程.Part 2 ...

  10. asp.net mvc上传图片案例

    1.放在ajax.BeginForm里,不好使,同asp.net 表单中 fileupload控件不支持ajax差不多吧,如果异步的话可以借助jquery.form.js,很方便 2. //上传文件  ...