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

来源:互联网 发布:阿里云缩写 编辑:程序博客网 时间:2024/05/21 16:54

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的自定义路由对象,我们不得不采用手工添加的方式。

1 0
原创粉丝点击