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 }

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

  • 相关阅读:
    PATA 1071 Speech Patterns.
    PATA 1027 Colors In Mars
    PATB 1038. 统计同成绩学生(20)
    1036. 跟奥巴马一起编程(15)
    PATA 1036. Boys vs Girls (25)
    PATA 1006. Sign In and Sign Out (25)
    读取web工程目录之外的图片并显示
    DOS命令
    java连接oracle集群
    servlet
  • 原文地址:https://www.cnblogs.com/yangda/p/2987186.html
Copyright © 2011-2022 走看看