zoukankan      html  css  js  c++  java
  • MVC4路由机制源码剖析

    首先我们从如何设置路由开始吧。 

     我们知道,在MVC4中注册路由,可以在App_Star文件夹中的RouteConfig(路由配置类)中注册路由,我们来看看

    RouteConfig

    1  public class RouteConfig
    2 {
    3    public RouteConfig();
    4    public static void RegisterRoutes(RouteCollection routes);
    5  }

    可以直接使用其中的静态方法RegisterRoutes对路由进行注册。将路由信息内容存放在路由集合

    RouteCollection

    public class RouteCollection : Collection<RouteBase>
    {
         public void Ignore(string url, object constraints);
         public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens);
    }
    

     RouteCollection 是一个RouteBase类型的集合,我们来分析一下两个比较重要的方法,一个是Ignore,一个是MapPageRoute,前者是用于定义不需要检查是否匹配路由的URL,后者是注册路由信息。

    我们来仔细分析这两个的实现。

    Ignore方法:

     1 public void Ignore(string url, object constraints)
     2 {
     3     if (url == null)
     4     {
     5         throw new ArgumentNullException("url");
     6     }
     7     IgnoreRouteInternal item = new IgnoreRouteInternal(url) {
     8         Constraints = new RouteValueDictionary(constraints)
     9     };
    10     base.Add(item);
    11 }

    Ignore方法创建了一个IgnoreRouteInternal对象,该对象看名称也应该知道了吧,忽略路由信息类,我们来看看其构造函数内部是怎样的。

    public IgnoreRouteInternal(string url) : base(url, new StopRoutingHandler())
    {
    }
    

     调用了父类,也就是Route的一个构造方法,忘了说了,IgnoreRouteInternal是Route的派生子类。我们仔细看看参数,有一个 StopRoutingHandler实例对象。

    这是一个什么对象呢?StopRoutingHandler继承自IRouteHandler接口,使用StopRoutingHandler的方式可以确保忽略通过路由的请求。

    IgnoreRouteInternal item = new IgnoreRouteInternal(url) 只是间接的调用了Route的构造器,所以,如果想忽略某些url不通过路由访问,其实我们也可以这样做:

     1 public static void RegisterRoutes(RouteCollection routes)
     2 {
     3   routes.IgnoreRoute("{resource}.axd/{*pathInfo}");    //使用IgnoreRoute方法进行排除
     4 
     5     routes.Add(new Route            //使用Route的构造器进行排除
     6     (
     7     “{resource}.axd/{*pathInfo}”,
     8     new StopRoutingHandler()
     9     ));
    10 }

    我们再来看看注册Ignore方法中IgnoreRouteInternal对象的Constraints属性所赋的值,是一个RouteValueDictionary对象,看看方法中使用到的构造器,我们进一步查看:

    1 public RouteValueDictionary(object values)
    2 {
    3     this._dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
    4     this.AddValues(values);
    5 }

    这是一个形参为object 的构造器,看见此处你应该了解到了一点什么吧?是的,可以使用匿名类。

    1 var routeConstraint = new RouteValueDictionary(
    2                 new { Controller = "[0-4]", Action = "[5-8]" }
    3                 );

          这是添加了一个路由约束,当然,匿名类设置成约束的时候,属性必须和当前设置的路由的参数要保持一致,那样才能进行验证操作。值得注意的是:排除路由的设置必须在注册路由之前实现,否则是没有效果的。

    再来看看MapPageRoute方法

     1 1 public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens)
     2  2 {
     3  3     if (routeUrl == null)
     4  4     {
     5  5         throw new ArgumentNullException("routeUrl");
     6  6     }
     7  7     Route item = new Route(routeUrl, defaults, constraints, dataTokens, new PageRouteHandler(physicalFile, checkPhysicalUrlAccess));
     8  8     this.Add(routeName, item);
     9  9     return item;
    10 10 }

    内部是根据提供的参数创建了一个Route对象,再添加到当前的路由集合中。

    当然,注册路由我们也可以通过路由表来进行注册,我们来看看RouteTable

    public class RouteTable
    {
        // Fields
        private static RouteCollection _instance;
        // Methods
        static RouteTable();
        public RouteTable();
        // Properties
        public static RouteCollection Routes { get; }
    }

    RouteTable没什么特别,有一个类型为RouteCollection的Routes属性,用来存放当前所有的路由信息数据。

    可以通过调用RouteTable的Routes属性来对路由集合进行操作。

    好了,说了那么久,路由的操作就这样了,我们来看看今天的主角Route类型吧。

    Route:

     1 public class Route:RouteBase
     2 {
     3  public override RouteData GetRouteData(HttpContextBase httpContext);
     4      public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
     5      protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
     6      private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection);
     7  
     8      // Properties
     9      public RouteValueDictionary Constraints {get; set; }
    10      public RouteValueDictionary DataTokens {get; set; }
    11      public RouteValueDictionary Defaults { get; set; }
    12      public IRouteHandler RouteHandler {  get; set; }
    13      public string Url { get; set; }
    14 }

    Route继承于RouteBase,其中GetRouteData 和GetVirtualPath方法,是重写父类RouteBase的两个方法,前者得到的是一个RouteData路由对象,而后者则是得到VirtualPathData虚拟路径对象,还有两个ProcessConstraint方法,用来处理约束。提供了一个protected虚方法用于子类重写。

    Route有五个属性,Constraints,表示为路由设置的约束;DataTokens,也就是路由信息提供的自定义变量;Defaults,路由的默认值。RouteHandler,处理请求路由的对象.

    就拿那几个重写的方法开始讲起吧:

    首先是:GetRouteData方法

     1 public override RouteData GetRouteData(HttpContextBase httpContext)
     2 {
     3     string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
     4     RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
     5     if (values == null)
     6     {
     7         return null;
     8     }
     9     RouteData data = new RouteData(this, this.RouteHandler);
    10     if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
    11     {
    12         return null;
    13     }
    14     foreach (KeyValuePair<string, object> pair in values)
    15     {
    16         data.Values.Add(pair.Key, pair.Value);
    17     }
    18     if (this.DataTokens != null)
    19     {
    20         foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
    21         {
    22             data.DataTokens[pair2.Key] = pair2.Value;
    23         }
    24     }
    25     return data;
    26 }
    27 
    28  

    方法内部调用了ParsedRoute对象的Match方法,其功能是将URL(即virtualPath)解析为一个RouteValueDictionary.

    接着调用ProcessConstraints方法,挨个对URL遍历验证,我们来看看此方法。

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

     遍历了约束集合,每个处理约束调用的还是当前类Route的另一个ProcessConstraint方法。

       Route的GetRouteData方法,处理完约束后,遍历将自定义变量添加到当前的DataTokens属性中。

    再来看看GetVirtualPath方法

     1 public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
     2 {
     3     BoundUrl url = this._parsedRoute.Bind(requestContext.RouteData.Values, values, this.Defaults, this.Constraints);
     4     if (url == null)
     5     {
     6         return null;
     7     }
     8     if (!this.ProcessConstraints(requestContext.HttpContext, url.Values, RouteDirection.UrlGeneration))
     9     {
    10         return null;
    11     }
    12     VirtualPathData data = new VirtualPathData(this, url.Url);
    13     if (this.DataTokens != null)
    14     {
    15         foreach (KeyValuePair<string, object> pair in this.DataTokens)
    16         {
    17             data.DataTokens[pair.Key] = pair.Value;
    18         }
    19     }
    20     return data;
    21 }

    首先调用的是ParseRoute的Bind方法,Bind的作用是根据几个RouteValueDictionary集合构造一个URL. 接着便是执行对URL和路由约束进行处理的方法ProcessConstraints方法。之后便是创建VirtualPathData对象,将当前DataTokens自定义变量集合的值,遍历,复制到VirtualPathData对象的DataTokens中,返回一个VirtualPathData对象。

    RouteBase

    讲了这几个方法我们再回头来看看Route的基类RouteBase:

    1 publicabstractclassRouteBase
    2 {
    4     public abstract RouteData GetRouteData(HttpContextBase httpContext);
    5     public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
    6    public bool RouteExistingFiles{get;set;}
    7 
    8 }

    RouteBase 有两个抽象方法,前者返回RouteData对象,后者返回的是VirtualPathData对象。还有一个属性:RouteExistingFiles。该属性默认设置为false,当设置为true的时候,将会与现有文件匹配的URL注册到路由中.

     RouteData:

     1 public class RouteData
     3 {
     5     public RouteValueDictionary DataTokens {get; }
     6     public RouteBase Route {  get; set; }
     7     public IRouteHandler RouteHandler { get;  set; }
     8     public RouteValueDictionary Values { get; }
     9 
    10 }

    RouteData对象有几个个属性:

    DataTokens:返回的类型为RouteValueDictionary,在上文中也细讲过,该属性用来存放路由信息中,传递过来的自定义变量集合。

    Route:返回类型是:RouteBase,该属性得到的是一个路由对象

    Values:返回的类型也是RouteValueDictionary,该属性用来保存路由的URL参数和默认值。

    再来看看VirtualPathData对象:

    VirtualPathData

    1 public class VirtualPathData
    2 {
    3   public RouteValueDictionary DataTokens{get;}
    4   public RouteBase Route{get;set;}
    5   public string VirtualPath{get;set;}
    6 }

    其中有一个VirtualPath属性,这个属性也不特殊,是用来设置和保存根据路由生成的URL。

    也许你仍然对一个类型不是很理解,也很感兴趣,那接下来我们就来看看吧。

    RouteValueDictionay:

     1 public class RouteValueDictionary : IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable
     2 {
     3     // Fields
     4     private Dictionary<string, object> _dictionary;
     5 
     6     // Methods
     7     public RouteValueDictionary();
     8     public RouteValueDictionary(IDictionary<string, object> dictionary);
     9     public RouteValueDictionary(object values);
    10     public void Add(string key, object value);
    11     private void AddValues(object values);
    12     public void Clear();
    13     public bool ContainsKey(string key);
    14     public bool ContainsValue(object value);
    15     public Dictionary<string, object>.Enumerator GetEnumerator();
    16     public bool Remove(string key);
    17     void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item);
    18     bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item);
    19     void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex);
    20     bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item);
    21     IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator();
    22     IEnumerator IEnumerable.GetEnumerator();
    23     public bool TryGetValue(string key, out object value);
    24 
    25     // Properties
    26     public int Count { get; }
    27     public object this[string key] { get; set; }
    28     public Dictionary<string, object>.KeyCollection Keys { get; }
    29     bool ICollection<KeyValuePair<string, object>>.IsReadOnly { get; }
    30     ICollection<string> IDictionary<string, object>.Keys { get; }
    31     ICollection<object> IDictionary<string, object>.Values { get; }
    32     public Dictionary<string, object>.ValueCollection Values { get; }
    33 }

    这是一个字典集合类型,也没什么特殊,在路由设置中,通常运用于约束,默认值,自定义变量集合等等。

  • 相关阅读:
    小毛病,大问题
    [zz]Libvirt 虚拟化库剖析
    libvirt XML 学习笔记
    Ubuntu 10.04下使用 libvirt 创建KVM虚拟机
    [zz]使用libvirt管理kvm虚拟机(更新中)
    [zz]LXC:Linux 容器工具
    一些比较好的URL
    [zz]Libvirt XML学习笔记
    [zz]一些KVM xml 例子
    [zz]kvm环境使用libvirt创建虚拟机
  • 原文地址:https://www.cnblogs.com/yangda/p/2987186.html
Copyright © 2011-2022 走看看