RouteBase
我们所说的Route泛指的是继承自抽象类RouteBase的某个类型对象。如下面的代码段所示,RouteBase具有两个返回类型分别为RouteData和VirtualPathData的方法GetRouteData和GetVirtualPath,它们分别体现了针对两个“方向”的路由。实现在GetRouteData方法中的路由解析是为了获取路由数据,而GetVirtualPath方法则通过路由解析生成一个完整的虚拟路径。
public abstract class RouteBase
{
public abstract RouteData GetRouteData(HttpContextBase httpContext);
public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
/// <summary>
/// 是否对现有的物理文件实施路由。
/// 默认为False
/// </summary>
public bool RouteExistingFiles { get; set; }
}
RouteBase定义了一个布尔类型的属性RouteExistingFiles,它表示是否对现有的物理文件实施路由。该属性的默认值为True,意味着默认情况下在我们的实例中通过地址/employees/hr/default.aspx是访问不到Default.aspx页面文件。
RouteData
如下代码段所示,RouteData具有一个类型为RouteBase的属性Route,该属性返回生成此RouteData的Route对象。
public class RouteData
{
public RouteData();
public RouteData(RouteBase route, IRouteHandler routeHandler);
public string GetRequiredString(string valueName);
public RouteBase Route { get; set; }
public IRouteHandler RouteHandler { get; set; }
public RouteValueDictionary DataTokens { get; set; }
public RouteValueDictionary Values { get; set; }
}
public class RouteValueDictionary:IDictionary<string,object>
{
//...
}
ASP.NET路由系统利用RouteData的Values和DataTokens属性来保存路由变量,字典元素的Key和Value分别表示变量的名称和值。存储与Values和DataTokens这两个属性中的路由变量的不同之处在于:前者是通过对请求URL进行解析得到的,后者则是直接附加到路由对象上的自定义变量。
RouteData通过其RouteHandler属性返回一个RouteHandler对象。RouteHandler用于提供最终处理请求的HttpHandler对象。所有的RouteHandler类型均实现了具有如下定义的IRouteHandler接口,HttpHandler的提供实现在它的GetHttpHandler方法中。我们可以在构造函数中对RouteData的RouteHandler属性进行初始化,也可以直接对这个属性赋值。
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
VirtualPathData
当RouteBase的GetVirtualPath方法被执行的时候,如果定义在路由模板中的变量与指定变量列表相匹配,它会使用指定的路由变量值去替换路由模板中对应的占位符并生成一个虚拟路径。生成的虚拟路径与Route对象最终被封装成一个VirtualPathData对象做为返回值。它们对应着VirtualPath和Route属性。VirtualPathData的DataToekns属性和RouteData的同名属性一样都是来源与附加到Route对象的自定义变量集合。
public class VirtualPathData
{
public VirtualPathData(RouteBase route,string virtualPath);
public RouteValueDictionary DataTokens { get; }
public RouteBase Route { get; set; }
public string VirtualPath { get; set; }
}
RouteBase的GetVirtualPath方法具有一个类型为RequestContext的参数,一个RequestContext对象表示针对某个请求的上下文。从如下的代码片段不难看出它实际上是对HTTP上下文和RouteData的封装。
public class RequestContext
{
public RequestContext();
public RequestContext(HttpContextBase httpContext,RouteData routeData);
public virtual HttpContextBase HttpContext { get; set; }
public virtual RouteData RouteData { get; set; }
}
Route
RouteBase是一个抽象类,在ASP.NET路由系统的应用编程接口中,Route类型是其唯一的直接继承者,在默认的情况下调用RouteCollection的MapPageRoute方法在路由表中添加的就是这么一个对象。如下的代码片段所示,Route类型具有一个字符串类型的属性Url,它代表绑定在该路由对象上的路由模板。
public class Route:RouteBase
{
public Route(string url,IRouteHandler routeHandler);
public Route(string url,RouteValueDictionary defaults,IRouteHandler routeHandler);
public Route(string url,RouteValueDictionary defaults,RouteValueDictionary constraints,IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,RouteValueDictionary dataTokens, IRouteHandler routeHandler);
public override RouteData GetRouteData(HttpContextBase httpContext);
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
public RouteValueDictionary Contsraints { get; set; }
public RouteValueDictionary Defaults { get; set; }
public RouteValueDictionary DataTokens { get; set; }
public IRouteHandler RouteHandler { get; set; }
public string Url { get; set; }
}
在默认的情况下,针对请求的路由解析由路由表中的某个Route对象来完成,而某个Route对象是否会被选择取决于请求URL是否与对应的路由模板的模式相匹配。
对于一个具体的URL来说,匹配成功需要有两个基本的条件,即该URL包含的段的数量和URL模板相同,对应的文本段内容也一致。
除了用于表示路由模板的核心属性之外,Route类型还具有一些额外属性。属性Constraints为定义在模板中的变量以正则表达式的形式设定一些约束条件,其Key和Value分别表示变量名和做为约束的正则表达式。
Route类型的另一个属性Defaults,它保存了为路由变量定义的默认值。如果URL只能匹配路由模板前面的部分,但后边部分均为变量并且具有对应的默认值,这种情况下,依然被视为成功匹配。
Route类型的DataTokens属性它用于存储一些额外的路由变量,这些变量不会参与针对请求的路由解析。对应调用Route类型的GetRouteData和GetVirtualPath方法分别得到的RouteData和VirtualPathData对象来说,它们的DataTokens属性所包含的路由变量都来源于此。
RouteTable
一个Web应用通过RouteTable类型的静态只读属性Routes维护一个全局的路由表,如下的代码片段所示,该属性返回一个RouteCollection对象。
public class RouteTable
{
public static RouteCollection Routes { get; }
}
public class RouteCollection:Collection<RouteBase>
{
public RouteData GetRouteData(HttpContextBase httpContext);
public virtual VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
public virtual VirtualPathData GetVirtualPath(RequestContext requestContext,string name, RouteValueDictionary values);
public void Ignore(string url);
public void Ignore(string url, object constraints);
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile);
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess);
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults);
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints);
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens);
public bool AppendTrailingSlash { get; set; }
public bool LowercaseUrls { get; set; }
public bool RouteExistingFiles { get; set; }
}
当我们调用RouteCollection的GetRouteData和GetVirtualPath方法的时候,该方法会遍历集合中的每一个Route对象。针对每个Route对象,同名的方法会被调用。如果方法返回一个具体的RouteData或者VirtualPathData对象,它们会直接作为方法的返回值。
RouteCollection的RouteExistingFiles属性用于控制是否对存在的物理文件实施路由,该属性默认为False,即注册的路由不会影响针对物理文件的请求。
AppendTrailingSlash和LowercaseUrls这两个布尔类型的属性与方法GetVirtualPath有关,它们决定了对URL的正常化行为。AppendTrailingSlash属性表示是否需要在生成的URL末尾添加“/”,而LowercaseUrls属性则表示是否需要将生成的URL转变成小写。
其实我们使用的最为频繁的还是MapPageRoute和Ignore这两个方法。前者用于注册某个物理文件与路由模板自己的映射,其本质就是在本集合中添加一个Route对象。后者与此相反,用于注册一个路由模板使用路由系统可以忽略具有对应模式的URL。
当我们调用MapPageRoute方法的时候,它会将routeName参数作为对应Route对象的注册名称。如下的代码所示,RouteCollection具有一个Dictionary<string,RouteBase>类型的字段,注册的Route对象和注册名称之间的映射关系就保存在这个字典对象中。
public class RouteCollection : Collection<RouteBase>
{
private Dictionary<string, RouteBase> _namedMap;
}
通过RouteTable的静态只读属性Routes表示RouteCollection对象是针对整个应用的全局路由表。这个集合对象本身并不能提供线程安全的保证,所以同一个RouteCollection对象在多个线程中被同时操作时就有可能造成并发问题。为了解决这个问题,如下两个方法被定义在RouteCollection类型中。
public class RouteCollection : Collection<RouteBase>
{
public IDisposable GetReadLock();
public IDisposable GetWriteLock();
}