首先我们从如何设置路由开始吧。
我们知道,在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 }
这是一个字典集合类型,也没什么特殊,在路由设置中,通常运用于约束,默认值,自定义变量集合等等。
本文详细介绍了MVC4框架中的路由配置过程,包括如何在RouteConfig类中注册路由、Ignore方法的应用、MapPageRoute方法的工作原理及Route类的实现细节。
1224

被折叠的 条评论
为什么被折叠?



