学习ASP.NET MVC5框架揭秘笔记-ASP.NET路由(十一)

3.对现有物理文件的路由

在成功注册路由的情况下,如果我们按照传统的方式访问一个现存的物理文件,在请求地址满足某个Route的路由规则,ASP.NET是否还能正常实施路由呢?我们不妨通过实例来测试一下。为了让针对某个物理文件的访问地址也满足注册路由对象的路由模板采用的URL模式,我们需要按照如下的方式在进行路由注册时将表示约束的参数设置为Null

public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 } }; 
            var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
            RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}","~/weather.aspx", false, defaults, null, dataTokens);
        }
    }


当通过传统的方式来访问存放于跟目录下的Weather.aspx页面时,页面上并没有参数,而如果发生了路由,基于页面的RouteData的各项属性都不可能为空。

如果请求URL对应着一个现存的物理文件的路径,ASP.NET会不会总是自动忽略路由呢?实则不然,不对现有文件实施路由仅仅是默认采用的行为而已,是否对现有文件实施路由取决于代表全局路由表的RouteCollection对象的RouteExistingFiles属性(该属性默认情况下为False)。

我们可以将此属性设置为True使ASP.NET路由系统忽略现有物理文件的存在,让它总是按照注册的路由表进行路由表进行路由。为了演示这种情况,我们对Global.asax文件作了如下改动,在进行路由注册之前将RouteTableRoutes属性代表的RouteCollection对象的RouteExistingFiles属性设置为True

public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.RouteExistingFiles = true;
            var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 } }; 
            var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
            RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}","~/weather.aspx", false, defaults, null, dataTokens);
        }
    }


依旧是针对Weather.aspx页面的访问却得到了不一样的结果。在这里里面的参数值就都是默认值了。

通过上面的介绍我们知道,作为路由对象集合的RouteCollectionRouteBase均具有一个布尔类型的RouteExistingFiles属性,用以控制是否对现有物理文件实施路由,他们的默认值分别是FalseTrue。由上面的实例演示我们知道,当请求与RouteCollection的某个Route对象的路由规则相匹配的情况下,RouteCollection本身的RouteExistingFiles属性会改变GetRouteData的值,使该方法只有在RouteExistingFiles属性值为False的情况下才会返回一个RouteData对象,否则直接返回Null

我们现在需要讨论的是另一个问题:Route对象自身的RouteExistingFiles属性值对于自身的GetRouteData方法及它所在的RouteCollection对象的GetRouteData又会造成什么样的影响呢?

对于一个Route对象来说,他自身的GetRouteData方法不受其RouteExistingFiles属性值的影响,也就是说GetRouteData能否返回一个具体的RouteData对象完全取决于定义的路由规则与请求相匹配。如果一个RouteCollection包含唯一的Route对象,那么它的GetRouteData方法只有同时满足如下3个条件才能返回一个具体的RouteData对象。

1.RouteCollection自身的RouteExistingFiles属性为True

2.Route对象的RouteExistingFiles也为True

3.Route对象的路由规则与请求相匹配。

也就是说Route自身的RouteExistingFiles属性对于自身的路由没有影响,该属性最终是给RouteCollection使用的。

我们创建一个空的ASP.NET应用,并在添加的默认Web页面Default.aspx的后台文件中定义如下一个GetRouteData方法。该方法根据指定的参数返回一个RouteData对象,其中枚举类型的参数routeOrCollection决定返回的RouteData是调用Route对象的GetRouteData方法生成的还是调用RouteCollectionGetRouteData方法生成的,参数routeExistingFiles4CollectionrouteExistingFiles4Route则分别控制着RouteCollectionRoute对象的RouteExistingFiles属性。

