5.路由源码解读

前面学习如何使用路由,如果不看看底层原理,那么岂不是浑浑噩噩的?还是那一句话所有的扩展,都是基于源代码来的而不是百度(再说看源代码,也可以学习别人的一些设计方式)

我们都知道我们在Global.asax文件中调用了RouteConfig类中的RegisterRoutes方法,里面传递了一个参数RouteTable.Routes,那么这个是什么了,我们来看看源码:

namespace System.Web.Routing
{
      [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
      public class RouteTable
      {
          private static RouteCollection _instance = new RouteCollection();
          public static RouteCollection Routes
          {
              get
              {
                 return RouteTable._instance;
              }
           }
      }
}

我们可以看出,此方法返回一个RouteCollection实例,通过名字我们也可以知道,其实这个就是一个装路由的容器

那么我们在RouteConfig里面注册的路由,有时通过什么方式放入里面了?F12进入你们我们可以看到如下几个方法:

public static Route MapRoute(this RouteCollection routes, string name, string url);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults);
public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces);

我们看源码如下:

// System.Web.Mvc.RouteCollectionExtensions
public static Route MapRoute(this RouteCollection routes, string name, string url)
{
    return routes.MapRoute(name, url, null, null);
}

其实前面几个重载方法,都是调用最后一个方法,源代码如下:

// System.Web.Mvc.RouteCollectionExtensions
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 = RouteCollectionExtensions.CreateRouteValueDictionaryUncached(defaults),
        Constraints = RouteCollectionExtensions.CreateRouteValueDictionaryUncached(constraints),
        DataTokens = new RouteValueDictionary()
    };
    ConstraintValidation.Validate(route);
    if (namespaces != null && namespaces.Length > 0)
    {
        route.DataTokens["Namespaces"] = namespaces;
    }
    routes.Add(name, route);
    return route;
}

我们可以看到里面其中有二行核心代码:

Route route = new Route(url, new MvcRouteHandler())
....
routes.Add(name, route);

意思简单明了就是实例化一个路由,然后添加到我们上面说的RouteCollection中,Route源代码如下:

namespace System.Web.Routing
{
    [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
    public class Route : RouteBase
    {
        private const string HttpMethodParameterName = "httpMethod";

        private string _url;

        private ParsedRoute _parsedRoute;

        public RouteValueDictionary Constraints
        {
            get;
            set;
        }

        public RouteValueDictionary DataTokens
        {
            get;
            set;
        }

        public RouteValueDictionary Defaults
        {
            get;
            set;
        }

        public IRouteHandler RouteHandler
        {
            get;
            set;
        }

        public string Url
        {
            get
            {
                return this._url ?? string.Empty;
            }
            set
            {
                this._parsedRoute = RouteParser.Parse(value);
                this._url = value;
            }
        }

        public Route(string url, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.RouteHandler = routeHandler;
        }

        public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.Defaults = defaults;
            this.RouteHandler = routeHandler;
        }

        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.Defaults = defaults;
            this.Constraints = constraints;
            this.RouteHandler = routeHandler;
        }

        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.Defaults = defaults;
            this.Constraints = constraints;
            this.DataTokens = dataTokens;
            this.RouteHandler = routeHandler;
        }

        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
            RouteValueDictionary routeValueDictionary = this._parsedRoute.Match(virtualPath, this.Defaults);
            if (routeValueDictionary == null)
            {
                return null;
            }
            RouteData routeData = new RouteData(this, this.RouteHandler);
            if (!this.ProcessConstraints(httpContext, routeValueDictionary, RouteDirection.IncomingRequest))
            {
                return null;
            }
            foreach (KeyValuePair<string, object> current in routeValueDictionary)
            {
                routeData.Values.Add(current.Key, current.Value);
            }
            if (this.DataTokens != null)
            {
                foreach (KeyValuePair<string, object> current2 in this.DataTokens)
                {
                    routeData.DataTokens[current2.Key] = current2.Value;
                }
            }
            return routeData;
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            BoundUrl boundUrl = this._parsedRoute.Bind(requestContext.RouteData.Values, values, this.Defaults, this.Constraints);
            if (boundUrl == null)
            {
                return null;
            }
            if (!this.ProcessConstraints(requestContext.HttpContext, boundUrl.Values, RouteDirection.UrlGeneration))
            {
                return null;
            }
            VirtualPathData virtualPathData = new VirtualPathData(this, boundUrl.Url);
            if (this.DataTokens != null)
            {
                foreach (KeyValuePair<string, object> current in this.DataTokens)
                {
                    virtualPathData.DataTokens[current.Key] = current.Value;
                }
            }
            return virtualPathData;
        }

        protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            IRouteConstraint routeConstraint = constraint as IRouteConstraint;
            if (routeConstraint != null)
            {
                return routeConstraint.Match(httpContext, this, parameterName, values, routeDirection);
            }
            string text = constraint as string;
            if (text == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[]
                {
                    parameterName,
                    this.Url
                }));
            }
            object value;
            values.TryGetValue(parameterName, out value);
            string input = Convert.ToString(value, CultureInfo.InvariantCulture);
            string pattern = "^(" + text + ")$";
            return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
        }

        private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection)
        {
            if (this.Constraints != null)
            {
                foreach (KeyValuePair<string, object> current in this.Constraints)
                {
                    if (!this.ProcessConstraint(httpContext, current.Value, current.Key, values, routeDirection))
                    {
                        return false;
                    }
                }
                return true;
            }
            return true;
        }
    }
}

