ASP.NET Web API提供了一个独立于执行环境的抽象化的HTTP请求处理管道,而ASP.NET Web API自身的路由系统也不依赖于ASP.NET路由系统,所以它可以采用不同的寄宿方式运行于不同的应用程序中。如果采用Web Host的方式将定义Web API寄宿于一个Web应用之中,其实最终的URL路由还是通过ASP.NET本身的路由系统完成的,那么两个路由系统之间是如何衔接在一起的呢?。[本文已经同步到《How ASP.NET Web API Works?》]

目录
一、HostedHttpRoute
二、HttpWebRoute
三、HostedHttpRouteCollection
四、HttpControllerRouteHandler
五、HttpControllerHandler

前文我们谈到包括路由注册在内的对整个ASP.NET Web API管道的配置是通过HttpConfiguration来完成的。对于Web Host这种寄宿方式,这么一个HttpConfiguration可以通过静态类型GlobalConfiguration来获取。如下面的代码片断所示,GlobalConfiguration具有一个静态只读属性Configuration,它返回的正式我们用于配置的全局HttpConfiguration对象。

   1: public static class GlobalConfiguration

   2: {

   3:     //其他成员

   4:     private static Lazy<HttpConfiguration> _configuration = new Lazy<HttpConfiguration>(delegate {

   5:         HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));

   6:         //其他操作

   7:         return config;

   8:     });

   9:     

  10:     public static HttpConfiguration Configuration

  11:     {

  12:         get

  13:         {

  14:             return _configuration.Value;

  15:         }

  16:     }

  17: }

如上面的代码片断所示,GlobalConfiguration的Configuration属性采用了延迟加载的模式来设计的。从对字段_configuration的初始化代表我们可以看到:返回的HttpConfiguration包含的路由表的真实类型并不是我们上面介绍的HttpRouteCollection,而是一个叫做HostedHttpRouteCollection的类型。HostedHttpRouteCollection是一个定义在System.Web.Http.WebHost.Routing命名空间下的内部类型,它直接继承自HttpRouteCollection。包含在HostedHttpRouteCollection之中的Route的类型也不是HttpRoute,而是HostedHttpRoute。

一、HostedHttpRoute

与HostedHttpRouteCollection一样,HostedHttpRoute也是System.Web.Http.WebHost.Routing命名空间下的一个内部类型,它直接实现了接口IHttpRoute(而不是继承自HttpRoute)。HostedHttpRoute可以看成是对一个Route对象的封装,这个被封装的Route对象对应着只读属性OriginalRoute。实现在HostedHttpRoute之中的核心路由功能基本上是通过这个Route对象完成的,所以我们才说Web Host下的ASP.NET Web API的URL路由最终还是利用ASP.NET自身的路由系统实现的。

   1: internal class HostedHttpRoute : IHttpRoute

   2: {

   3:     public HostedHttpRoute(string uriTemplate, 

   4:     IDictionary<string, object> defaults, 

   5:     IDictionary<string, object> constraints, 

   6:     IDictionary<string, object> dataTokens, HttpMessageHandler handler);

   7:  

   8:     public IHttpRouteData GetRouteData(string rootVirtualPath, HttpRequestMessage request);

   9:     public IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values);

  10:  

  11:     public IDictionary<string, object>    Constraints { get; }

  12:     public IDictionary<string, object>    DataTokens { get; }

  13:     public IDictionary<string, object>    Defaults { get; }

  14:     public HttpMessageHandler             Handler { get; }

  15:     internal Route                        OriginalRoute { get; }

  16:     public string                         RouteTemplate { get; }

  17: }

在正常情况下,当我们调用HostedHttpRoute的GetRouteData或者GetVirtualPath方法的时候,当前HTTP上下文对象已经被方式表示当前HTTP请求的HttpRequestMessage的属性字典中,对应的Key为“MS_HttpContext”。HostedHttpRoute可以直接这个Key从通过HttpRequestMessage的Properties属性表示的属性字典中获取当前HTTP上下文。

