MVC笔记 网址路由与MVC的生命周期

一、网址路由

1.1  比对通过浏览器传来的HTTP请求

客户端对ASP.NET网站发出请求时,能通过R偶汤尼盖找到适当的HttpHandler来处理网页,大致的流程如图:

如果HttpHandler是由MvcHandler来处理,那么,此时就会进入MVC的执行生命周期,并且会找到适当的Controller与Action来对其进行处理,并将信息反馈给客户端。

1.2 将适当的网址返回浏览器

网址路由的另一个用途是决定MVC 应该输出什么样的网址并将其返回浏览器,跳转地址或在View中显示超链接时,都需要参考网址路由的定义,因为这样才能动态决定MVC输出网址应该是什么。

1.3  默认网址路由

Global.asax已经定义了两个默认的网址路由,参考图如下:

      ① 所有ASP.NET Web应用程序执行的入口都是HttpApplication的Application_Start()事件,所有的MVC Routing都会在此定义。其中,RoutTable.Routes是一个公开的静态对象,用于存储所有Routing的规则集(RouteCollection类)。

② 默认RegisterRoutes()方法中的IgnoreRoute()辅助方法用于定义不需要通过Routing处理的网址。

 ③ "{resource}"代表一个名为"resource"的Route Valueg表达式,但其实这里取任何名字都可以,它只是代表一个变量空间(PlaceHolder类),总之就是代表一个位置,用于放置一个用不到的变量。

 "{*pathInfo}"代表一个名称为"pathInfo"的RouteValue表达式,名称前面的星号(*)的意思是"CatchAll"(抓到全部)。这个名为"pathInfo"的RouteValue表达式的值是完整的路径信息(Path Info)中扣除在中比对到的剩余的网址。例如:网址是"/TEST.axd/a/b/c/d",则"{PathInfo}"的值为"a/b/c/d",如果没有加上星号,"{PathInfo}"的值应为"a"。其实在这里取任何名字都可以,因为它只代表一个变量的位置。

 ⑤ MapRoute()是最常用来定义Routing规则的辅助方法。

 定义Route的名称,在此为"Default"。

 定义网址格式和每个网址段落(PathSegment)的RouteValue表达式名称。

TIP : 该网址不能以斜线(/)开头。

 定义各RouteValue表达式的默认值,当网址路由比对不到HTTP请求网址时,就会改以默认值替代。

二、HTTP请求的URL如何对应网址路由

由于默认定义了两个网址路由,按照ASP.NET Routing的规则,当HTTP提出请求后,URL会进行网址路由的比对,而且是由上而下地一条一条比对,直到比对到符合HTTP请求的网址为止。

2.1 网址路由范例

下面举例,弄清URL和Routing之间更多概念上的联系。

[1]. http://localhost/Trace.axd/a/b/c/d/e
TIP :所有的网址都是从"http://localhost/"之后开始比对的!

  • 比对顺序

(1)比对routes.IgnoreRoute命名空间的"{resource}.axd/{*pathInfo}"网址格式。

(2)"{resource}.axd"比对到"Trace.axd",因此继续比对下一个RouteValue表达式。

(3)比对"{*pathInfo}",得到"a/b/c/d/e"。

(4)因为所有的RouteValue表达式都比对成功,所以该HTTP请求会由此网址路由提供服务。

  • 比对结果

该网址使用routes.IgnoreRoute命名空间进行处理,即,MVC为忽略此请求,改为ASP.NET架构本身继续进行处理。

[2] http://localhost/Trade.axd

  • 比对顺序

(1)比对routes.IgnoreRoute命名空间的"{resource}.axd/{*pathInfo}"网址格式。

(2)"{resource}.axd"比对到"Trace.axd",因此继续比对下一个RouteValue表达式。

(3)比对"{*pathInfo}",由于请求的部分已经没有数据,所以理论上不会比对到任何结果。但由于"{*pathInfo}"属于CallAll,此语法会比对到包括空字符串在内的全部内容,所以这个部分也算比对成功,只是"{*pathInfo}"为空而已。

(4)因为所有的RouteValue表达式都比对成功,所以该HTTP请求会由此网址路由提供服务。

  • 比对结果

