拥有一个网站意味着需要投入时间和精力在互联网上推广,确保搜索引擎索引所有页面,并通过博客或讨论板获得曝光。然后,当有了新的想法,真的需要重新组织网站——改变一些文件夹名称,移动一些页面等。那么,那些曾经引以为豪的第三方链接会发生什么?想要失去它们吗?
在.NET Framework 3.5 SP1的到来,有了一个优雅的解决这个问题的方法——ASP.NET路由。最初,它是ASP.NET MVC Preview 2的一部分,现在它已经成为框架的一部分。
这个想法是向网站添加特殊的“路由”,其单一目标是处理对不再存在于网站上的页面的请求。在其最简单的形式中,处理可以在一个ASPX页面中完成,该页面负责正确处理请求。
下面是一个示例:
附加的项目包含了所需要的所有部分:
让看看Global.asax:
        void Application_Start(object sender, EventArgs e) {
            // 运行在应用程序启动时
            RegisterMyRoutes(System.Web.Routing.RouteTable.Routes);
        }
        private void RegisterMyRoutes(System.Web.Routing.RouteCollection routes) {
            // 引用IRouteHandler实现
            // (示例由Chris Cavanagh创建)
            // 参见 http://chriscavanagh.wordpress.com/2008/03/11/aspnet-routing-goodbye-url-rewriting/
            var startPageRouteHandler = new WebFormRouteHandler("~/default.aspx");
            // 排除.axd以处理Web服务和AJAX,而不需要检查所有路由
            // 参见 http://msdn.microsoft.com/en-us/library/
            // system.web.routing.stoproutinghandler.aspx
            routes.Add(new System.Web.Routing.Route("{resource}.axd/{*pathInfo}", new System.Web.Routing.StopRoutingHandler()));
            routes.Add(new System.Web.Routing.Route("{service}.asmx/{*path}", new System.Web.Routing.StopRoutingHandler()));
            // 映射:
            // 提取文件夹名称和页面名称作为HttpContext.Items中的项
            routes.Add(new System.Web.Routing.Route("{folderName}/", startPageRouteHandler));
            routes.Add(new System.Web.Routing.Route("{folderName}/{pageName}", startPageRouteHandler));
        }
     
在这里,定义了一个单一的路由处理器——default.aspx,以及路由规则。
规则#1:
        routes.Add(new System.Web.Routing.Route("{folderName}/", startPageRouteHandler));
    
表明所有结构为"http://mysite.com/something"的URL请求将由default.aspx页面处理,如果网站上没有实际的"something"。例如,如果网站上有RealPage.aspx页面,那么对http://mysite.com/RealPage.aspx的请求将由该页面处理。
但是,如果客户端请求RealPage2.aspx,那么该请求将由default.aspx页面根据规则#1处理。请注意,客户端不会被重定向到default.aspx,它将只是Web服务器在响应请求时运行default.aspx中的代码。对于客户端来说,响应将来自RealPage2.aspx。
可以添加尽可能多的路由,例如规则#2:
        routes.Add(new System.Web.Routing.Route("{folderName}/{pageName}", startPageRouteHandler));
    
表明所有结构为"http://mysite.com/somefolder/somethingelse"的URL请求将由default.aspx页面处理,如果网站上没有实际的"somefolder/somethingelse"。
default.aspx的代码背后展示了如何提取请求的这些部分。如所见,它们将被放置在HttpContext.Items集合中。
        lblFolder.Text = Context.Items["folderName"] as string;
        lblPage.Text = Context.Items["pageName"] as string;
    
在现实生活中的应用:
这里有一个实际使用这种技术的网站——Digitsy Global Store。除了处理过时的URL,ASP.NET路由还被用来处理网站上的多种语言,实时切换CultureInfo:
        protected void Page_PreInit(object sender, EventArgs e) {
            CultureInfo lang = new CultureInfo(getCurrentLanguage());
            Thread.CurrentThread.CurrentCulture = lang;
            Thread.CurrentThread.CurrentUICulture = lang;
        }
        private static string getCurrentLanguage() {
            string lang = HttpContext.Current.Items["language"] as string;
            switch (lang) {
                case "france":
                    return "fr-FR";
                case "canada":
                    return "en-CA";
                case "germany":
                    return "de-DE";
                case "japan":
                    return "ja-JP";
                case "uk":
                    return "en-GB";
                case "russia":
                    return "ru-RU";
                default:
                    return "en-US";
            }
        }
    
如所见,默认语言是英语,美国:"en-US"。在内部链接中,网站使用结构http://{sitename}/{language}/…其他东西…
所以,如果尝试http://digitsy.com/us/,将得到美国版本,尝试http://digitsy.com/japan/将带去日本版本,如果尝试http://digitsy.com/whatever/——不会得到404错误,将再次得到美国版本。
ASP.NET路由使网站的重构变得非常容易。文件夹结构"{language}/{index}/category/{categoryID}"最近被替换为"{language}/{index}/shopping/{categoryID}"。网站上不应该再有"category"文件夹。但由于两个路由都指向同一个处理页面,两个文件夹"category"和"shopping"都返回有效响应。
尝试http://digitsy.com/us/Electronics/shopping/541966将使用规则:
        routes.Add(new System.Web.Routing.Route("{language}/{index}/shopping/{categoryID}", categoryRouteHandler));
    
而尝试http://digitsy.com/us/Electronics/category/541966将使用:
        routes.Add(new System.Web.Routing.Route("{language}/{index}/category/{categoryID}", categoryRouteHandler));
    
两者都将解析到同一个路由处理页面。