理解ASP.NET MVC的路由系统
引言
路由,正如其名,是决定消息经由何处被传递到何处的过程。也正如网络设备路由器Router一样,ASP.NET MVC框架处理请求URL的方式,同样依赖于一张预定义的路由表。以该路由表为转发依据,请求URL最终被传递给特定Controller的特定Action进行处理。而在相反的方向上,MVC框架的渲染器同样要利用这张路由表,生成最终的HTML页面并返回URL。所以,理解整个ASP.NET MVC的路由系统,有两个必须出现的关键元素:Controller与Action,有两个方向的操作:传入的路径解析与传出的路径生成。
以下整理自《Pro ASP.NET MVC 3 Framework》学习笔记。
路由解析
一个URL由以下三部分组成:
http://www.website.com/Admin/Index
协议 主机 查询串
路由解析,就是要将Admin/Index这部分片段Segment传递给适当Controller的Action进行处理。至于路由何方,则又依赖于预置的路由表项进行指引。所以,在一切开始之前,需要通过添加路由项来完成路由表的配置。
路由项的添加,由RouteCollection.MapRoute()或RouteCollection.Add()方法实现,并通常在在MVC项目入口Global.asax中的RegisterRoutes()方法中使用。其中,MapRoute()更为常用和直接。
MapRoute(string routeName, //路由项的名称,用于路径模式匹配
string urlPattern, //路径模式
object defaultProperties, //路径元素与参数的默认值
object constraints, //条件满足时才适用本路由项的约束
string[] namespaces) //界定Controller位置的命名空间
换言之,路由的最终结果就是得到controller.action(parameters)这三项内容。而这三项内容,都将出现在MapRoute()的defaultProperties中。defaultProperties是一个匿名类的对象,其属性包括controller名、action名,以及其他参数在内。
routes.MapRoute("routeA",
"{controller}/{action}/{page}",
new {controller = "Admin", action = "Index", page = 1})
就象上面这个示例一样,第2个参数urlPattern对应的那个模式串中,{}包围的部分是一个占位符,类似string.Format()中的格式化模式串。整个模式串,用于匹配URL中主机名后的全部内容。而占位符对应的元素,则将完整出现在之后的defaultProperties中,映射到相应的属性上。{}里的这个元素,被称为Segment Variable。
在用urlPattern匹配URL时,会象方法的参数表一样进行严格匹配。对请求URL没有提供的项,将自动使用defaultProperties中提供的默认值。最终,匹配的结果,是将请求传递到类似这样的一个方法上,匿名类中作为属性的Controller名、Action名以及参数名page都将严格与实际的方法原型匹配(匹配将忽略名称的大小写)。而且匹配的过程呈现出两面性,一面是它只会按Pattern里Segment Variable的数量进行匹配(死板的),另一面是它不会关心每个Segment能否解析出恰当的内容(开明的),比如某个位置本该对应一个整数而不是字符串。
public class SomeController
{
public ViewResult SomeAction(int page) { .... }
}
占位片段过多的URL则会失配。正如下表:
URL controller action page
http://www.website.com/ Admin Index 1
http://www.website.com/Product Product Index 1
http://www.website.com/Admin/Index Admin Index 1
http://www.website.com/Admin/Logon Admin Logon 1
http://www.website.com/Product/List Product List 1
http://www.website.com/Admin/Index/5 Admin Index 5
http://www.website.com/Admin/Index/5/detail null null null
其次,路由表项的排列顺序直接影响路由结果。因为MVC框架按路由项添加的先后顺序进行匹配,而非匹配程度高低,所以越特殊的路由项需要越早被定义。反观URL模式,它将严格按格式串的语义进行解析。如"Prefix/{controller}/{action}",将匹配到类似http://www.website.com/Prefix/Product/List"这样的URL。如果URL指向特定的文件,比如"download/somefile.pdf",同样也可以设置RouteCollection.RouteExistFiles = true,使文件也被路由系统接管,传递给特定的controller。
除去上述这种在路由中指定参数默认值的方式外,还可以利用UrlParameter.Optional声明可选参数来影响路由。与路由定义中的默认参数将始终存在不同,可选参数意味着仅当请求URL中出现对应于该可选参数的片段时,才会有一个相应的参数被构造并被传递给action,否则就当这个参数从来没有被定义和存在过。
routes.MapRoute("routeA",
"{controller}/{action}/{page}",
new { controller = "Admin", action = "Index", page = UrlParameter.Optional })
对于上例中的可选参数page,可以在定义action时指定其默认值。如果从默认值的角度看,默认参数与可选参数起到的作用非常近似,但可选参数对于action而言并非固定的存在。
public class AdminController
{
public ViewResult Index(int page = 5) { .... }
}
此外,还有可变长度的路由变量,类似于用C#中params关键字定义的可变长度的方法参数。具体地,是在占位符名称前加上*,就象{*arguments}这样。之后,路由框架会按照路径模式对URL逐段进行匹配,并将末端失配的所有片段作为一个由/分隔的串传递给参数arguments。在action内部,可以对arguments进行分割与解析,做出自己需要的其他处理。
接下来,是路由项中的namespace,该参数确定该路由项在查找controller时可以参考的命名空间。MVC将先在该参数给定的命名空间中查找controller,查找未果后才去其可触及的其他命名空间查找。可以通过设置路由项的DataTokens["UseNamespaceFallback"] = false来强迫只在指定空间查找。
namespaces参数给出的命名空间,无论其顺序如何,其拥有的controller具有同等的优先权。如果不同空间下有同名controller存在,将直接导致同名冲突。为避免冲突,可以分别为这些冲突的controller所属命名空间定义不同的路由项,并适当考虑将哪一条路由项放在更前面。
最后还有一个略复杂些的参数constraints,它决定了该路由项适用的条件。若干条件之间,为AND的关系,即须同时满足。约束既可以是简单地利用正则匹配,就象下面这样仅当controller名以H开头,action为Index或About时才适用此路由项。
MapRoute("MyRoute",
"{controller}/{action}/{id}/{*arguments}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = "^H.*", action = "^Index$|^About$" },
new[] { "SomeNamespace.Controllers" });
更复杂一些的约束,则要通过实现了IRouteConstraint接口的约束对象来提供,其重点是实现该接口中的bool Match()方法。然后往上面的约束表中简单地加入一段ieConstraint = new AgentIEConstraint()即可实现对浏览器内核的筛选。
public class AgentIEConstraint : IRouteConstraint
{
public bool Match(HttpContextBase context, Route route, string name, RouteValueDictionary values, RouteDirection direction)
{
return (context.Request.UserAgent != null)
&& (context.Request.UserAgent.Contains("IE");
}
}
输出URL
路由系统的另一重要功能,是生成URL。这主要是通过Html.ActionLink()、Html.RouteLink()、Url.Action()、Url.RouteUrl()等方法实现的,他们作用相当、参数近似,又以ActionLink()生成<a>标签指向特定的action较为常用,RouteLink()则可以直接选择特定路由并指向具体的文件、文件夹等资源。
尽量避免用手工方式定义URL,这实在太危险!
ActionLink(string text, //链接显示的文本
string action, //action名称
string controller, //controller名称
string protocol, //URL 协议,如“http”或“https”
string host, //URL中 的主机名
string fragment, //URL 片段名称(定位点名称)
object routeValues, //一个包含路由参数的对象
object htmlAttributes) //一个对象,其中包含要为该元素设置的 HTML 特性
URL的解析与输出,并不当然的是一个互逆的过程。
引入Areas进行分区
分区,是对URL更精细的一种路由手段。
理解ASP.NET MVC的路由系统的更多相关文章
- ASP.NET Web API路由系统:路由系统的几个核心类型
虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分(定义在程序集System.Web.Http.dll中)已经移除 ...
- asp.net MVC URL路由入门指南
asp.net MVC 的URL路由是一个非常强大的功能,而且有个优点:强大单不复杂.然而,目前我在网上看到的相关资料,却都仅仅提供一些示例,仅通过这些示例,初学者基本上不可能明白为什么要这么配置,更 ...
- 深入理解ASP.NET MVC(目录)
学ASP.NET MVC2有一段时间了,也针对性的做了个练习.感觉这个框架还是不错的,所以决定要深入系统的学习一下.看到这样一本书: 作者博客:http://blog.stevensanderson. ...
- 深入理解ASP.NET MVC(5)
系列目录 回顾 系列的前4节深入剖析了ASP.NET URL路由机制,以及MVC在此基础上是如何实现Areas机制的,同时涉及到inbound和outbound很多细节部分.第2节中提到MvcRout ...
- ASP.NET Web API路由系统:Web Host下的URL路由
ASP.NET Web API提供了一个独立于执行环境的抽象化的HTTP请求处理管道,而ASP.NET Web API自身的路由系统也不依赖于ASP.NET路由系统,所以它可以采用不同的寄宿方式运行于 ...
- 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC
系列文章 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)——ASP.NET MVC 数据传递 七天学会ASP.NET MVC (三)— ...
- ASP.NET MVC 自定义路由中几个需要注意的小细节
本文主要记录在ASP.NET MVC自定义路由时,一个需要注意的参数设置小细节. 举例来说,就是在访问 http://localhost/Home/About/arg1/arg2/arg3 这样的自定 ...
- [转载]深入理解ASP.NET MVC之ActionResult
Action全局观 在上一篇最后,我们进行到了Action调用的“门口”: 1 if (!ActionInvoker.InvokeAction(ControllerContext, actionNam ...
- 返璞归真 asp.net mvc (2) - 路由(System.Web.Routing)
原文:返璞归真 asp.net mvc (2) - 路由(System.Web.Routing) [索引页] [源码下载] 返璞归真 asp.net mvc (2) - 路由(System.Web.R ...
随机推荐
- 【C++】指针数组和数组指针
首先的首先,稍微抱怨一下阿里今天的严重失误.说好的晚六点笔试,说好的务必提前半小时到场.六点十五的时候告诉闷在一个大教室里躁动的我们“今天七点半开考,大家先回去吧,七点半再过来”,满脸黑线…等到七点半 ...
- linux 安装python,pip,
Linux下python升级步骤 http://www.cnblogs.com/lanxuezaipiao/archive/2012/10/21/2732864.html 在 https://www. ...
- 虚拟专用网络VPN
寒假回到家里需要下载论文,怎样才能访问学校图书馆的数据库呢?解决方法是学校图书馆在内网中架设一台VPN服务器,VPN服务器有两块网卡,一块连接内网,一块连接公网.然后就可以通过互联网找到VPN服务器, ...
- Visual Studio 快捷键
Visual Studio 快捷键 CTRL + DELETE 删除至词尾 CTRL + BACKSPACE 删除至词头Ctrl+Shift+L: 删除当前行 Ctrl+K+Crtr+C: 注释选定内 ...
- mysql之视图
视图 视图是虚拟的表.与包含数据的表不一样,视图只包含使用时动态检索数据的查询. 理解视图最好的办法就是来看一下例子: SELECT cust_name , cust_contact FRO ...
- 【poj3243-Clever Y】高次同余方程-拓展BabyStepGiantStep
http://poj.org/problem?id=3243 题意:给定X,Z,K,求一个最小的Y满足XY mod Z = K. 关于拓展BSGS的详细解释我写了一篇博文:http://www.cnb ...
- zend studio10破解
http://blog.csdn.net/qq1355541448/article/details/16807429
- lintcode:移动零
题目 给一个数组 nums 写一个函数将 0 移动到数组的最后面,非零元素保持原数组的顺序 注意事项 1.必须在原数组上操作2.最小化操作数 样例 给出 nums = [0, 1, 0, 3, 1 ...
- 【PSR规范专题(4)】PSR-3 日志接口规范
本文转自:https://github.com/PizzaLiu/PHP-FIG/blob/master/PSR-3-logger-interface-cn.md 本文制定了日志类库的通用接口规范. ...
- 怎样查看Tomcat动态控制台信息
在web项目调试期间,时常需要查看输出的调试信息,例如当hibernate设置为显示SQL语句时,每次运行的SQL语句会输出到终端,另外有时需要在代码中插入一些输出语句,以方便掌握运行情况,但当插入S ...