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 }

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

你可能感兴趣的:(mvc)