对于GetRouteData方法来说,它会将此HTTP上下文作为参数调用通过属性OriginalRoute属性表示的Route对象的GetRouteData方法。如果返回一个具体的RouteData对象,它会被转换成一个具有如下定义的HostedHttpRouteData对象并返回。如果调用Route的GetRouteData方法返回Null,最终的返回结果自然为Null。对于返回的HostedHttpRouteData对象来说,其Route属性自然是对自身的引用,RouteData的Values属性直接作为HostedHttpRouteData对象的同名属性,而OriginalRouteData属性直接就是对该RouteData对象的引用。

   1: internal class HostedHttpRouteData : IHttpRouteData

   2: {

   3:     public HostedHttpRouteData(RouteData routeData);

   4:  

   5:     public IHttpRoute                      Route { get; }

   6:     public IDictionary<string, object>     Values { get; }

   7:     internal RouteData                     OriginalRouteData { get; }

   8: }

对于GetVirtualPath方法来说,逻辑稍微复杂一些。除了得到当前HTTP上下文之外,HostedHttpRoute还会通过调用扩展方法GetRouteData方法获取附加在HttpRequestMessage对象上的HttpRouteData对象。在这种情况下,得到的HttpRouteData实际上就是一个HostedHttpRouteData对象,所以它可以通过其OriginalRouteData属性得到原始的RouteData。随后HostedHttpRoute根据得到HTTP上下文和RouteData创建一个RequestContext对象,并将其作为参数调用Route对象的GetVirtualPath方法,传输的参数除了该RequestContext对象之外还有一个根据values参数创建的RouteValueDictionary对象。如果该方法调用返回一个具体的VirtualPathData对象,HostedHttpRoute会将其转换成一个具有如下定义的HostedHttpVirtualPathData对象,该对象的用于返回生成URL的VirtualPath属性自然对应于VirtualPathData的VirtualPath属性。倘若方法返回Null,那么最终返回的自然就是Null。

   1: internal class HostedHttpVirtualPathData : IHttpVirtualPathData

   2: {   

   3:     public HostedHttpVirtualPathData(VirtualPathData virtualPath, IHttpRoute httpRoute);

   4:    

   5:     public IHttpRoute     Route { get; private set; }

   6:     public string         VirtualPath { get; set; }

   7: }

上面介绍的关于HostedHttpRoute的两个方法GetRouteData和GetVirtualPath的逻辑基本上可以通过如下的代码片断来体现(真实的代码于此稍有不同)。

   1: internal class HostedHttpRoute : IHttpRoute

   2: {

   3:     //其他成员

   4:     public IHttpRouteData GetRouteData(string rootVirtualPath, HttpRequestMessage request)

   5:     {      

   6:         HttpContextBase httpContextBase;

   7:         if (!request.Properties.TryGetValue<HttpContextBase>("MS_HttpContext", out httpContextBase))

   8:         {

   9:             return null;

  10:         }    

  11:         RouteData routeData = this.OriginalRoute.GetRouteData(httpContextBase);

  12:         if (routeData != null)

  13:         {

  14:             return new HostedHttpRouteData(routeData);

  15:         }

  16:         return null;

  17:     }

  18:  

  19:     public IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values)

  20:     {       

  21:         HttpContextBase httpContextBase = request.GetHttpContext();

  22:         if (httpContextBase != null)

  23:         {

  24:             HostedHttpRouteData routeData = request.GetRouteData() as HostedHttpRouteData;

  25:             if (routeData != null)

  26:             {

  27:                 RequestContext requestContext = new RequestContext(httpContextBase, routeData.OriginalRouteData);

  28:                 VirtualPathData virtualPathData = this.OriginalRoute.GetVirtualPath(requestContext, new RouteValueDictionary(values));

  29:                 if (virtualPathData != null)

  30:                 {

  31:                     return new HostedHttpVirtualPathData(virtualPathData, routeData.Route);

  32:                 }

  33:             }

  34:         }

  35:         return null;

  36:     }

  37: }

 

二、HttpWebRoute

HostedHttpRoute的只读属性OriginalRoute在构造函数中初始化,其真实类型并非Route,而是它具有如下定义的子类HttpWebRoute。HttpWebRoute返回的是创建它的HttpRoute对象,在此情况下自然就是创建它的HostedHttpRoute对象。对于重写的GetRouteData和GetVirtualPath,如果HttpRoute属性类型为HostedHttpRoute(在此情况下此条件永远成立),它们会直接调用基类Route的同名方法。除此之外,HttpWebRoute还重写了用于检验约束的ProcessConstraint方法,在该方法中如果表示约束的constraint参数是一个HttpRouteConstraint对象(在此情况下此条件永远成立),它会根据HTTP上下文创建一个HttpRequestMessage对象,并将其作为参数传入HttpRouteConstraint对象的Match方法进行约束检验。

   1: internal class HttpWebRoute : Route

   2: {   

   3:     public HttpWebRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler, IHttpRoute httpRoute);

   4:    

   5:     public override RouteData GetRouteData(HttpContextBase httpContext);   

   6:     public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);

   7:  

   8:     protected override bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);

   9:  

  10:     public IHttpRoute HttpRoute { get; }

  11: }