public partial class Default : System.Web.UI.Page
    {
        public enum RouteOrRouteCollection
        {
            Route,
            RouteCollection
        }
 
        public RouteData GetRouteData(RouteOrRouteCollection routeOrCollection,
        bool routeExistingFiles4Collection, bool routeExistingFiles4Route)
        {
            Route route = new Route("{areaCode}/{days}", new RouteValueDictionary { { "areacode", "010" }, { "days", 2 } }, null);
            route.RouteExistingFiles = routeExistingFiles4Route;
            HttpContextBase context = CreateHttpContext();
 
            if (routeOrCollection == RouteOrRouteCollection.Route)
            {
                return route.GetRouteData(context);
            }
 
            RouteCollection routes = new RouteCollection();
            routes.Add(route);
            routes.RouteExistingFiles = routeExistingFiles4Collection;
            return routes.GetRouteData(context);
        }
 
        private static HttpContextBase CreateHttpContext()
        {
            HttpRequest request = new HttpRequest("~/weather.aspx", "http://localhost:3721/weather.aspx", null);
            HttpResponse response = new HttpResponse(new StringWriter());
            HttpContext context = new HttpContext(request, response);
            HttpContextBase contextWrapper = new HttpContextWrapper(context);
            return contextWrapper;
        }
    }


在上面定义的这个GetRouteData方法中创建的Route对象分别采用我们熟悉的路由模板“{areaCode}/{days}”,并且两个变量均有默认值。如果需要返回Route对象自身生成的RouteData对象,我们直接调用其GetRouteData方法,否则创建一个仅仅包含该Route对象的RouteCollection对象并调用其GetRouteData方法。调用GetRouteData方法传人的参数是我们手工创建的HttpContextWrapper对象,它的请求URL“http://localhost:3721/weather.aspx”)与Route对象创建的路由模板想匹配。

由于需要验证针对现有物理文件的路由,所以我们在应用的根目录创建一个名为Weather.aspx的空页面,同时将应用发布的端口设置为3721。然后我们在Default.aspx页面的主体部分定义如下的HTML,它会将RouteCollectionRouteRouteExistingFiles属性在不同组合下调用各自GetRouteData方法的返回值通过表格的形式呈现出来(具体来说,如果返回值不为空则输出“RouteData”,否则输出“Null”)。

<form id="form1" runat="server">
    <table class="table table-bordered">
        <thead>
          <tr>
            <th>RouteCollection.RouteExistingFiles</th>
            <th colspan="2">True</th>
            <th colspan="2">False</th>
          </tr>
          <tr>
            <th>Route.RouteExistingFiles</th>
            <th>True</th>
            <th>False</th>
            <th>True</th>
            <th>False</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Route.GetRouteData()</td>
            <td><%=this.GetRouteData(RouteOrRouteCollection.Route,true,true) == null ? "Null":"RouteData" %></td>
            <td><%=this.GetRouteData(RouteOrRouteCollection.Route,true,false) == null ? "Null":"RouteData" %></td>
            <td><%=this.GetRouteData(RouteOrRouteCollection.Route,false,true) == null ? "Null":"RouteData" %></td>
            <td><%=this.GetRouteData(RouteOrRouteCollection.Route,false,false) == null ? "Null":"RouteData" %></td>
          </tr>
          <tr>
            <td>RouteCollection.GetRouteData()</td>
            <td><%=this.GetRouteData(RouteOrRouteCollection.RouteCollection,true,true) == null ? "Null":"RouteData" %></td>
            <td><%=this.GetRouteData(RouteOrRouteCollection.RouteCollection,true,false) == null ? "Null":"RouteData" %></td>
            <td><%=this.GetRouteData(RouteOrRouteCollection.RouteCollection,false,true) == null ? "Null":"RouteData" %></td>
            <td><%=this.GetRouteData(RouteOrRouteCollection.RouteCollection,false,false) == null ? "Null":"RouteData" %></td>
          </tr>
        </tbody>
  </table>
</form>

运行程序后我们会发现 Route 对象的 GetRouteData 方法不会受自身 RouteExistingFiles 属性影响。在请求与路由规则匹配的情况下, RouteCollection 只有在自身 RouteExistingFiles 属性和 Route 对象的 RouteExistingFiles 属性同时为 True 的情况下才会返回一个具体的 RouteData 对象。

 

4.注册路由忽略地址

RouteTable的静态属性Routes返回的RouteCollection对象代表针对整个应用的全局路由表。如果我们将该对象的RouteExistingFiles属性设置为TrueASP.NET路由系统将会对所有抵达的请求实施路由,但这同样会带来一些问题。

举个简单的例子,一个Web应用往往涉及很多静态文件,比如文本类型的JavaScript或者CSS文件和图片。如果一个Web应用寄宿于IIS下,对于Classic模式下的IIS 7.x及之前的版本,针对这些静态文件请求直接由IIS来响应,并不会进入ASP.NET的管道,所以由ASP,NET提供的路由机制并不会针对他们的访问造成任何影响。

