相信经过前面2节文字的介绍,同学们可以快速地实现route路由的目的,并运用在实际的项目中了。在这一节里,我们在讲述一下路由更进一步的实现。
在前面的例子里,我们是把所有的路由信息给放在 Globla.asax.cs 文件里了,这样就产生了一个问题:由于实际业务的变化,每次都需要在该文件里做调整,然后重新编译部署。是不是觉得很麻烦,有没有一种更好的方式,不需要编译是最好啦。
很自然,有经验的同学会想到把路由表放在 web.config 配置文件里,这样的话每次在调整的时候只需要修改 web.config 文件就可以了。我也查询了下网上的资料,由于路由表是一个键值队列,需要在 web.config 里添加自定义节点,并且还需要编写这个自定义节点的实现类。我感觉实现思路有些麻烦,所以下面的一种方式就出来了。
我们把路由表信息放在 xml 文件里而不是 web.config 文件里,即使 web.config 事实上也是个xml。这样做的好处,一来不需要编写自定义节点的实现类;二是把路由表单独抽离出来,避免由于路由表的修改而带来误操作影响全局。
我们新建一个PageRoutes文件,继承 RouteBase 类,并实现其方法。主要有2个方法 GetRouteData 和 GetVirtualPath 。这2个方法是什么意思呢,简单地举例说,如果想从 /u/wave 得到 /user.aspx ,调用的是 GetRouteData 方法,如果是从 user.aspx 里得到 /u/wava , 调用的是 GetVirtualPath 方法。后者在 MVC 里用的非常多,在 webform 中,我这里详细描述 GetRouteData 方法,其它的原理是一样的。
我们来新建一个Route.xml文件用来保存路由信息。
<?xml version=”1.0″ encoding=”utf-8″ ?>
<Route>
<Page virtualPath=”/home” routePage=”~/default.aspx” />
<Page virtualPath=”/about” routePage=”~/about.aspx” />
<Page virtualPath=”/u/{userid}/{noteid}” routePage=”~/notedetail.aspx” />
<Page virtualPath=”/u/{userid}” routePage=”~/notelist.aspx” />
</Route>
接下来,我们把精力放在 GetRouteData 方法上,这个方法会拦截所有的请求,如果没有需要处理的,则返回 null 跳过进行后面的操作。下面的代码有点长,来慢慢地解说,
public IRouteHandler RouteHandler { get; set; }
public override RouteData GetRouteData (HttpContextBase httpContext) {
string xmlPath = httpContext.Server.MapPath (“~”) + “Route.xml”;
if (!File.Exists (xmlPath)) { return null; } //如果配置文件不存在则直接返回
XmlDocument doc = new XmlDocument ( );
doc.Load (xmlPath);
XmlNodeList list = doc.GetElementsByTagName (“Page”);
string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(1).ToLower();
foreach (XmlNode node in list) {
string n = node.Attributes[“virtualPath”].Value.ToLower();
//第一步,直接比对,优先级最高
if (n== virtualPath) {
var data = new RouteData();
data.RouteHandler = new PageRoute(node.Attributes[“routePage”].Value);
return data; }
//第二步,带 {userId}的参数比对
string v=n;
string o=new Regex(“{(\\w|\\W)+?}”).Replace(v,”(\\w|\\W)+”);
Regex reg = new Regex(o);
if (reg.IsMatch(virtualPath)){
var data = new RouteData();
data.RouteHandler = new PageRoute(node.Attributes[“routePage”].Value);
string[] a = n.Split(new char[] { ‘/’, ‘.’ });
string[] b = virtualPath.Split(new char[] { ‘/’, ‘.’ });
for (int i = 0, j = a.Length; i < j; i++) {
if (a[i].StartsWith(“{“)) {
string d = a[i].Replace(“{“, “”).Replace(“}”, “”);
data.Values[d] = b[i]; }}
return data; }}
return null; }
程序首先是检查xml文件是否存在,如果存在就加载并生成列表,使用当前的 url 在列表里遍历,如果找到了就返回信息。对于有参数的,则找到参数部分,看是否符合要求,符合的话先把参数填充进去,再返回信息。最后,如果都没有找到,则返回 null 跳过进行后面的。
现在,我们只需要在 Global 文件里添加 routes.Add(new PageRoutes()); 这一行就可以了。以后需要调整的时候直接修改 Route.xml 文件即可。至此,webform 中使用 Route 的文章基本上就写完了。
写在后面的话,其实这么写有一个潜在的性能问题,就是每次访问都需要加载 xml 文件,比较好的实现方式是把请求的路由信息放在缓存里,缓存依赖设置为文件类型的,这样的话每当xml有修改的话,缓存就自动失效重新加载 xml 。这个不是很难,就留给各位同学们去实现了。
而对于 GetVirtualPath 方法,我这里并没有去提及,一是对于传统的 webform 来说,用到的机会比较少;二是需要根据具体的项目来定义,这里以通用例子来讲解有误导嫌疑;三是给大家个锻炼的机会。
祝大家编码愉快。