理解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 ...
随机推荐
- 游戏引擎网络开发者的64做与不做(二A):协议与API
[编者按]在这个系列之前的文章"游戏引擎网络开发者的64做与不做(一):客户端方面"中,Sergey介绍了游戏引擎添加网络支持时在客户端方面的注意点.本文,Sergey则将结合实战 ...
- 【五】PHP数组操作函数
1.输出数组的结构:bool print_r(数组); $arr=array('jack','mike','tom'); print_r($arr);//Array ( [0] => jack ...
- 字典树trie的学习与练习题
博客详解: http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html http://eriol.iteye.com/bl ...
- Twitter注册
Twitter注册 - (一般分享不了是回调地址不对) 1.打开twitter的官网https://dev.twitter.com,如果还没有注册账号的,需要注册账号,已经注册账号的,请先登录: 2. ...
- MetInfo安装
安装MetInfo企业网站管理系统需要经历三个步骤:安装准备.上传文件.安装系统. 第一步:安装前的准备 环境要求:需要支持PHP并提供Mysql数据库的空间(虚拟主机),详细要求: 下载MetInf ...
- 初学Ajax(一)
以下文字根据李炎恢——jQuery教程整理而成. Ajax全称为:“Asynchronous JavaScript and XML”(异步JavaScript和XML),它并不是JavaScript的 ...
- 浅说Java中的反射机制(二)
写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编 ...
- dojo新建widget步骤----主要针对widget路径
一,新建目录 二,新建文件 三,写urtil widget代码 四,写RedTextDialog代码 五,写HTML代码 =====================如有不懂,结合http://blog ...
- dwz ie10一直提示数据加载中
dwz js资源jquery.validate.js 搜索 this.attr('novalidate', 'novalidate'); 在33行左右 用if (typeof (Worker) !== ...
- MSBuild和Jenkins搭建持续集成环境
http://www.2cto.com/os/201409/334323.html http://my.oschina.net/anxuyong/blog/353897 http://www.cnbl ...