该网址使用routes.IgnoreRoute命名空间进行处理,即,MVC为忽略此请求,改为ASP.NET架构本身继续进行处理。

[3] http://localhost/Member/Detail?id=123

  • 比对顺序

(1)比对routes.IgnoreRoute命名空间的"{resource}.axd/{*pathInfo}"网址格式。

(2)比对请求URL的的一部分,即"Member",由于没有比对到".axd",所以比对失败。

(3)跳转到routes.MapRoute命名空间的"{controller}/{action}/{id}"网址格式。

(4)比对请求URL的第一部分,即"Member",并且比对到{controller}参数。

(5)比对请求URL的第二部分,即"Detail",并且比对到"{action}"参数。

(6)接下来的"?id=123"就不算是网址的一部分了,所以它不会被算进RouteValue表达式中,因此不会在对此比对了。

(7)"{id}"部分因为没有比对到,所以会读取默认值,也就是"UrlParameter.Optional"部分。由于存在默认值,所以也算比对成功。

(8)因为所有的RouteValue表达式都比对成功,所以该HTTP请求会由此网址路由提供服务。

  • 比对结果

该网址使用routes.MapRoute命名空间进行处理,并调过MvcHandler将值赋予适当的Controller和Action程序。在这里会对应MemberController的Detail动作。

TIP :在"URL及参数"位置出现的所有路由参数都是必要的参数,必须完全符合必读规则才能比对成功;如果比对失败,就会调至下一条网址路由规则继续比对。

2.2 为网址路由加上限制条件

MapRoute()是最常用来定义Routing规则的辅助方法,它有许多应用方式(重载)。一个常用的应用方式为"样式比对规则(正则表达式)+限制条件"。

1            context.MapRoute(
2
3                  "Order_default",
4                  "Order/{controller}/{action}/{id}",
5                  new { action = "Index", id = UrlParameter.Optional },
6                  new { id = @"\d+" }
7             );

我们在MapRoute()辅助方法中设置了4个参数,这些参数指定了一个匿名对象,其中的id属性就是我们比对{id}路由值的限制条件,其限制条件是用正则表达式(Regular Expression)来表示的。"\id+"代表比对到的{id}路由值时必须为数字才算比对成功,而这就是限制条件。

若网址为"http://localhost/Order/Member/Index/123ABC.",由于此对到的{id}路由值不符合限制条件,所以这个网址就算比对失败,接着会自动跳到下一个网址路由规则继续比对。

TIP : 这里定义的正则表达式默认是完全比对。如果你定义的样式为"\id+‘,事实上,在比对时会转换为"^\d+$"。

三、网址路由如何在MVC中生成网址。

URL是如何比对网址路由是网址路由的一个功能,另一个主要功能是在Controller或View中依据网址路由的定义生成适当的网址。下面介绍如何使用RouteTable.Routes.GetVirtualPath命名空间取得动态网址。

先用默认的MVC项目模块进行测试。打开网址http://localhost/Home/About,此请求所得到的路由值如图:


字段



controller


Home


action


About


id


UrlParameter.Optional

在"/Views/Home/About.aspx"页面中添加以下程序代码。

1        <%=
2             RouteTable.Routes.GetVirtualPath(
3                  Request.RequestContext,
4                  new RouteValueDictionary(new { page=1})
5             ).VirtualPath
6         %>

可以看到:在RouteTable.Routes.GetVirtualPath命名空间中,第一个参数为Request.RequestContext,在会输入当前的请求信息,包括RouteValue、QueryString和其他完整的请求;第二个参数中多输入了一个RouteValueDictionary对象,并插入了一个{page}路由值。因此,在获取网址路由之前会先合并出一组路由值,如表所示:


字段



Action


About


Id


UrlParameter.Optional


Page


1

最后,MVC会使用这组新的路由值由上而下——比对路由表(RouteTable)中所有的路由规则,已得到最适合的路由规则,并产生适当的网址。