但是对于Integrated模式下的IIS7.5,如果采用ASP.NET集成管道,所有类型的请求都将进入ASP.NET管道。这种情况下,如果允许路由系统路由现有物理文件,针对某个静态文件的请求就有可能被重定向到其他地方,这意味着我们将不能正常访问这些静态文件。除了采用基于Integrated模式下的IIS作为Web服务器,在采用Visual Studio提供的ASP.NET Development ServerIIS ExpressIIS ExpressIIS功能上面基本相同)的情况下这种问题依然存在。

我们就用上面的实例来演示这个问题。为了能让ASP.NET管道能够接管所有类型的访问请求,我们需要在web.config中添加如下一段配置。

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>


我们在“/Content/”目录下放置一个名为bootstrap.cssCSS文件来控制页面显示的样式,并在允许针对现有物理文件路由的情况下(RouteTable.Routes.RouteExistingFiles = True)通过浏览器来访问这个CSS文件。由于CSS文件的路径(/content/bootstrap.css)与注册的路由模板({areacode}/{days})是匹配的,所以对应的请求自然就被路由到Weather.aspx页面了。

这是一个不得不解决的问题,因为它使我们无法正常地在页面中引用JavaScriptCSS文件。我们可以通过调用RouteCollectionIgnore方法来注册一些需要让路由系统忽略的URL。从前面给出的关于RouteCollection的定义中我们可以看到它具有两个Ignore方法重载,除了指定与需要忽略的URL相匹配的路由模板之外,还可以对相关的变量定义约束正则表达式。为了让路由系统忽略针对CSS文件的请求,我们可以按照如下的方式在Global.sapx中调用RouteTableRoutes属性的Ignore方法。值得一提的是,这样的方法调用应该放在路由注册之前,否则起不到任何作用。

public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.RouteExistingFiles = true;
            RouteTable.Routes.Ignore("content/{filename}.css/{*pathInfo}");
            var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 } }; 
            var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
            RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}","~/weather.aspx", false, defaults, null, dataTokens);
        }
    }


5.直接添加路由对象

我们调用RouteCollection对象的MapPageRoute方法进行路由注册的本质就是在路由表中添加Route对象,所以我们完全可以调用Add方法添加一个手工创建的Route对象。这两种路由注册方式是完全等效的。如果需要添加一个继承自RouteBase的自定义路由对象,我们不得不采用手工添加的方式。

ASP.NET MVC 5 框架揭秘》以一个模拟ASP.NET MVC内部运行机制的“迷你版MVC框架”作为开篇,其目的在于将ASP.NET MVC真实架构的“全景”勾勒出来。接下来本书以请求消息在ASP.NET MVC框架内部的流向为主线将相关的知识点串连起来,力求将”黑盒式”的消息处理管道清晰透明地展示在读者面前。相信精读本书的读者一定能够将ASP.NET MVC从接收请求到响应回复的整个流程了然于胸,对包括路由、Controller的激活、Model元数据的解析、Action方法的选择与执行、参数的绑定与验证、过滤器的执行以及View的呈现等相关的机制具有深刻的理解。 本书以实例演示的方式介绍了很多与ASP.NET MVC相关的很好实践,同时还提供了一系列实用性的扩展,相信它们一定能够解决你在真实开发过程中遇到的很多问题。本书末章提供的案例不仅仅用于演示实践中的ASP.NET MVC,很多的架构设计方面的东西也包含其中。除此之外,本书在很多章节还从设计的角度对ASP.NET MVC的架构进行了深入分析,所以从某种意义上讲本书可以当成一本架构设计的书来读。 ASP.NET MVC 5 框架揭秘 目录 第1章 ASP.NET + MVC 第2章 路由 第3章 Controller的激活 第4章 Model元数据的解析 第5章 3个描述对象 第6章 Model的绑定(上篇) 第7章 Model的绑定(下篇) 第8章 Model的验证(上篇) 第9章 Model的验证(下篇) 第10章 Action方法的执行 第11章 View的呈现 第12章 过滤器 第13章 特性路由 第14章 案例实践
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值