首先我们里面的代码什么的都不看,就看它的继承关系

public class Route : RouteBase

可以看出它继承于RouteBase,什么是Base,我就不说了哈,都懂的,源代码如下:

namespace System.Web.Routing
{
    [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
    public abstract class RouteBase
    {
        private bool _routeExistingFiles = true;

        public bool RouteExistingFiles
        {
            get
            {
                return this._routeExistingFiles;
            }
            set
            {
                this._routeExistingFiles = value;
            }
        }

        public abstract RouteData GetRouteData(HttpContextBase httpContext);

        public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
    }
}

通过这里其实我们就可以进行扩展了,就是我们创建一个类然后继承于RouteBase,实现里面的抽象方法,然后通过RouteCollection类的add方法把我们定义的路由添加进去。

可以看出里面的抽象包含二个参数,HttpContextBase  RequestContext,上下文代表着什么,代表着梦想,这里面基本上涵盖了请求的各种信息,也就是我们可以在这两个方法里面做很多很多的事

我们再来看看Add方法的源码:

// System.Web.Routing.RouteCollection
public void Add(string name, RouteBase item)
{
    if (item == null)
    {
        throw new ArgumentNullException("item");
    }
    if (!string.IsNullOrEmpty(name) && this._namedMap.ContainsKey(name))
    {
        throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("RouteCollection_DuplicateName"), new object[]
        {
            name
        }), "name");
    }
    base.Add(item);
    if (!string.IsNullOrEmpty(name))
    {
        this._namedMap[name] = item;
    }
    Route route = item as Route;
    if (route != null && route.RouteHandler != null)
    {
        TelemetryLogger.LogHttpHandler(route.RouteHandler.GetType());
    }
}

其中:name参数表示路由的名称(名称只能唯一哦),RouteBase参数表示接受RouteBase的派生类,也就是路由

其中核心代码如下:

base.Add(item);

这个是调用父类的Add方法,我们来看看源码:

。。。。打住不能看了,跑偏了,就当这个不重要,有兴趣的可以去看看

this._namedMap[name] = item;

这个嘛,我们来看看_namedMap是什么东东:

private Dictionary<string, RouteBase> _namedMap = new Dictionary<string, RouteBase>(StringComparer.OrdinalIgnoreCase);

前面一直说放入RouteCollection里面,一直不明白(别说你们不想知道),原来是用一个字典存储的啊!!!!

如果还想更加了解里面的原理机制,可以查看https://www.cnblogs.com/wangiqngpei557/p/3379095.html

原文地址:https://www.cnblogs.com/zjdbk/p/10628368.html

时间: 2024-10-12 04:03:50

5.路由源码解读的相关文章

HttpClient 4.3连接池参数配置及源码解读