到此为止,我们基本上可以清楚地了解到ASP.NET Web API路由系统在Web Host情况下是如何利用ASP.NET自身的路有系统实现URL路由的:ASP.NET Web API路由系统中的HostedHttpRoute对象通过创建ASP.NET路由系统的HttpWebRoute进行基于URL模板的路由解析,但是针对约束的检验依然是利用ASP.NET Web API路由系统中的HttpRouteConstraint来完成的。

三、HostedHttpRouteCollection

上面我们对ASP.NET Web API在Web Host下采用的路由类型HostedHttpRoute作了详细介绍,对于通过静态类型GlobalConfiguration的Configuration属性获取到的用于配置请求处理管道的HttpConfiguration对象,我们也指出通过其Routes属性返回的路由表类型是HostedHttpRouteCollection,但是依然没有回答:调用该对象的扩展方法MapHttpRoute进行路由影射时对应的HostedHttpRoute对象是如何创建并添加的?

在上面介绍HttpRouteCollection的扩展方法的时候提到过:该方法调用HttpRouteCollection的CreateRoute方法最终实现对HttpRoute的创建。HostedHttpRouteCollection就是通过重现该方法来创建HostedHttpRoute的,如下所示的代码片断体现了该方法的实现逻辑。

   1: internal class HostedHttpRouteCollection : HttpRouteCollection

   2: {

   3:     //其他成员

   4:     public override IHttpRoute CreateRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)

   5:     {

   6:         return new HostedHttpRoute(uriTemplate, defaults, constraints, dataTokens, handler);

   7:     }

   8: }

既然ASP.NET Web API在Web Host模式下依然是借助ASP.NET自身的路由系统实现URL路由,那么意味着当我们针对ASP.NET Web API进行路由映射的时候必须在ASP.NET路由系统的全局路由表中添加对一个继承自抽象类RouteBase的Route对象(而不是实现了接口IHttpRoute的HttpRoute对象)。说得更加具体一点,当我们针对一个HostedHttpRouteCollection对象调用其扩展方法MapHttpRoute方法的时候,创建的HostedHttpRoute对象必须被转换成一个Route对象。

通过上面的介绍,HostedHttpRoute对象实际上是对一个HttpWebRoute对象的封装,对应其OriginalRoute属性,最终被添加到ASP.NET全局路由表的就是这么一个HttpWebRoute对象。如下面的代码片断所示,HostedHttpRouteCollection具有一个RouteCollection类型的字段_routeCollection。通过前面GlobalConfiguration的定义我们知道,默认使用的HostedHttpRouteCollection是根据通过RouteTable的静态属性Routes表示的ASP.NET路由表创建的。

   1: internal class HostedHttpRouteCollection : HttpRouteCollection

   2: {

   3:     //其他成员

   4:     private readonly RouteCollection _routeCollection;

   5:  

   6:     public HostedHttpRouteCollection(RouteCollection routeCollection)

   7:     {    

   8:         this._routeCollection = routeCollection;

   9:     }

  10:  

  11:     public override void Add(string name, IHttpRoute route)

  12:     {

  13:         this._routeCollection.Add(name, 

route.ToRoute()

);

  14:     }

  15: }

HostedHttpRouteCollection重写了Add方法,它会将添加的HttpRoute对象转换成Route对象并添加到ASP.NET的全局路由表中。如果添加的HttpRoute是一个HostedHttpRoute对象,被“转换”后的Route对象就是通过其OriginalRoute属性表示的HttpWebRoute对象。

通过前面针对ASP.NET路由实现原理的介绍,我们知道整个路由系统的核心是一个叫做UrlRoutingModule的HttpModule,它通过注册HttpApplication的PostResolveRequestCache事件的注册实现了请求的拦截,并动态映射一个HttpHandler来实现对请求的处理和响应。通过UrlRoutingModule映射的HttpHandler来源于与当前请求匹配的Route对象。说得更加具体一点,Route利用通过其RouteHandler提供对应的HttpHandler用于处理与之匹配的请求。Web Host模式下的ASP.NET Web API使用的Route类型为HttpWebRoute,它的RouteHandler是一个类型为System.Web.Http.WebHost.HttpControllerRouteHandler的对象,而后者提供的HttpHandler类型为System.Web.Http.WebHost.HttpControllerHandler。

