ASP.NET的路由系统:路由映射
总的来说,我们可以通过RouteTable的静态属性Routes得到一个基于应用的全局路由表,通过上面的介绍我们知道这是一个类型的RouteCollection的集合对象,我们可以通过调用它的MapPageRoute进行路由映射,即注册URL模板与某个物理文件的匹配关系。路由注册的核心就是在全局路由表中添加一个Route对象,该对象的绝大部分属性都可以通过MapPageRoute方法的相关参数来指定。接下来我们通过实现演示的方式来说明路由注册的一些细节问题。
目录
一、变量默认值
二、约束
三、对现成文件的路由
四、注册路由忽略地址
五、直接添加路由对象
我们已前面介绍的关于获取天气预报信息的路由地址,我们在创建的ASP.NET Web应用中创建一个Weather.aspx页面,不过我们并不打算在该页面中呈现任何天气信息,而是将基于该页面的路由信息打印出来。该页面主体部分的HTML如下所示,我们不仅将基于当前页面的RouteData对象的Route和RouteHandler属性类型输出来,还将存储于Values和DataTokens字典的变量显示出来。
1: <body>
2: <form id="form1" runat="server">
3: <div>
4: <table>
5: <tr>
6: <td>Route:</td>
7: <td><%=RouteData.Route != null? RouteData.Route.GetType().FullName:"" %></td>
8: </tr>
9: <tr>
10: <td>RouteHandler:</td>
11: <td><%=RouteData.RouteHandler != null? RouteData.RouteHandler.GetType().FullName:"" %></td>
12: </tr>
13: <tr>
14: <td>Values:</td>
15: <td>
16: <ul>
17: <%foreach (var variable in RouteData.Values)
18: {%>
19: <li><%=variable.Key%>=<%=variable.Value%></li>
20: <% }%>
21: </ul>
22: </td>
23: </tr>
24: <tr>
25: <td>DataTokens:</td>
26: <td>
27: <ul>
28: <%foreach (var variable in RouteData.DataTokens)
29: {%>
30: <li><%=variable.Key%>=<%=variable.Value%></li>
31: <% }%>
32: </ul>
33: </td>
34: </tr>
35: </table>
36: </div>
37: </form>
38: </body>
在添加的Global.asax文件中,我们将路由注册操作定义在Application_Start方法中。如下面的代码片断所示,映射到weather.aspx页面的URL模板为{areacode}/{days}。在调用MapPageRoute方法的时候,我们还为定义在URL模板的两个变量定义了默认值以及正则表达式。除此之外,我们还在注册的路由对象上附加了两个变量,表示对变量默认值的说明(defaultCity:BeiJing;defaultDays:2)。
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 }};
6: var constaints = new RouteValueDictionary { { "areacode", @"0\d{2,3}" }, { "days", @"[1-3]{1}" } };
7: var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
8:
9: RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, constaints, dataTokens);
10: }
11: }
一、变量默认值
由于我们为定义在URL模板中表示区号和天数的变量定义了默认值(areacode:010;days:2),如果我们希望返回北京地区未来两天的天气,可以直接访问应用根地址,也可以只指定具体区号,或者同时指定区号和天数。如下图所示,当我们在浏览器地址栏中输入上述三种不同的URL会得到相同的输出结果。
从下图所示的路由信息我们可以看到,默认情况下RouteData的Route属性类型正是Route,而RouteHandler属性则一个是PageRouteHandler对象,我们会在本章后续部分对PageRouteHandler进行详细介绍。通过地址解析出来的变量被存储数Values属性中,而在进行路由注册过程为Route对象DataTokens属性定义的变量被转移到了RouteData的同名属性中。[实例源代码下载]
二、约束
我们以电话区号代表对应的城市,为了确保用户在的请求地址中提供有效的区号,我们通过正则表达式(“0\d{2,3}”)对其进行了约束。此外,我们只能提供未来3天以内的天气情况,我们同样通过正则表达式(“[1-3]{1}”)是对请求地址中表示天数的变量进行了约束。如果请求地址中的内容不能符合相关变量段的约束条件,则意味着对应的路由对象与之不匹配。
对于本例来说,由于我们只注册了唯一的路由对象,如果请求地址不能满足我们定义的约束条件,则意味着找不到一个具体目标文件,会返回404错误。如下图所示,由于在请求地址中指定了不合法的区号(01)和天数(4),我们直接在浏览器界面上得到一个HTTP 404错误。
对于约束,除了可以通过字符串的形式为某个变量定义相应的正则表达式之外,我们还可以指定一个实现了IRouteConstraint接口的类型的对象对整个请求进行约束。如下面的代码片断所示,IRouteConstraint具有唯一的方法Match用于定义约束的逻辑,该方法的5个参数分别表示:HTTP上下文、当前路由对象、约束的名称(存储约束对象的RouteValueDictionary的Key)、解析被匹配URL得到的变量集合以及表示路由的方向。
1: public interface IRouteConstraint
2: {
3: bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
4: }
5: public enum RouteDirection
6: {
7: IncomingRequest,
8: UrlGeneration
9: }
所谓路由的方向表示是针对请求匹配(入栈)还是针对URL的生成(出栈),分别通过如上所示的枚举类型RouteDirection的两个枚举值表示。具体来说,当调用路由对象的GetRouteData和GetVirtualPathData方法时,枚举值IncomingRequest和UrlGeneration分别被采用。
ASP.NET路由系统的应用编程接口中定义了如下一个实现了IRouteConstraint接口的HttpMethodConstraint类型。顾名思义,HttpMethodConstraint提供针对HTTP方法(GET、POST、PUT、DELTE等)的约束。我们可以通过HttpMethodConstraint为路由对象设置一个允许的HTTP方法列表,只有方法名称在这个指定的列表中的HTTP请求才允许被路由。这个被允许被路由的HTTP方法列表对于HttpMethodConstraint的只读属性AllowedMethods,并在构造函数中初始化。
1: public class HttpMethodConstraint : IRouteConstraint
2: {
3: public HttpMethodConstraint(params string[] allowedMethods);
4: bool IRouteConstraint.Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
5: public ICollection<string> AllowedMethods { get; }
6: }
同样是针对我们演示的例子,我们在进行路由注册的时候通过如下的代表应用了一个类型为HttpMethodConstraint的约束,并将允许的HTTP方法设置为POST,意味着被注册的Route对象仅限于路由POST请求。
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 } };
6: var constaints = new RouteValueDictionary { { "areacode", @"0\d{2,3}" }, { "days", @"[1-3]{1}" }, { "httpMethod", new HttpMethodConstraint("POST") } };
7: var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
8:
9: RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, constaints, dataTokens);
10: }
11: }
现在我们采用与注册的URL模板相匹配的地址(/010/2)来访问Weather.aspx页面,依然会得到如下图所示的404错误。[实例源代码下载]
三、对现有文件的路由
在成功注册路由的情况下,如果我们按照传统的方式访问一个物理文件(比如.asxp、.css或者.js等),在请求地址满足某个路由的URL模板模式的情况下,ASP.NET是否还是正常实施路由呢?我们不妨通过我们的实例还测试一下。为了让针对某个物理文件的访问地址也满足注册路由对象的URL模板模式,我们需要按照如下的方式将上面定义的关于正则表达式约束删除。
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 }};
6: //var constaints = new RouteValueDictionary { { "areacode", @"0\d{2,3}" }, { "days", @"[1-3]{1}" } };
7: var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
8:
9: RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, null, dataTokens);
10: }
11: }
当我们通过传统的方式来访问存放于根目录下的weather.aspx页面时会得到如下图所示的结果。从界面上的输出结果我们不难看出,虽然请求地址完全满足我们注册路由对象的URL模板模式,但是ASP.NET并没有对请求地址实施路由。原因很简单,如果中间发生了路由,基于页面的RouteData的各项属性都不可能为空。[实例源代码下载]
那么是否意味着如果请求地址对应着一个现存的物理文件,ASP.NET就会自动忽略路由呢?实则不然,或者说不对现有文件实施路由仅仅默认采用的行为。是否对现有文件实施路由取决于代表全局路由表的RouteCollection对象的RouteExistingFiles属性,该属性默认情况下为False,我们可以将此属性设置为True使ASP.NET路由系统忽略现有物理文件的存在,总是按照注册的路由表进行路由。为了演示这种情况下,我们对Global.asax文件作了如下的改动,在进行路由注册之前将RouteTable的Routes属性代表的RouteCollection对象的RouteExistingFiles属性设置为True。
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: RouteTable.Routes.RouteExistingFiles = true;
6: var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 } };
7: var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
8:
9: RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, null, dataTokens);
10: }
11: }
依旧是针对weather.aspx页面的访问,我们却得到不一样的结果。从下图中我们可以看到,针对页面的相对地址weather.aspx不再指向具体的Web页面,在这里就是一个表示获取的天气信息对应的目标城市(areacode=weather.aspx)。[实例源代码下载]
四、注册路由忽略地址
如果将代表全局路由表的RouteTable的静态属性Routes的RouteExistingFiles属性设置为True,意味着ASP.NET针对所有抵达的请求都一视同仁,都按照注册的路由表进行注册,但这会代码一些问题。不知道读者有没有发现上图所示的页面具有不一样的格式(标签部分没有加粗,也没有居右上对齐),这是因为这是采用了jQuery的方式来控制的,为此我们必须按照如下的方式来饮用jQuery相关的脚本文件。
1: <script src="/jquery-1.4.1.min.js" type="text/javascript"></script>
但是由于我们将全局路由表的RouteExistingFiles属性设置为True,意味着针对上面这个.js脚本文件的访问也会被路由。根据我们注册的路由规则,针对这个文件的访问会自动被导向weather.aspx这个页面。如下图所示,我们直接在浏览器的地址栏中属性.js文件的地址,呈现出来还是我们熟悉的界面(areacode=jquery-1.4.1.min.js)。[实例源代码下载]
这是一个不得不解决的问题,因为它是我们无法正常地在页面中引用向javascript和css文件。我们可以通过调用RouteCollection的Igore方法来注册一些需要让路由系统忽略的URL模板。从前面给出的关于RouteCollection的定义我们可以看到它具有两个Igore重载,除了指定需要忽略的URL模板之外,我们还可以对相关的变量定义约束正则表达式。为了让ASP.NET路由系统忽略掉针对.js文件请求,我们可以按照如下的方式在Global.asax中调用RouteTable的Routes属性的Ignore方法。[实例源代码下载]
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: RouteTable.Routes.RouteExistingFiles = true;
6: RouteTable.Routes.Ignore("{filename}.js/{*pathInfo}");
7: //其他操作
8: }
9: }
五、直接添加路由对象
当我们调用RouteCollection对象的MapPageRoute方法进行路由注册的本质就在路由字典中添加Route对象,所以我们完全调用Add方法添加一个手工创建的Route对象,如下所示的两种路由注册方式是完全等效的。如果我们需要添加一个继承自RouteBase的自定义路由对象,我们不得不采用手工添加的方式。
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 }};
6: var constaints = new RouteValueDictionary { { "areacode", @"0\d{2,3}" }, { "days", @"[1-3]{1}" } };
7: var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
8:
9: //路由注册方式1
10: RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, constaints, dataTokens);
11:
12: //路由注册方式2
13: Route route = new Route("{areacode}/{days}", defaults, constaints, dataTokens, new PageRouteHandler("~/weather.aspx", false));
14: RouteTable.Routes.Add("default", route);
15: }
16: }
ASP.NET的路由系统:URL与物理文件的分离
ASP.NET的路由系统:路由映射
ASP.NET的路由系统:根据路由规则生成URL
ASP.NET的路由系统:路由映射的更多相关文章
- ASP.NET MVC3中的路由系统 Routes
MVC中,用户访问的地址并不映射到服务器中对应的文件,而是映射到对应Control里对应的ActionMethod,由ActionMethod来决定返回用户什么样的信息.而把用户访问的地址对应到对应的 ...
- ASP.NET MVC3中的路由系统(Routes) .
MVC中,用户访问的地址并不映射到服务器中对应的文件,而是映射到对应Control里对应的ActionMethod,由ActionMethod来决定返回用户什么样的信息.而把用户访问的地址对应到对应的 ...
- ASP.NET MVC3中的路由系统
MVC中,用户访问的地址并不映射到服务器中对应的文件,而是映射到对应Control里对应的ActionMethod,由ActionMethod来决定返回用户什么样的信息.而把用户访问的地址对应到对应的 ...
- MVC3中的路由系统(Routes)
转载:http://blog.csdn.net/francislaw/article/details/7429317 MVC中,用户访问的地址并不映射到服务器中对应的文件,而是映射到对应Control ...
- Django之URL(路由系统)用法
路由系统 路由系统概念 简而言之,路由系统就是路径和视图函数的一个对应关系.django的路由系统作用就是使views里面处理数据的函数与请求的url建立映射关系.使请求到来之后,根据urls.py里 ...
- [oldboy-django][2深入django]django目录说明 + 路由系统
django project目录说明 project - app01 -- admin.py #django自带后台管理 -- apps.py #app01配置文件 -- models.py #编写类 ...
- Django 的路由系统
Django 的路由系统 Django 的路由系统 路由层 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^$',views.ho ...
- django框架基础-路由系统-长期维护
################## 路由系统介绍 ####################### 路由系统就是路径和函数的对应关系, 路由系统可以看成支撑你这个网站的目录,就像是一本书一样 ...
- Flask之路由系统
路由系统 路由的两种写法 1.第一种方法: def index(): return render_template('index.html') app.add_url_rule('/index', ' ...
- ASP.NET路由系统实现原理:HttpHandler的动态映射
我们知道一个请求最终通过一个具体的HttpHandler进行处理,而我们熟悉的用于表示一个Web页面的Page对象就是一个HttpHandler,被用于处理基于某个.aspx文件的请求.我们可以通过H ...
随机推荐
- Maven 分模块,启动父工程时异常
1.1 运行方式 Maven方式:命令的 方式1:运行父工程.父工程将各个子模块聚合到一起.将ssh-web打war包发布到tomcat 方式2:直接运行web工程 其他方式:传统的, 部署到to ...
- 假设result 是一个float型变量,value是一个int型变量。执行以下赋值语句以后,变量value将是什么类型?为什么?
假设result 是一个float型变量,value是一个int型变量.执行以下赋值语句以后,变量value将是什么类型?为什么? 在执行这条语句的过程中,保存在vulue变量中的值被读取出来并转化为 ...
- win10家庭版多用户
1.Windows 找不到gpedit.msc https://jingyan.baidu.com/article/54b6b9c08b08382d593b4747.html 2.win10家庭版 创 ...
- android页面渲染速度提升的常用方法
参考文档:http://blog.csdn.net/vector_yi/article/details/24402101 当activity中用到的布局较多较为复杂时,页面渲染就会变得复杂,现汇总以下 ...
- MySql delete和truncate区别
项目 delete truncate 添加where条件 可以添加 不可以添加 执行效率 略高 高 自增长列 delete删除后,插入数据的自增长 列值从断点开始 truncate删除后,插入数据的自 ...
- go语言中的运算符^,&
一.^运算符 1.作为二元运算符 ^作二元运算符就是异或,包括符号位在内,相同为0,不相同为1 规则:1^1 =0, 0^0=0,1^0=1,0^1=1 事例: (1)0001 0100 ^ 0000 ...
- Web QQ 协议 登录加密算法 —— VC++实现
BOOL ToHexStr(const CHAR * lpStr, int nSrcLen, CHAR * lpHex, int nDestLen) { const CHAR cHexTable[] ...
- vc++高级班之多线程篇[6]---线程间的同步机制①
①.线程同步的必要性: int g_Num = 0; UINT __cdecl ThreadProc(LPVOID lpParameter) { for (int idx = 0; idx &l ...
- python二叉树练习
#coding=utf8 node_list=[5,3,6,2,4,None,8,1,None,None,None,7,9] class Node: def __init__(self,item): ...
- 全系列Unity4.x.x到2017.1.1破解Win&Mac!最新Unity2017.1.1p3&4.7.2f1破解!
Unity官网所有版本下载地址请戳: http://unity3d.com/unity/download/archive 补丁版本请戳: http://unity3d.com/cn/unity/qa/ ...