微软官网对这个类的说明是:提供用于定义路由及获取路由相关信息的属性和方法。这个说明已经很简要的说明了这个类的作用,下面我们就从源码的角度来看看这个类的内部是如何工作的。

     public class Route : RouteBase {

         private string _url;
private ParsedRoute _parsedRoute; public Route(string url, IRouteHandler routeHandler) {
Url = url;
RouteHandler = routeHandler;
} public RouteValueDictionary Defaults {
get;
set;
} public IRouteHandler RouteHandler {
get;
set;
} public string Url {
get {
return _url ?? String.Empty;
}
set {
_parsedRoute = RouteParser.Parse(value);
_url = value;
}
} public override RouteData GetRouteData(HttpContextBase httpContext) { string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring() + httpContext.Request.PathInfo; RouteValueDictionary values = _parsedRoute.Match(requestPath, Defaults); if (values == null) {
return null;
} RouteData routeData = new RouteData(this, RouteHandler); if (!ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) {
return null;
}
foreach (var value in values) {
routeData.Values.Add(value.Key, value.Value);
} if (DataTokens != null) {
foreach (var prop in DataTokens) {
routeData.DataTokens[prop.Key] = prop.Value;
}
} return routeData;
}
}

Route部分源码

  上面是类的核心代码,如果在这里对所有的代码都进行列出或学习,则无法突出重点。

  主要包括构造函数,几个属性,一个方法。我们从这个类的对象被创建的地方开始,逐步深入理解。

  这个方法会在应用程序启动时被第一个调用,在这里,我们注册了全局的路由信息。

public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
       //将全局变量 RouteTable.Routes作为参数传递进方法里,然后向这个全局变量添加数据。系统定义的路由数据都被存放在这里变量里
       RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}

2从RouteConfig这个类开始,这是创建Mvc项目时,系统自动替我们添加的类,也是很重要的一个类.

  public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}

RouteConfig-开始的地方

  在这里类里,系统替我们添加了默认的URL路由并添加了默认值。

  这里调用了RouteCollection对象的MapRoute方法进行路由的映射,但是查看RouteCollection这个类,并不能找到MapRoute这个方法。

  这个方法的实现,是通过RouteCollection的一个拓展方法进行实现。

 public static class RouteCollectionExtensions
{
public static void IgnoreRoute(this RouteCollection routes, string url, object constraints)
{
if (routes == null)
{
throw new ArgumentNullException("routes");
}
if (url == null)
{
throw new ArgumentNullException("url");
} IgnoreRouteInternal route = new IgnoreRouteInternal(url)
{
Constraints = CreateRouteValueDictionaryUncached(constraints)
}; ConstraintValidation.Validate(route); routes.Add(route);
} public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
{
if (routes == null)
{
throw new ArgumentNullException("routes");
}
if (url == null)
{
throw new ArgumentNullException("url");
} Route route = new Route(url, new MvcRouteHandler())
{
Defaults = CreateRouteValueDictionaryUncached(defaults),
Constraints = CreateRouteValueDictionaryUncached(constraints),
DataTokens = new RouteValueDictionary()
}; ConstraintValidation.Validate(route); if ((namespaces != null) && (namespaces.Length > ))
{
route.DataTokens[RouteDataTokenKeys.Namespaces] = namespaces;
} routes.Add(name, route); return route;
}
}

RouteCollectionExtensions部分代码

  在上面的MapRoute方法里,类Route的对象使用指定的值被创建。这也是在系统中,Route对象最初被创建的对象。在这里,路由被定义。

其中最核心的就是下面的代码,我们也将重点讲解下面的代码,看我们的路由信息时如何被定义的以及为什么这样定义。

             Route route = new Route(url, new MvcRouteHandler())
{
Defaults = CreateRouteValueDictionaryUncached(defaults),
Constraints = CreateRouteValueDictionaryUncached(constraints),
DataTokens = new RouteValueDictionary()
};

  在第一行中,给Route的构造方法传递了两个参数。

  第一个是url"{controller}/{action}/{id}"这样的值,这样的一个值,定义了系统能处理的路由模板,后面每当有请求被处理,

总是会将请求的http链接与这个路由模板进行匹配,看是否满足我们定义的模板。

  第二个参数则是实现了IRouteHandler接口的类MvcRouteHandler。使用该类对象的主要作用是创建实际处理请求的类MvcHandler对象。

关于这两个类的作用及地位会在其他随笔里介绍,这里仅作了解。

下面让我们进入类Route的内部,看构造函数里到底发生了什么。也就是本片文章一开始的那段代码。然后在返回看看对Defaults属性的赋值。

   public Route(string url, IRouteHandler routeHandler) {
Url = url;
RouteHandler = routeHandler;
}
  public string Url {
    get {
        return _url ?? String.Empty;
       }
    set {
        _parsedRoute = RouteParser.Parse(value);
        _url = value;
       }
  }

  在构造方法里,将传递进去的连个参数保存在两个属性里,而仅在Url属性中,对url路由模板做了特殊处理。所做的这些特殊处理,也是它可以匹配用户的