四、HttpControllerRouteHandler

通过上面的介绍我们知道ASP.NET Web API在Web Host下真正使用的Route是一个类型为HttpWebRoute的对象,而该对象被ASP.NET Web API路由系统下一个类型为HostedHttpRoute的对象封装,那么HttpWebRoute最终用于处理与之匹配的请求的HttpHandler是什么呢?

   1: internal class HostedHttpRoute : IHttpRoute

   2: {

   3:     //其他成员

   4:     public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults,     IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)

   5:     {

   6:         //其他操作

   7:         this.OriginalRoute = new HttpWebRoute(uriTemplate, routeDefaults, 

   8:         routeConstraints, routeDataTokens, 

   9:         HttpControllerRouteHandler.Instance, this);   

  10:     }

  11: }

  12:  

  13: public class HttpControllerRouteHandler : IRouteHandler

  14: {   

  15:     protected HttpControllerRouteHandler();

  16:     protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext);

  17:     IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext);

  18:  

  19:     public static HttpControllerRouteHandler Instance { get; }

  20: }

HostedHttpRoute通过只读属性OriginalRoute引用被其封装的HttpWebRoute对象。如上面的代码片断所示,当HostedHttpRoute在对该属性进行初始化的时候为其指定的RouteHandler对象通过HttpControllerRouteHandler的静态属性Instance提供,实际上它以单例的模式(通过“延迟加载”的方式实现)提供一个HttpControllerRouteHandler对象。

五、HttpControllerHandler

RouteHandler之于Route的最终目的在于提供一个具体的HttpHandler来处理与之匹配的请求,HttpWebRoute的RouteHandler是一个HttpControllerRouteHandler对象,而后者具体提供一个怎样的HttpHandler呢?

   1: public class HttpControllerRouteHandler : IRouteHandler

   2: {

   3:     //其他成员   

   4:     protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)

   5:     {

   6:         return new HttpControllerHandler(requestContext.RouteData);

   7:     }

   8:  

   9:     IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)

  10:     {

  11:         return this.GetHttpHandler(requestContext);

  12:     }

  13: }

  14:  

  15: public class HttpControllerHandler : HttpTaskAsyncHandler

  16: {

  17:     //省略成员

  18: }

如上面的代码片断所示,HttpControllerRouteHandler实现的GetHttpHandler方法返回的是一个类型为HttpControllerHandler对象。HttpControllerHandler直接继承自HttpTaskAsyncHandler,所以它是一个异步模式的HttpHandler。ASP.NET Web API提供一个管道来处理请求和响应回复,毫不夸张地说:整个消息处理管道就是通过HttpControllerHandler这个HttpHandler创建的。

当我们将定义的Web API已Web Host模式部署在某个Web应用中并进行相应的路由影射,这些注册的HttpRoute(HostedHttpRoute)最终转换成ASP.NET全局路由表中的Route(HttpWebRoute)。ASP.NET路由系统对每个抵达的请求进行拦截,如果当前请求与路由表中的某个Route匹配,相应的路由数据被解析出来并保存在RequestContext中。

随后,ASP.NET路由系统的实现者UrlRoutingModule从匹配的Route中获取RouteHandler,这是一个HttpControllerRouteHandler对象,后者提供的HttpHandler(一个HttpControllerHandler对象)被UrlRoutingModule映射到当前请求。HttpHandler一旦成功映射,HttpControllerHandler将最终接管当前请求,而它会构造整个消息处理管道来处理这个请求并对请求予以响应。至于ASP.NET Web API的消息处理管道以及HttpControllerHandler对它的创建,我们会在后续的文章中进行详细介绍。

