ASP.NET MVC 的路由实际上是建立在 ASP.NET 的路由系统之上的.

MVC 路由注册通常是这样的:

RouteTable 是一个全局路由表, 它的 Routes 静态属性是一个 RouteCollection 类型的实例,而 RouteCollection 是一个继承自 Collection<RouteBase> 的子类, RouteBase 是 ASP.NET 路由系统定义的基类

.

RouteBase 有一个唯一的实现类:

当我们通过如下方法注册一个路由时:

实际是向全局路由表中添加了一个 Route 类型的实例,部分源码如下:

    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
{
......
Route route = new Route(url, (IRouteHandler) new MvcRouteHandler())
{
    ......
};
    ......
routes.Add(name, (RouteBase) route);
return route;
}

从源码中我们可以看到,添加 Route 对象的时候,直接传入了一个 MvcRouteHandler 类型的实例.

我们知道, ASP.NET 的路由系统对路由的解析是通过一个注册的 HttpModule 对象实现对请求的拦截,然后为当前 Http 上下文动态映射一个 HttpHandler 对象, 而这个  HttpHandler 对象会接管对当前请求的处理并最终对请求予以响应.

这个注册的 HttpModule 对象的类型叫做 UrlRoutingModule .

我们可以在ASP.NET 的全局系统配置文件中找到它:

     <httpModules>
        ......
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule"/>
    ......
</httpModules>

该类型在 PostResolveRequestCache 事件实现对请求的拦截:

在拦截时,它做了这么几件事(部分源码省略):

    public virtual void PostResolveRequestCache(HttpContextBase context)
{
RouteData routeData = this.RouteCollection.GetRouteData(context);
IRouteHandler routeHandler = routeData.RouteHandler;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
context.RemapHandler(httpHandler);
}

1.遍历所有注册的路由,也就是所有添加到全局路由表中的 Route 类型的实例,通过调用它们的 GetRouteData 方法,拿到第一个匹配的 RouteData (路由数据);

2.拿到路由数据中的 RouteHandler 对象, 其实就是 MvcRouteHandler 类型的实例;

3.调用 MvcRouteHandler 的 GetHttpHandler 方法,拿到 HttpHandler.

MvcRouteHandler 的 GetHttpHandler 方法源码如下:

    protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
return (IHttpHandler) new MvcHandler(requestContext);
}

可以看到,直接 new 了一个 MvcHandler 类型的实例,

最终,请求转交给这个 MvcHandler 类型的实例处理.

ASP.NET Web API 是怎么与 ASP.NET 路由系统接轨的呢?

我们知道, ASP.NET 的路由系统对路由的解析是通过一个注册的 HttpModule 对象实现对请求的拦截,然后为当前 Http 上下文动态映射一个 HttpHandler 对象, 而这个  HttpHandler 对象会接管对当前请求的处理并最终对请求予以响应.

这一条不仅对 MVC 适用, 对 Web API 同样适用,因为他俩都是借助于 ASP.NET 的路由系统.

区别在于 HttpHandler 的类型不一样而已.

ASP.NET Web API 注册路由的代码通常是这样的:

            config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
}
);

config.Routes 是一个 HttpRouteCollection 类型的实例,并且是只读的.

只读的,就意味着只能在该实例所属的类的构造函数中初始化.

我们知道,这个 config 是 HttpConfiguration 类型,它在 GlobalConfiguration 类中初始化.

在它的初始化代码中,我们可以看到:

        private static Lazy<HttpConfiguration> CreateConfiguration()
{
return new Lazy<HttpConfiguration>(() =>
{
HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));
          ......return config;
});
}

HttpConfiguration 实际是对 HostedHttpRouteCollection 的封装,而后者是对 RouteTable.Route 的封装. 即 ASP.NET 全局路由表的封装.

所以说, HttpConfiguration 类型封装了 ASP.NET 的全局路由表. 它的 Routes 属性的实际类型是  HostedHttpRouteCollection

我们再回头看 config.Routes.MapHttpRoute 方法 , 也就是 HostedHttpRouteCollection 类型的 MapHttpRoute 方法:

        public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler)
{
......
IHttpRoute route = routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: null, handler: handler);
routes.Add(name, route);
return route;
}

很简单,创建了一个路由,然后添加它.

我们继续查看 HostedHttpRouteCollection 类型的 CreateRoute 方法:

    public override IHttpRoute CreateRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
{
    ......
    return (IHttpRoute) new HostedHttpRoute(uriTemplate, defaults, constraints, dataTokens, handler);
}

返回了一个 HostedHttpRoute 类型的实例.

我们可以把这个方法 和 上面 MVC 的 MapRoute 方法做比较:

MVC:

    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
{
......
Route route = new Route(url, (IRouteHandler) new MvcRouteHandler())
{
    ......
};
    ......
routes.Add(name, (RouteBase) route);
return route;
}

是不是非常像!不同的只是 MVC  new 的路由对象是 Route 类型,而 Web API new 的路由对象是 HostedHttpRoute 类型.

讲到这里,其实 ASP.NET Web API 的路由系统还没有和 ASP.NET 的路由系统衔接起来,它们二者的关系仅仅体现在下面这句话:

HttpConfiguration 实际是对 HostedHttpRouteCollection 的封装,而后者是对 RouteTable.Route 的封装. 即 ASP.NET 全局路由表的封装.

但是,当 HostedHttpRoute 创建后,调用 HostedHttpRouteCollection 的 Add 方法添加时,衔接就真正开始了:

        public override void Add(string name, IHttpRoute route)
{
_routeCollection.Add(name, route.ToRoute());
}
 

_routeCollection 是 RouteCollection 类型,没看错,就是 ASP.NET 路由系统的 RouteCollection .

所以,这句代码实际是向 ASP.NET 路由系统的路由集合中添加路由,目的就是为了让 UrlRoutingModule 能够拦截到匹配了 Web API 注册的路由的请求.

但是,问题来了,从上面 MVC 的讲解中我们知道, ASP.NET 路由系统的 RouteCollection 是一个继承自 Collection<RouteBase> 的子类, RouteBase 是 ASP.NET 路由的基类,

而 HostedHttpRoute 是实现了 IHttpRoute 接口的实例,

IHttpRoute  和 RouteBase 风马牛不相接啊!

所以,添加时,Web API 通过 HostedHttpRoute  的 ToRoute 方法,将自己转成了 RouteBase 类型!!

这个转化非常简单:

 public static Route ToRoute(this IHttpRoute httpRoute)
{
       ......
HostedHttpRoute hostedHttpRoute = httpRoute as HostedHttpRoute;
if (hostedHttpRoute != null)
{
return hostedHttpRoute.OriginalRoute;
}
       ......
}

问题又来了, HostedHttpRoute 类型的 OriginalRoute 是个什么鬼?当然,肯定是个 Route  类型,也就是说,它是一个 ASP.NET 路由系统定义的 Route 类型.那它是怎么来的呢?

我们知道,在 Web API 注册路由时, MapHttpRoute 内部创建了一个 HostedHttpRoute 类型的实例,并且是直接 new 的.

那么我们去看看 HostedHttpRoute 的构造函数:

        public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
{
......
OriginalRoute = new HttpWebRoute(uriTemplate, routeDefaults, routeConstraints, routeDataTokens, HttpControllerRouteHandler.Instance, this);
......
}

OriginalRoute 原来是一个 HttpWebRoute 类型,而 HttpWebRoute 则是 ASP.NET 路由系统定义的 Route 类型的子类.

并且,创建 HttpWebRoute 类型的实例时,传入了一个 ASP.NET 路由系统定义的 IRouteHandler 类型的实例 : HttpControllerRouteHandler.Instance

而 HttpControllerRouteHandler 的 GetHttpHandler 方法如下:

        /// <summary>
/// Provides the object that processes the request.
/// </summary>
/// <param name="requestContext">An object that encapsulates information about the request.</param>
/// <returns>
/// An object that processes the request.
/// </returns>
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new HttpControllerHandler(requestContext.RouteData);
}

返回了一个 HttpControllerHandler 类型的实例.

HttpControllerHandler 类型的XML注释则非常清晰的解释了它的作用:

/// 用于将 ASP.NET 请求传递给管道并写回结果。</summary>

这里说的管道,自然就是 Web API 的消息处理管道了.

总结:

ASP.NET MVC 和 ASP.NET Web API 都是通过 UrlRoutingModule ,在 PostResolveRequestCache 事件实现对请求的拦截.

拦截后,通过对HTTP上下文,路由等一系列处理后,

MVC 创建了 MvcHandler 进行具体的请求处理及响应;

Web API 创建了 HttpControllerHandler 进行具体的请求处理及响应.