请求连接的关键。我将第10行使用的代码用源代码中拷贝了出来,新建了一个项目,进行测试。返回的_parsedRoute对象如下图所示。

这里主要使用了两个类,一个是ContentPathSegment,一个是SeparatorPathSegment(代表URL中的“/”分隔符)。重点解释ContentPathSegment类。上截图。

从截图中可以看出,这个类主要存储的是我们定义的url模板切割后子段值。

下面列出该类的具体代码,可以比较理解.

    //代表不是分隔符的网段。 它包含诸如文字和参数的子段。
internal sealed class ContentPathSegment : PathSegment
{
public ContentPathSegment(IList<PathSubsegment> subsegments)
{
Subsegments = subsegments;
} public bool IsCatchAll
{
get
{
//
return Subsegments.Any<PathSubsegment>(seg => (seg is ParameterSubsegment) && (((ParameterSubsegment)seg).IsCatchAll));
}
} public IList<PathSubsegment> Subsegments
{
get;
private set;
}
}

类ContentPathSegment

至此,Route类构造函数中的结束已经完毕,下面解释另外一个属性的赋值--Defaults。这是属性中存储的是我们给url路由模板指定的默认值,在http请求时,如果只输入了ip,没有指定控制器名称和action时,使用这里的默认值。

defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

Defaults = CreateRouteValueDictionaryUncached(defaults),

下面是经转化后返回的值的截图,该属性返回的是一个RouteValueDictionary字典。

至此,类Route的创建已经完成,该类的创建工作在应用程序启动时完成,且只会被初始化一次,然后保存在路由集合中。

有两个重点,1是路由模板经过处理后得到的_parsedRoute对象;2是传递的路由默认值。这两个对象是Route类的核心功能点。

下面,我们就从这个类的使用角度开始解析。UrlRoutingModule这个类是使用Route开始的地方

  public class UrlRoutingModule : IHttpModule {
private static readonly object _contextKey = new Object();
private static readonly object _requestDataKey = new Object();
private RouteCollection _routeCollection; public RouteCollection RouteCollection {
get {
if (_routeCollection == null) {
_routeCollection = RouteTable.Routes;
}
return _routeCollection;
}
set {
_routeCollection = value;
}
} protected virtual void Dispose() {
} protected virtual void Init(HttpApplication application) { if (application.Context.Items[_contextKey] != null) {
return; // already added to the pipeline
}
application.Context.Items[_contextKey] = _contextKey;
application.PostResolveRequestCache += OnApplicationPostResolveRequestCache;
} private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) {
HttpApplication app = (HttpApplication)sender;
HttpContextBase context = new HttpContextWrapper(app.Context);
PostResolveRequestCache(context);
} public virtual void PostResolveRequestCache(HttpContextBase context) {
// Match the incoming URL against the route table
RouteData routeData = RouteCollection.GetRouteData(context); // Do nothing if no route found
if (routeData == null) {
return;
} // If a route was found, get an IHttpHandler from the route's RouteHandler
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
SR.GetString(SR.UrlRoutingModule_NoRouteHandler)));
} if (routeHandler is StopRoutingHandler) {
return;
}
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
SR.GetString(SR.UrlRoutingModule_NoHttpHandler),
routeHandler.GetType()));
} if (httpHandler is UrlAuthFailureHandler) {
if (FormsAuthenticationModule.FormsAuthRequired) {
UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
return;
}
else {
throw new HttpException(, SR.GetString(SR.Assess_Denied_Description3));
}
} // Remap IIS7 to our handler
context.RemapHandler(httpHandler);
} #region IHttpModule Members
void IHttpModule.Dispose() {
Dispose();
} void IHttpModule.Init(HttpApplication application) {
Init(application);
}
#endregion
}

UrlRoutingModule部分代码

这是一个路由模块类,在网站启动时,会自动从配置文件中加载这个类。在类的Init方法中,会传递一个HttpApplication对象,然后会注册HttpApplication对象的PostResolveRequestCache事件,关于类HttpApplication

会在其他随笔中重点的讲解,这里仅做介绍。我们只需知道,每当一个请求到达时,HttpApplication对象的PostResolveRequestCache事件会被出发,进而调用类UrlRoutingModule的PostResolveRequestCache方法。

在这个方法里,会根据亲求的url链接,去匹配一个系统中已经定义的路由信息。

 RouteData routeData = RouteCollection.GetRouteData(context);

这是PostResolveRequestCache方法的第一行代码,从路由集合中获取一个路由数据。RouteCollection这个属性的真实值就是RouteTable.Routes,这其中的数据,就是本文上半部分介绍的Route被创建后存储的地方。RouteCollection中存储的就是一个个的Route对象,所以上面的一行代码,最终就是遍历这些Route对象,然后调用每一个对象的GetRouteData方法。

         public override RouteData GetRouteData(HttpContextBase httpContext) {
//解析传入的URL(我们修剪掉前两个字符,因为它们总是“〜/”)
//解析后的字符串类似这样 Home/Index、User/Info 这样的格式
string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring() + httpContext.Request.PathInfo;
       //将用户请求的url中的与控制器和Action相关的字符串与我们定义的路由信息进行匹配
       RouteValueDictionary values = _parsedRoute.Match(requestPath, Defaults);
       //如果没有匹配到数据,则返回空值
      if (values == null) {
return null;
}
       //说明请求的url解析成功,则新建一个路由数据对象,且将当前对象及当前对象的一个属性传递给构造函数
RouteData routeData = new RouteData(this, RouteHandler);
//将匹配到的路由信息添加到路由数据中
foreach (var value in values) {
routeData.Values.Add(value.Key, value.Value);
}
return routeData;
}