ASP.NET Web API路由系统:Web Host下的URL路由的更多相关文章

  1. Web Host下的URL路由

    Web Host下的URL路由 ASP.NET Web API提供了一个独立于执行环境的抽象化的HTTP请求处理管道,而ASP.NET Web API自身的路由系统也不依赖于ASP.NET路由系统,所 ...

  2. ASP.NET MVC Web API 学习笔记---Web API概述及程序示例

    1. Web API简单说明 近来很多大型的平台都公开了Web API.比如百度地图 Web API,做过地图相关的人都熟悉.公开服务这种方式可以使它易于与各种各样的设备和客户端平台集成功能,以及通过 ...

  3. 详解Net Core Web Api项目与在NginX下发布

    前言 本文将介绍Net Core的一些基础知识和如何NginX下发布Net Core的WebApi项目. 测试环境 操作系统:windows 10 开发工具:visual studio 2019 框架 ...

  4. [Web API] 如何让 Web API 统一回传格式以及例外处理[转]

    [Web API] 如何让 Web API 统一回传格式以及例外处理 前言 当我们在开发 Web API 时,一般的情况下每个 API 回传的数据型态或格式都不尽相同,如果你的项目从头到尾都是由你一个 ...

  5. [Web API] 如何让 Web API 统一回传格式以及例外处理

    [Web API] 如何让 Web API 统一回传格式以及例外处理 前言 当我们在开发 Web API 时,一般的情况下每个 API 回传的数据型态或格式都不尽相同,如果你的项目从头到尾都是由你一个 ...

  6. Web API 2 入门——Web API 2(C#)入门(谷歌翻译)

    ASP.NET Web API 2(C#)入门 在这篇文章中 本教程中使用的软件版本 创建一个Web API项目 添加模型 添加控制器 使用Javascript和jQuery调用Web API 运行应 ...

  7. ASP.NET Web API 框架研究 Web Host模式路由及将请求转出到消息处理管道

    Web Host 模式下的路由本质上还是通过ASP.NET 路由系统来进行路由的,只是通过继承和组合的方式对ASP.NET路由系统的内部的类进行了一些封装,产生自己专用一套类结构,功能逻辑基本都是一样 ...

  8. ASP.NET Web API 框架研究 Web Host模式下的消息处理管道

    寄宿的作用是开启一个进程为Web API提供一个运行环境以解决持续监听.请求监听和响应回复,即将接收到的请求转换成HttpRequestMessage对象传入管道,并将管道生成并经过处理后的HttpR ...

  9. ASP.NET Web API 框架研究 Self Host模式下的消息处理管道

    Self Host模式下的ASP.NET Web API与WCF非常相似,都可以寄宿在任意类型的托管应用程序中,宿主可以是Windows Form .WPF.控制台应用以及Windows Servic ...

随机推荐

  1. 使用Redis的INCR、Hsetnx、Hincrby的命令生成序列号

    Redis INCR命令 用于由一个递增key的整数值.如果该key不存在,它被设置为0执行操作之前.如果key包含了错误类型的值或包含不能被表示为整数,字符串,则返回错误.该操作被限制为64位带符号 ...

  2. 命名规范(数据库,c#)

    Ⅰ.  Naming Conventions 1. Table Naming Rule 1a ( Prefix) 新加的Table要加上適當的前缀 e.g.  mUsr, eTxn, tmpRolle ...

  3. C#解析json文件的方法

    C# 解析 json JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的 ...

  4. python 引用传递与值传递

    https://taizilongxu.gitbooks.io/stackoverflow-about-python/content/16/README.html 1.也就是如果传可变对象,就是引用传 ...

  5. linux安装oracle11g

    准备oracle安装文件 Oracle11gR2包含两个文件linux_11gR2_database_1of2.zip和linux_11gR2_database_2of2.zip,将这两个文件通过SS ...

  6. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  7. js基础3

    内置对象 内置对象就是JS提供的一套工具箱,我们通过内置可以很方便的进行一些操作. Math对象: Math.ceil() //向上取整,天花板 Math.floor() //向下取整,地板 Math ...

  8. postman发送带cookie的http请求

    1:需求:测试接口的访问权限,对于某些接口A可以访问,B不能访问. 2:问题:对于get请求很简单,登录之后,直接使用浏览器访问就可以: 对于post请求的怎么测试呢?前提是需要登录态,才能访问接口. ...

  9. CSS3属性transform详解之(旋转:rotate,缩放:scale,倾斜:skew,移动:translate)

    CSS3属性transform详解之(旋转:rotate,缩放:scale,倾斜:skew,移动:translate)   在CSS3中,可以利用transform功能来实现文字或图像的旋转.缩放.倾 ...

  10. ajax+div 代替iframe 学习尝试

    工作的时候遇到了所谓html内多tab展示的情况,主要是通过iframe来关联子页面: 不过也不知道从何时开始记得是说iframe不建议多用,所以想想,还是找找有没有其他方法(不应用于工作): 先说下 ...