ASP.NET MVC , ASP.NET Web API 的路由系统与 ASP.NET 的路由系统是怎么衔接的?的更多相关文章

  1. 如何将一个 ASP.NET MVC 4 和 Web API 项目升级到 ASP.NET MVC 5 和 Web API 2

    ----转自微软官网www.asp.net/mvc/ ASP.NET MVC 5 和 Web API 2 带来的新功能,包括属性路由. 身份验证筛选器,以及更多的主机.请参阅http://www.as ...

  2. ASP.NET MVC View 和 Web API 的基本权限验证

    ASP.NET MVC 5.0已经发布一段时间了,适应了一段时间,准备把原来的MVC项目重构了一遍,先把基本权限验证这块记录一下. 环境:Windows 7 Professional SP1 + Mi ...

  3. 在ASP.NET MVC中使用Web API和EntityFramework构建应用程序

    最近做了一个项目技术预研:在ASP.NET MVC框架中使用Web API和EntityFramework,构建一个基础的架构,并在此基础上实现基本的CRUD应用. 以下是详细的步骤. 第一步 在数据 ...

  4. MVC中使用Web API和EntityFramework

    在ASP.NET MVC中使用Web API和EntityFramework构建应用程序   最近做了一个项目技术预研:在ASP.NET MVC框架中使用Web API和EntityFramework ...

  5. 002.Create a web API with ASP.NET Core MVC and Visual Studio for Windows -- 【在windows上用vs与asp.net core mvc 创建一个 web api 程序】

    Create a web API with ASP.NET Core MVC and Visual Studio for Windows 在windows上用vs与asp.net core mvc 创 ...

  6. ASP.NET Core MVC中构建Web API

    在ASP.NET CORE MVC中,Web API是其中一个功能子集,可以直接使用MVC的特性及路由等功能. 在成功构建 ASP.NET CORE MVC项目之后,选中解决方案,先填加一个API的文 ...

  7. 【ASP.NET Web API教程】4.3 ASP.NET Web API中的异常处理

    原文:[ASP.NET Web API教程]4.3 ASP.NET Web API中的异常处理 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本系列教程,请先看前面的内 ...

  8. 【ASP.NET Web API教程】4.1 ASP.NET Web API中的路由

    原文:[ASP.NET Web API教程]4.1 ASP.NET Web API中的路由 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. ...

  9. Asp.net MVC集成Google Calendar API(附Demo源码)

    Asp.net MVC集成Google Calendar API(附Demo源码) Google Calendar是非常方便的日程管理应用,很多人都非常熟悉.Google的应用在国内不稳定,但是在国外 ...

  10. 从头编写 asp.net core 2.0 web api 基础框架 (1)

    工具: 1.Visual Studio 2017 V15.3.5+ 2.Postman (Chrome的App) 3.Chrome (最好是) 关于.net core或者.net core 2.0的相 ...

随机推荐

  1. 关于Python编码问题小记

    Python编码问题小记: 引子: 最近在复习redis,当我在获取redis的key的时候,redis 存储英文和汉字下面这个样子的,我知道汉字是用16进制的UTF-8编码了,然后突然很想搞清楚字符 ...

  2. [转]Linux 线程分离状态

    线程的分离与结合 在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached).一个可结合的线程能够被其他线程收回其资源和杀死:在被其他线程回收之前,它的存储器资源(如栈) ...

  3. JDK1.8源码TreeMap

    基于红黑树(Red-Black tree)的 NavigableMap 实现:键的排序由构造方法决定:自然排序,Comparator排序:非线程安全(仅改变与现有键关联的值不是结构上的修改):线程安全 ...

  4. github 优秀的开源项目

    https://github.com/wlcaption/AndroidMarket---- 这是手机应用商店,包含应用的下载,用户中心等内容 https://github.com/wlcaption ...

  5. MYSQL-重做系统恢复MYSQL过程

    记笔记是好习惯,记笔记是好习惯,记笔记是好习惯! 重要的事情说三遍. 说多了都是泪.第一次装MYSQL时候就遇到了很多问题,当时解决了忘记记录了.家里硬盘满了,于是买了个4T的硬盘重装系统.重装系统后 ...

  6. 简易博客[ html + css ] 练习

    1. 前言 通过使用 html + css 编写一个简易的博客作为入门练习 2. 代码及实现 2.1 目录结构 2.2 代码部分 <!DOCTYPE html> <html lang ...

  7. js权威指南---学习笔记01

    1.当函数赋值给对象的属性时,就变为了方法:2.被零整除不报错,只会返回无穷大(Infinity)或者负无穷大.例外:零除以零等于非数字(NaN).3.NaN与任何值都不相等! 4.Javascrip ...

  8. TypeScript的配置文件 tsconfig.json

    //tsconfig.json指定了用来编译这个项目的根文件和编译选项 { "compilerOptions": { //compilerOptions:编译选项,可以被忽略,这时 ...

  9. python 统计MySQL大于100万的表

    一.需求分析 线上的MySQL服务器,最近有很多慢查询.需要统计出行数大于100万的表,进行统一优化. 需要筛选出符合条件的表,统计到excel中,格式如下: 库名 表名 行数 db1 users 1 ...

  10. Linux 系统目录结构和常用指令

    一.系统目录结构 /bin 经常使用的命令 /etc 所有系统管理所需的配置文件和子目录 /home 用户主目录 /usr 应用程序目录 /usr/bin 系统用户使用的应用程序 /usr/sbin ...