本文共 10050 字,大约阅读时间需要 33 分钟。
在使用mvc的过程中,我们经常用到的就是路由,今天我们来了解一下路由的一些东西。
在我们进行路由的讲解之前,我们要做一些准备,新建一个项目。
用”Empty(空)”模板创建一个新的MVC应用程序,并称此项目“UrIsAndRoutes”。
为了演示路由特性,对此示例应用程序添加一些简单的控制器,以此对URL进行解释以调用动作方法的方式,因此,所用的视图模型都是视图包中的一些字符串值,用它们来报告控制器和动作方法的名称。首先,创建一个Home控制器,并设置其内容如下:
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace urlAndRoutes.Controllers{ public class HomeController : Controller { // GET: Home public ActionResult Index() { ViewBag.Controller = "Home"; ViewBag.Action = "Index"; return View("ActionName"); } }}
创建一个customer控制器,代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace urlAndRoutes.Controllers{ public class CustomerController : Controller { // GET: Customer public ActionResult Index() { ViewBag.Controller = "Customer"; ViewBag.Action = "Index"; return View("ActionName"); } public ActionResult List() { ViewBag.Controller = "Customer"; ViewBag.Action = "List"; return View("ActionName"); } }}
创建admin控制器,代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace urlAndRoutes.Controllers{ public class AdminController : Controller { // GET: Admin public ActionResult Index() { ViewBag.Controller = "Admin"; ViewBag.Action = "Index"; return View("ActionName"); } }}
在这些控制器的所有动作方法中,小编指定的都是ActionName视图,这让小编只需定义一个视图,并将它用于整个示例应用程序。在Views文件夹中创建Shared文件夹,并添加一个新的名为ActionName.cshtml的视图,内容如下:
@{ Layout = null;}ActionName The Controller is :@ViewBag.ControllerThe action is :@ViewBag.Action
此时启动下面,如下图:
路由系统用一组路由来实现它的功能。这些路由共同组成了应用程序的URL架构(Schema)或方案(Scheme),这种URL架构(或方案)是应用程序能够识别并能对之做出响应的一组URL。
我们不需要手动输入应用程序中打算支持的各个URL,而是让每一条路由都包含一个URL模式(Pattern),用它与一个输入URL进行比较。如果该模式与这个URL匹配,那么它(URL模式)便被路由系统用来对这个URL进行处理。让我们先从上述示例应用程序的一个URL开始:URLs可以分成几个片段。除主机名和查询字符串之外,这些URL的组成部分都用“/”字符进行分割。在这个示例URL中有两个片段,第一个片段含有单词“home”,第二个片段含有单词“Index”。很显然,第一个片段与控制器有关,而第二个片段与动作有关。
当然,需要以一种路由系统能够理解的方式来表示这种关系。以下是做这件事的一个URL模式:{controller}/{action} 当处理一个输入请求时,路由系统的工作是将这个请求URL与一个模式进行匹配,然后从此URL为这个模式中定义的片段变量提取出相应的值。片段变量用花括号(“{”和“}”字符)表示。上述示例模式有两个片段变量,其名称分别为”controller”和”action”,因此,controller片段变量的值将是home,而action片段变量的值将是Index。 所谓“与一个模式匹配”是指,一个MVC应用程序通常会有几条路由,而路由系统会把输入URL逐一与每条路由的URL模式相比较,直到能找到一条匹配的路由为止(意即,这条路由中的URL模式与这个输入URL匹配)。 默认情况下,一个URL模式将匹配具有正确片段数的任何URL。例如,模式 {controIler}/{action}将匹配任何具有两个片段的URL,如下表:请求URL | 片段变量 |
---|---|
controller=Admin action=Index | |
controller=Index action=Admin | |
Controller=Apples action=Oranges | |
不匹配一一一片段太少 | |
不匹配一一片段太多 |
表中突出了URL模式的两个关键行为:
URL模式是宽松的(Liberal)。如果一个URL正好具有正确的片段数,该模式就会用来为片段变量提取值,而不管这个值可能是什么。
正如前面己经提到的,路由系统并不知道关于MVC应用程序的任何情况,因此,即使不存在从一个URL提取出来的值所对应的控制器或动作,URL模式也会进行匹配(这里的含义是,只要模式匹配,路由系统便会从URL中为片段变量提取值,至于应用程序中是否实际存在相应的控制器和动作,路由系统是不管的)。你可以从表中的第二个例子中看到这种情况的演示。这个例子的URL中调换了Admin和Index片段,因此,从这个URL提取的值也被调换了,尽管示例项目中没有Index控制器。
了解了这些,我们开始正式定义路由啦!!!!!一旦在头脑中有了URL模式,便可以用它来定义一条路由。路由是在RouteConfig.cs文件中进行定义的,该文件位于项目的App_Start文件夹中。内容如下:
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace urlAndRoutes{ public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }}
在RouteConfig.cs文件中定义的静态RegisterRoutes方法是通过Global.asax.cs文件进行调用的,当启动应用程序时,它建立了一些核心的MVC特性。代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace urlAndRoutes{ public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); //RouteConfig·RegisterRoutes方法的调用 } }}
下面将介绍如何在RouteConfig.cs文件的RegisterRoutes方法中,使用URL模式来创建一条路由。(我们删除了原有的url模式)。
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace urlAndRoutes{ public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { Route myRoute = new Route("{controller}/{action}",new MvcRouteHandler()); routes.Add("myRoute",myRoute); } }}
该清单创建了一个新的Route,以URL模式作为一个构造器参数,并将其表示成一个字符串。
另外,还为构造器传递了一个MvcRouteHandIer实例。不同的ASP.NET技术提供了不同的类来定制路由的行为,而这个类(指MvcRouteHandIer类)是用于ASP.NETMVC应用程序的类。一旦创建了这个路由,就可以用Add方法把它添加到RouteCoIIection对象,在其中传递给这条路由所起的名字和已经创建的这条路由。上面是注册路由的方法,下面是另外一种更加方便的方法:
使用在RouteCollection类所定义的MapRoute方法。下面的代码介绍如何用这个方法来注册路由。它和前面的示例有同样的效果,但语法更简洁。using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace urlAndRoutes{ public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}"); } }}
通过运行应用程序,可以看到对路由所做修改的效果。当浏览器试图导航到应用程序的根URL时,你将看到一个错误。
但如果导航到一个与{controIler}/{action}模式匹配的URL,将会出现下图的结果:
上面的配置文件中,简单路由并未告诉MVC框架,如何对根URL的请求进行响应,并且只支持简单的、非常具体的URL模式。在功能上,要比VisualStudio在创建MVC项目时,添加到RouteConfig.cs文件中的路由所具备的功能暂时后退了一步(因为此刻的路由还无法对根URL进行解析)。前面曾解释过,URL模式是保守的,它们只匹配指定片段数的URL。改变这种行为的一个办法是使用默认值。当URL不包含与一个片段匹配的值时,便使用默认值。设置方法如下:
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace urlAndRoutes{ public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}", new { Action = "Index" }); } }}
默认值是作为匿名类型的属性来提供的。上面的代码中为action变量提供了一个Index的默认值。这条路由将匹配所有两片段的URL,就像它之前所做的那样。例如,如果请求,该路山将为controller提取Home值,为action提取Index值。但是现在,由于己为action片段提供了一个默认值,该路由也将匹配单片段URL。当处理单片段URL时,路由系统将从唯一的URL片段中提取controller的值,并对action变量使用默认值。于是,可以请求,并调用Home控制器上的Index动作方法。
可以更进一步,定义根本不含任何片段变量的URL,只依靠默认值来标识controller和action。下面的代码中告诉我们如何为应用程序映射根URL。using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace urlAndRoutes{ public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}", new { Controller="Home", Action = "Index" }); } }}
上面的代码中已经为controller和action变量提供默认值,为了方便解释:这里己经创建了与0个、1个或2个片段的ULR匹配的路由,如下表所示:
片段数 | 实例 | 映射结果 |
---|---|---|
0 | controller=Home ;action=Index | |
1 | controller=Customer;action=index | |
2 | controller=Customer;action=List | |
3 | 不匹配一一片段数太多 |
在输入URL中接收的片段数越少,所依赖的默认值便越多,直到接收一个无片段而只使用默认值的URL。重新启动该示例应用程序,你可以看到默认值的效果一一此时,当浏览器请求应用程序的根URL时,将使用controller和action片段变量的默认值,这将导致MVC框架请求Home控制器上的Index动作方法,如图所示:
并不是一个URL模式中的所有片段都需要是可变的,也可以创建具有静态片段的模式。假设希望匹配以下这种URL,以支持带有Public前缀的URL:,可以通过使用如下代码所示的模式来做这件事。
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace urlAndRoutes{ public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}", new { Controller="Home", Action = "Index" }); routes.MapRoute("","Public/{controller}/{action}",new { Controller="Home",Action="Index"}); } }}
这个新的URL模式将只匹配含有三个片段的URL,第一个必须是Public.其他两个片段可以含有任何值,并将被用于controller和action变量。如果省略后两个片段,那么将使用默认值。
当然,还可以创建既有静态也有可变元素片段的URL模式,比如下面的代码中:
//我是代码片段11using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace urlAndRoutes{ public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("","X{controller}/{action}"); routes.MapRoute("MyRoute", "{controller}/{action}", new { Controller = "Home", Action = "Index" }); routes.MapRoute("","Public/{controller}/{action}",new { Controller="Home",Action="Index"}); } }}
这条路由中的模式匹配任意两片段URL,而第一个片段以字母”x”打头。用于controller的值取自第一个片段除”X”以外的部分,Action值取自第二个片段。如果你启动应用程序并导航到/XHome/Index,可以看到这条路由的效果,结果如图
代码片段11中,定义了一条新路由,并把它放在RegisterRoutes方法中的所有其他路由之前。这么做是因为路由是以它们在RouteC011ection对象中出现的顺序被运用的。MapRoute方法会把一条路由添加到该集合的末尾,让路由以它们被定义的顺序来运用。
路由系统试图根据最先被定义的路由模式来匹配一个输入URL,并且只有在不匹配时,才会继续对下一条路由进行处理。路由被依次尝试,直至找到匹配的一条,或这组路由被尝试完。其结果是,必须首先定义较具体的路由。在代码片段11中所添加的路由要比其后的路由更具体些。假设顛倒路由的顺序,如下所示:
routes.MapRoute("MyRoute", "{controller}/{action}", new { Controller = "Home", Action = "Index" }); routes.MapRoute("","X{controller}/{action}");
那么第一条路由匹配任何具有0、1、2片段的URL,它将是被使用的一条。更具体
的路由现在是列表的第二条,它将是不可到达的。新路由(第二条)去掉URL的前导”x”但旧路由(第一条)却不会这么做,因此,像这样的一条URL:将以名为”XHome”的控制器为目标,而这是不存在的,因此,会导致一个“494一未找到”错误被发送给用户。
可以结合静态片段和默认值为特定的路由创建一个别名。如果你已经公开地发布了URL方案,并且它与你的用户形成了一种契约,那么,创建这种别名可能是有用的。如果在这种情况下(指己与用户形成契约)重构应用程序,则需要保留以前的URL格式。设想以前用的是一个Shop控制器,现在要由Home控制器来替代。代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace urlAndRoutes{ public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("shopdemo","shop/{action}",new { Controller="Home"}); } }}
添加的这条路由匹配第一个片段是”Shop”的任意两片段URL,action的值取自第二个URL片段。这个URL模式不含controller的可变片段,因而会使用所提供的默认值。这意味着对Shop控制器上一个动作的请求会被转换成对Home控制器的请求。
源码下载: