理解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 ...
随机推荐
- AwSnap:让全版本(Windows、iOS、Android)Chrome浏览器崩溃的有趣漏洞
彩蛋爆料直击现场 几周前,我们曾报道了13个字符导致Chrome崩溃的漏洞.然而,这个漏洞有个小小的遗憾,那就是它只在MAC OS X下生效,其他系统并不受影响. 现在,我们又有了一个更有趣的漏洞.黑 ...
- 十大技巧优化Android App性能
无论锤子还是茄子手机的不断冒出,Android系统的手机市场占有率目前来说还是最大的,因此基于Android开发的App数量也是很庞大的. 那么,如何能开发出更高性能的Android App?相信是软 ...
- 2014多校第十场1004 || HDU 4974 A simple water problem
题目链接 题意 : n支队伍,每场两个队伍表演,有可能两个队伍都得一分,也可能其中一个队伍一分,也可能都是0分,每个队伍将参加的场次得到的分数加起来,给你每个队伍最终得分,让你计算至少表演了几场. 思 ...
- Heroku 与 ASP.NET 5
一. Heroku 简单来讲,Heroku是一个支持多种语言.极易部署.多价位可免费的 Pass 平台,通过 Buildpack 搭建语言运行环境, 默认内建的大部分是 Web 开发中较为常见的语言, ...
- Android Activity 阻止软键盘自动弹出
在AndroidManifest.xml里面 选择那个acitivity, 把他的window soft input mode设置成stateHidden和 adjustUnspecified < ...
- dom对象详解--document对象(二)
dom对象详解--style对象 style对象 style对象和document对象下的集合对象styleSheets有关系,styleSheets是文档中所有style对象的集合,这里讲解的 ...
- linux登陆欢迎提示信息的设置
Linux可以设置登录前后的欢迎信息,虽然没啥技术含量,但却是非常实用的一个小技巧. 实现登录消息的功能,可以修改3个文件. 1./etc/issue 本地登陆显示的信息,本地登录前 2./etc/i ...
- libprotobuf ERROR
google/protobuf/wire_format.cc:1059] Encountered string containing invalid UTF-8 data while parsing ...
- iOS 开发--NSMutableArray使用枚举方法
可变数组也可以使用枚举方法, 我们在这里提供了两种枚举方法, 一个是正序枚举, 一个是倒序枚举, 在正序枚举中, 元素的个数和顺序都是不可以修改的, 但是在倒序枚举中却可以修改, 这有些耐人寻味. 涉 ...
- 【web性能】页面呈现、重绘、回流
在讨论页面重绘.回流之前.需要对页面的呈现流程有些了解,页面是怎么把html结合css等显示到浏览器上的,下面的流程图显示了浏览器对页面的呈现的处理流程.可能不同的浏览器略微会有些不同.但基本上都是类 ...