拿以下路由规则来说,其中定义了3个路由参数,而我们得到的4个路由值中有3个路由值完全符合定义,所以,此网址路由会被选中,并且MVC会以此路由定义好的格式产生网址。

 1           routes.MapRoute(
 2                 "Default",                     // 路由名称
 3                 "{controller}/{action}/{id}",  // 带有参数的 URL
 4                 new
 5                 {                              // 参数默认值
 6                     controller = "Home",
 7                     action = "Index",
 8                     id = UrlParameter.Optional
 9                 }
10             );

由于{page}参数并非网址路由参数之一,因此,新建的{page}参数就被替换成了QueryString参数,输出结果如下。

            /Home/About?page=1

再举一个复杂的例子。第一条路由规则定义如下:

 1           routes.MapRoute(
 2                 "Member",                       // 路由名称
 3                 "Member/{action}/{page}",       // 带有参数的 URL
 4                 new                             // 参数默认值
 5                 {
 6                     controller = "MemberCenter",
 7                     action = "List"
 8                 },
 9                 new
10                 {
11                     action = @"Index|List|Detail",
12                     page = @"\d+"
13                 }
14             );
15             routes.MapRoute(
16                 "Default",                     // 路由名称
17                 "{controller}/{action}/{id}",  // 带有参数的 URL
18                 new
19                 {                              // 参数默认值
20                     controller = "Home",
21                     action = "Index",
22                     id = UrlParameter.Optional
23                 }
24             );

在"/Views/Home/About.aspx‘页面中添加以下程序代码。

 1         <%=
 2             RouteTable.Routes.GetVirtualPath(
 3                   Request.RequestContext,
 4                           new RouteValueDictionary(new {
 5                              controller ="MemberCenter",
 6                              action="Detail"
 7                           })
 8             ).VirtualPath
 9
10         %>

在第2个参数中将"controller"替换成"MemberCenter",将"action"替换成"Detail",所以在获得网址之前,会先合并出一组新的路由值,如表:


字段



controller


MemberCenter


action


Detail


id


UrlParameter.Optional

MVC会用这组新的路由值由上而下一一比对路由表中的所有路由规则,以得到最适合的路由规则。当此对比到第一个规则时,由于已经定义了两个路由参数,分别是"{action}"和"{page}",而我们的路由表中只有"action"没有"page",此时就会查看参数默认值中是否有默认的{page}参数,结果还是没有,因此比对失败。MVC并不会以这个网址路由来产生网址,进而跳至下一条网址路由进行比对。最后,比对的结果为"/MemberCenter/Detail"。

修改"/Views/Home/About.aspx‘页面的程序代码,增加"page"参数,示例如下:

 1        <%=
 2             RouteTable.Routes.GetVirtualPath(
 3                   Request.RequestContext,
 4                           new RouteValueDictionary(new {
 5                              controller ="MemberCenter",
 6                              action = "Detail",
 7                              page = "TEST"
 8                           })
 9             ).VirtualPath
10
11         %>

执行以上操作后就会合并出一组新的路由值(RouteValue),如表:


字段



controller


MemberCenter


action


Detail


id


UrlParameter.Optional


page


TEST

当我们比对第一条规则时,由于已经定义了两个路由参数,分别是{action}和{page},而我们的路由表中已有"action"和"page"的路由值了,必要参数已经全部符合,所以会进一步比对限制条件是否符合。

由于已经定义了{page}参数的限制条件为“@"\d+"”,但路由值中的page值却是"TEST",因限制条件无法通过,所以比对还是失败了。因此,MVC并不会用这个网址路由来产生网址,进而跳至下一个网址路由进行比对。

最后比对结果为"/MemberCenter/Detail?page=TEST"。

总结:使用RouteTable.Routes.GetVirtualPath命名空间来生成网址大致会用到以下判断依据。

  • 若将第1个参数带入Request.RequestContext命名空间,会预先取得当前所有的路由值。也可以输入"null"代表没有默认的路由值。
  • 用当前合并后的所有路由值与网址路由表一一比对所有规则时,会先比对所有必要参数。若比对成功,就会进一步检查限制条件是否符合。
  • 若找不到必要参数,就会查找参数默认值;如果仍然找不到,则比对失败。
  • 若上述比对全部成功,RouteTable.Routes.GetVirtualPath命名空间就会用该网址路由定义的网址来生成网址。

