ASP.NET Web API的消息处理管道: HttpRoutingDispatcher
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的更多相关文章
- ASP.NET Web API的消息处理管道:"龙头"HttpServer
ASP.NET Web API的消息处理管道:"龙头"HttpServer 一般来说,对于构成ASP.NET Web API消息处理管道的所有HttpMessageHandler来 ...
- ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇]
ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇] ASP.NET Web API服务端框架核心是一个独立于具体寄宿环境的消息处理管道,它不关心请求消息来源于何 ...
- ASP.NET Web API 2 消息处理管道
Ø 前言 ASP.NET 的应用程序都会有自己的消息处理管道和生命周期,比如:ASP.NET Web 应用程序(Web Form).ASP.NET MVC,还有本文将讨论的 ASP.NET Web ...
- ASP.NET Web API标准的“管道式”设计
ASP.NET Web API的核心框架是一个消息处理管道,这个管道是一组HttpMessageHandler的有序组合.这是一个双工管道,请求消息从一端流入并依次经过所有HttpMessageHan ...
- Web API之消息处理管道
Web API之消息处理管道 前言 MVC有一套请求处理的机制,当然Web API也有自己的一套消息处理管道,该消息处理管道贯穿始终都是通过HttpMessageHandler来完成.我们知道请求信息 ...
- ASP.NET Web API的安全管道
本篇体验ASP.NET Web API的安全管道.这里的安全管道是指在请求和响应过程中所经历的各个组件或进程,比如有IIS,HttpModule,OWIN,WebAPI,等等.在这个管道中大致分两个阶 ...
- Web APi之消息处理管道(五)
前言 MVC有一套请求处理的机制,当然Web API也有自己的一套消息处理管道,该消息处理管道贯穿始终都是通过HttpMessageHandler来完成.我们知道请求信息存在 RequestMessa ...
- ASP.NET Web API标准的“管道式”设计
详见:http://www.cnblogs.com/artech/p/asp-net-web-api-pipeline.html http://www.codeproject.com/Articles ...
- 目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建
目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建 通过上面的介绍我们知道利用HttpControllerSelector可以根据 ...
随机推荐
- 【百度地图API】如何实现信息窗口轮询
原文:[百度地图API]如何实现信息窗口轮询 摘要: 很多微博或者SNS开发者,想结合地图展示用户的微博或者状态.这时,利用信息窗口就是最好的展示方式了. 在这里,我们使用信息窗口轮询的方式来实现这一 ...
- POJ1719- Shooting Contest(二分图最大匹配)
题目链接 题意:给定一个矩阵,每列有两个白点,其它都是黑点,如今要求每列选一个白点,使得每一行至少包括一个白点被选中 思路:利用白点所在的位置用行指向列建图,用行去匹配列,最大匹配数假设不等于行数的话 ...
- NPOI 的使用心得
Excel 数据 通过 c# 代码逻辑 导入 到数据库 . 其中各种坑爹.原因是 单元格 数据 与 数据库 字段的数据类型 会有出入.因为 Excel 是人工输入. 比如 一个单元格 本来 只能 ...
- 【转】简述什么是Web服务(Web Service)技术?
Web Service 是在 Internet 上进行分布式计算的基本构造块,是组件对象技术在 Internet 中的延伸,是一种部署在Web 上的组件.它融合了以组件为基础的开发模式和 ...
- MVC+Jqgrid
基于MVC+Jqgrid的列表页面 2014-12-08 12:01 by 刘尼玛, 1583 阅读, 20 评论, 收藏, 编辑 一.前言 “尼玛哥,上周你教我改了下OA系统UI,黄总看了很满意呀. ...
- Nginx搭建反向代理服务器
[大型网站技术实践]初级篇:借助Nginx搭建反向代理服务器 一.反向代理:Web服务器的“经纪人” 1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受int ...
- 批处理中set截取字符具体解释
set截取字符具体解释 在批处理中,set的功能有点繁杂:设置变量.显示环境变量的名及值.做算术运算.等待用户的输入.字符串截取.替换字符串,是我们经常使用的命令之中的一个. 在字符串截取方面,新手 ...
- SQL SERVER2005 excel float导入
接到mission:将一堆excel的东西导入到SQL SERVER2005 命令很easy SELECT * INTO XLImport3 FROM OPENDATASOURCE('Microsof ...
- javascript 学习总结(二)Array数组
1.数组常用方法 var colors = ["red", "blue", "green"]; //creates an array wit ...
- 单独一个img标签的居中显示
针对页面当中通过img插入图片的时候,要保证这个图片在页面内容当中居中,一般的做法是在外面套一个div,通过给div加入 {margin:0 auto;} 来控制图片的居中. 那么如果针对后台上传的图 ...