目前所在公司使用HttpClient 4.3.3版本发送Rest请求,调用接口.最近出现了调用查询接口服务慢的生产问题,在排查整个调用链可能存在的问题时(从客户端发起Http请求->ESB->服务端处理请求,查询数据并返回),发现原本的HttpClient连接池中的一些参数配置可能存在问题,如defaultMaxPerRoute.一些timeout时间的设置等,虽不能确定是由于此连接池导致接口查询慢,但确实存在可优化的地方,故花时间做一些研究.本文主要涉及HttpClient连接池.请求的参数

【Spark】SparkContext源码解读

SparkContext的初始化 SparkContext是应用启动时创建的Spark上下文对象,是进行Spark应用开发的主要接口,是Spark上层应用与底层实现的中转站(SparkContext负责给executors发送task). SparkContext在初始化过程中,主要涉及一下内容: SparkEnv DAGScheduler TaskScheduler SchedulerBackend SparkUI 生成SparkConf SparkContext的构造函数中最重要的入参是Sp

自动化WiFI钓鱼工具——WiFiPhisher源码解读

工具介绍 开源无线安全工具Wifiphisher是基于MIT许可模式的开源软件,运行于Kali Linux之上. github.com/sophron/wifiphisher 它能够对WPA加密的AP无线热点实施自动化钓鱼攻击,获取密码账户.由于利用了社工原理实施中间人攻击,Wifiphisher在实施攻击时无需进行暴力破解. 此外安利一个我们正在开发的项目,基于wifiphisher的校园网钓鱼工具,希望有小伙伴来一起玩耍:-P github.com/noScripts/Campus-Fake

QCustomplot使用分享(二) 源码解读

一.头文件概述 从这篇文章开始,我们将正式的进入到QCustomPlot的实践学习中来,首先我们先来学习下QCustomPlot的类图,如果下载了QCustomPlot源码的同学可以自己去QCustomPlot的目录下documentation/qcustomplot下寻找一个名字叫做index.html的文件,将其在浏览器中打开,也是可以找到这个库的类图.如图1所示,是组成一个QCustomPlot类图的可能组成形式. 一个图表(QCustomPlot):包含一个或者多个图层.一个或多个ite

vue源码解读预热-0

vueJS的源码解读 vue源码总共包含约一万行代码量(包括注释)特别感谢作者Evan You开放的源代码,访问地址为Github 代码整体介绍与函数介绍预览 代码模块分析 代码整体思路 总体的分析 从图片中可以看出的为采用IIFE(Immediately-Invoked Function Expression)立即执行的函数表达式的形式进行的代码的编写 常见的几种插件方式: (function(,){}(,))或(function(,){})(,)或!function(){}()等等,其中必有

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系

一般我们开发时,使用最多的还是@RequestMapping注解方式. @RequestMapping(value = "/", param = "role=guest", consumes = "!application/json") public void myHtmlService() { // ... } 台前的是RequestMapping ,正经干活的却是RequestCondition,根据配置的不同条件匹配request. @Re

jdk1.8.0_45源码解读——HashMap的实现

jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对的映射,允许多个null值和一个null键.但此类不保证映射的顺序,特别是它不保证该顺序恒久不变.  除了HashMap是非同步以及允许使用null外,HashMap 类与 Hashtable大致相同. 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能.迭代col

15、Spark Streaming源码解读之No Receivers彻底思考

在前几期文章里讲了带Receiver的Spark Streaming 应用的相关源码解读,但是现在开发Spark Streaming的应用越来越多的采用No Receivers(Direct Approach)的方式,No Receiver的方式的优势: 1. 更强的控制自由度 2. 语义一致性 其实No Receivers的方式更符合我们读取数据,操作数据的思路的.因为Spark 本身是一个计算框架,他底层会有数据来源,如果没有Receivers,我们直接操作数据来源,这其实是一种更自然的方式

jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现

jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现 一. Set架构 如上图: (01) Set 是继承于Collection的接口.它是一个不允许有重复元素的集合.(02) AbstractSet 是一个抽象类,它继承于AbstractCollection.AbstractCollection实现了Set中的绝大部分函数,为Set的实现类提供了便利.(03) HastSet 和 TreeSet 是Set的两个实现类.        HashSet依赖于HashMa