RouteTable.Routes.GetVirtualPath命名空间生成网址的完整流程图。

四、MVC执行的生命周期

MVC的执行生命周期大致上分成3阶段,分别是:

(1)网址路由比对;

(2)执行Controller与Action;

(3)执行View并返回结果。

4.1 网址路由比对

当IIS收到HTTP请求后,会先通过UrlRoutingModule处理所有与网址路由有关的运算。默认下若网址可以对应到网址根目录下的实体文件,就不会通过MVC进行处理,而是直接交由IIS或ASP.NET执行。

若网址是"http://localhostContent/Site.css",由于在网站根目录下有"Content"目录,而且"Content"目录中也有Site.css文件,所有MVC不会将此网址解析成Content控制器和Site.css动作。

再举一个.NET Web Forms的例子,网址为"http://localhost/Member/Login.aspx",在Global.asax文件中新建一个特殊的网址路由,具体如下。

 1           routes.MapRoute(
 2                 "Default_aspx",                     // 路由名称
 3                 "{controller}/{action.aspx}/{id}",  // 带有参数的 URL
 4                 new
 5                 {                                   // 参数默认值
 6                     controller = "Home",
 7                     action = "Index",
 8                     id = UrlParameter.Optional
 9                 }
10             );

在这种情况下,若在网站根目录中有使用.NET Web Forms编写的"/Member/Login.aspx"程序存在,MVC就不会应用UrlRouting,而是将流程的控制权交给IIS,并由IIS将其交友下一个模块执行。在此,就会执行"/Member/Login.aspx"程序。

如果"/Member/Login.aspx"程序不存在,那么MVC的Routing就会正式启动比对,并且在比对到上述网址后,将执行MemberController的Login动作。

若果在Global.asax文件的Application_Start()事件的最前面将RouteTableRoutes.RouteExistingFiles参数值设定为"true",MVC的UrlRouting就不会先判断是否有实体文件存在,程序代码如下:

1        protected void Application_Start()
2         {
3             RouteTable.Routes.RouteExistingFiles = true;
4             AreaRegistration.RegisterAllAreas();
5
6             RegisterRoutes(RouteTable.Routes);
7         }

设定完后,该网站收到的所以HTTP请求都会使用RegisterRoutes()方法中定义的网址路由规则一一进行比对。若比对成功,会用MVC进行处理;否则,将执行的权利交还给IIS。

NOTE : 若在MVC中是由IgnoreRoute()辅助方法比对成功的,会导致程序直接跳离MVC执行生命周期,将程序继续执行的权利交还给IIS,由IIS觉得接下来该由哪个模块(Model)或哪个处理历程(Handler)来执行。

在使用RegisterRoutes()方法定义的网址路由规则进行比对时,事实上,比对成功后,默认值会由MvcRouteHandler决定要将改HTTP请求发送给哪个HttpHandler来执行,如图所示。MVC默认会将HTTP请求交给MvcHandler来执行。

由图所示的MvcRouteHandler程序代码可知,若要自定义RouteHandler,只要自行开发IRouteHandler接口的类,即可通过自定义的RouteHandler来决定通过网址路由比对的网址应该交给哪个HttpHandker来执行。所以,可以通过自定义RouteHandler和HttpHandler来为MVC网站提供辅助功能。

4.2 执行Controller和Action 4.3 执行View并返回结果

当程序执行到MvcHandler时,HttpHandler的入口是ProcessRequest()方法。

时间: 2024-08-14 11:33:50

MVC笔记 网址路由与MVC的生命周期的相关文章

MVC - Routing - 网址路由

1. Routing  : 路由 主要是比对通过浏览器传来的http要求与响应适当的网址给浏览器. ? 1 @Html.ActionLink("关于","About","Home") 这段代码生成的HTML超连接: ? 1 <a href="/Home/About">关于</a> 2. 默认情况下 网址路由规则定义在 App_Start\RouteConfig.cs文档中. ? 1 2 3 4 5 6

[ASP.NET MVC] URL网址路由包含&ldquo;/&rdquo;斜线

ASP.NET MVC 网址包含"/"斜线 笔者碰到一个状况是,URL带入的参数是可能有"/"斜线的(使用者输入)像是以下 1.运行正常 http://localhost:45405/Home/Product?name=HTC/M8/手机套 2.运行正常 http://localhost:45405/Home/Product?name='抗涨'白人牙膏_特大号 到目前为止都还没问题,但我们为了让URL好看以及优化SEO,我们会做URL Rewriting像是以下 1

【Android笔记】(1)—— Activity生命周期与通信

一.Activity 1.Activity状态 活动中:如果Activity在屏幕前,即是可视的,可接受用户的输入. 暂停状态:Activity失去焦点,但仍然可见. 停止状态:如果一个Activity被另一个Activity遮住了,它的状态时停止的. 2.Activity生命周期动作方法 onRestart() : Activity第一次被创建时调用,用于初始化一些数据,如View,此时处于停止状态. onStart(): Activity将要对用户可见时调用,调用后进入停止状态. onRes

Android笔记之多个Activity的生命周期交互

单个Activity: 应用开启时调用:onCreate(),onStart(),onResume(); 按下返回键:onPause(),onStop(),onDestory(); 多个Activity之间交互时: 跳转至第二个Activity时: FirstActivity SecondActivity onPause()     onCreate()   onStart()   onResume() onStop()   按下锁屏键 SecondActivity onPause() onSt

Android学习笔记(五)——活动的生命周期

//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 为了能写出流畅连贯的程序,我们需要了解一下活动的生命周期. 一.返回栈 Android 中的活动是可以层叠的.我们每启动一个新的活动,就会覆盖在原活动之上,然后点击 Back 键会销毁最上面的活动.事实上,Android 是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack) .默认情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位

Android学习笔记(三三):Activity生命周期

Android很大的应用场景是手机,有一些应用具有特别的优先级别,例如电话,同时设备的内存是有限的.因在某些情况下系统将踢走activity,以便是否内存.因此在开发过程中,我们需要管理好activity的生命周期.右图是Android的docs中提供的activity的时间触发图. 四大状态 一般来讲,某一时刻,Activity处在下面四个状态之一: Active:已由用户启动,正在前台运行. Paused:已由用户启动,正在运行且可视,但是由于提示或者其他覆盖部分的屏幕.这是用户可以看到ac

Spring笔记——6.容器中bean的生命周期

spring可以管理单例bean的生命周期,知道何时被创建,核实初始化,何时销毁,也可以进行某些通用资源申请,销毁前资源回收.对于prototype,容器只负责创建,之后就撒手不管自生自灭.容器不知道一共创建了多少prototype,也不知道他们什么时候会被销毁,所以容器没法管理prototype. 管理bean的生命周期有如下两个时机: 注入依赖关系后 即将销毁bean前 依赖关系注入后的行为 通过设定init-method属性指定某个方法或者实现InitializingBean接口指定需要实

Android高级编程笔记(六)Fragment的生命周期

在上一篇博文中对Fragment做了简单的介绍,现在再来探讨一下Fragment的生命周期. 一.Fragment的几种状态: 与Activity类似,Fragment也有一下几种状态: · 活动状态:当前Fragment位于前台,可见,可获得焦点. · 暂停状态:其他Activity位于前台,该Fragment可见,不可获得焦点. · 停止状态:不可见,失去焦点. · 销毁状态:该Fragment被完全删除或被结束. 二.Fragment的回调方法: 在Fragment的生命周期中,有一下方法

Maven实战读书笔记(四):Maven生命周期与插件

Maven的生命周期是对所有构建过程的抽象和统一.包含了项目的清理.初始化.编译.测试.打包.集成测试.验证.部署和站点生成等几乎所有构建步骤. Maven的生命周期是抽象的,其实际行为是由插件来完成的,生命周期和插件两者协同合作,密不可分. 这种思想与设计模式中的模板方法非常相似.模板方法模式在父类定义算法的整体结构,子类通过实现或者重写父类的方法来控制实际行为,这样既能保证算法有足够的可扩展性,又能严格控制算法的整体结构. 4.1 生命周期 Maven拥有3套独立的生命周期:clean.de