根据此RouteData对象可以获取一个实现了IRouteHandler接口的类(MvcRouteHandler)的对象,而该对象又可以获取一个实现了IHttpHanlder的类(MvcHandler)的对象,

该对象,是真正处理用于请求的对象,其他所做的一切都只是准备工作。

结束语:

  该类的作用第一是用来存储我们定义的基础路由信息,第二是用来匹配检测用户输入的URL,第三是保存一个实现了IRouteHandler接口的类对象。

深入源码解析类Route的更多相关文章

  1. .net core 源码解析-mvc route的注册,激活,调用流程(三)

    .net core mvc route的注册,激活,调用流程 mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活c ...

  2. dubbo源码解析-spi(3)

    前言 在上一篇的末尾,我们提到了dubbo的spi中增加了IoC和AOP的功能.那么本篇就讲一下这个增加的IoC,spi部分预计会有四篇,因为这东西实在是太重要了.温故而知新,我们先来回顾一下,我们之 ...

  3. Java集合---Array类源码解析

    Java集合---Array类源码解析              ---转自:牛奶.不加糖 一.Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Prim ...

  4. junit源码解析--核心类

    JUnit 的概念及用途 JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个开源的单元测试框架.它属于白盒测试,只要将待测类继承 TestCase 类,就可以利用 JUnit ...

  5. [Java源码解析] -- String类的compareTo(String otherString)方法的源码解析

    String类下的compareTo(String otherString)方法的源码解析 一. 前言 近日研究了一下String类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识,  ...

  6. Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取

    在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao ...

  7. 线程池 ThreadPoolExecutor 类的源码解析

    线程池 ThreadPoolExecutor 类的源码解析: 1:数据结构的分析: private final BlockingQueue<Runnable> workQueue;  // ...

  8. Mybatis源码解析(三) —— Mapper代理类的生成

    Mybatis源码解析(三) -- Mapper代理类的生成   在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...

  9. java.lang.Void类源码解析_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 在一次源码查看ThreadGroup的时候,看到一段代码,为以下: /* * @throws NullPointerEx ...

随机推荐

  1. 18 UI美化transition 图片过渡

    让两张图片在一定时间过渡 在工程文件res/drawable/transition文件 <?xml version="1.0" encoding="utf-8&qu ...

  2. UE4使用C++创建枚举变量适用于C++与蓝图

    这个月勉勉强强才写了一篇,都快月底了,都还没有写第二篇博客的冲动,证明这个月确实收获甚少,有点状态不佳,懒毒入骨啊.刚刚看了这个月其实已经写了三篇,然而事实是这博客还有另外一个人也在更新文章,博主并没 ...

  3. (NO.00004)iOS实现打砖块游戏(十):砖块!更多的砖块!

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 到目前为止游戏基本可玩,但是砖块数量是不变的,等玩家打光所有的砖 ...

  4. python使用h5py读取mat文件数据,并保存图像

    1 安装h5py sudo apt-get install libhdf5-dev sudo pip install h5py 假设你已经安装好python和numpy模块 2 读取mat文件数据 i ...

  5. 【Unity技巧】制作一个简单的NPC

    1. 写在前面 前几天看了cgcookie的一个教程,学习了下怎么根据已有人物模型制作一个仿版的NPC人物,感觉挺好玩的,整理一下放到博客里! 先看一下教程里面的最终效果. 是不是很像个幽灵~ 下面是 ...

  6. spring struts2 ibatis 框架结构图

    spring struts2 ibatis 框架结构图

  7. C++ Primer 有感(标准库set类型)

    set容器只是单纯的键的集合,键必须为一.set容器不支持下标操作,而且没有定义maped_type类型.在set容器中,value_type不是pair类型,而是与key_type类型相同的类型. ...

  8. Android利用ViewPager仿微信主界面-android学习之旅(78)

    首先是介绍ViewPager这个控件 ,这个控件需要pagerAdapter作为容器来提供数据,同时pagerAdapter的数据源是View数组 效果图如下 部分代码如下,实现如下的方法 mPage ...

  9. MinerConstanits.java 常量类

    MinerConstanits.java 常量类 package com.iteye.injavawetrust.miner; /** * 常量类 * @author InJavaWeTrust * ...

  10. Linux进程实践(5) --守护进程

    概述 守护进程是在需要在后台长期运行不受终端控制的进程,通常情况下守护进程在系统启动时自动运行,在服务器关闭的时候自动关闭:守护进程的名称通常以d结尾,比如sshd.xinetd.crond.atd等 ...