本文共 9584 字,大约阅读时间需要 31 分钟。
上一节我们说了controller和action片段。controller和action片段变量对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 RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute","{controller}/{action}/{id}",new { Controller="Home",Action="Index",id="DefaultId"}); } }}
该路由的URL模式定义了标准的controller和action变量,以及一个名为“id”的自定义
变量。这条路由将匹配任何0、3个片段的URL,第三个片段的内容将被赋给id变量,而且如果没有第三个片段,将采用默认值。 通过使用RouteData.Values属性,用户能够在一个动作方法中访问任何一个片段变量。为了对此进行demo的展示,我们需要对对Home控制器添加了一个名为”CustomVariab1e”的动作方法,代码如下所示: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 Customvariable() { ViewBag.controller = "home"; ViewBag.Action = "Customvariable"; ViewBag.Customvarialbe = RouteData.Values["id"]; return View(); } }}
该方法获得了路由URL模式中自定义变量的值,并用ViewBag将它传递给视图。为了给动作方法创建视图,创建Views/Home文件夹,并右击该文件夹,从弹出的菜单中选择”Add WC5 View Page(添加MVC5视图页(Razor))”,设置名称为CustomVariable.cshtml。单击“OK(确定)”按钮创建该视图,视图中带如下:
@{ Layout = null;}Custom Varialbe The Controller is :@ViewBag.ControllerThe action is :@ViewBag.ActionThe Custom Variable is : @ViewBag.Customvarialbe
为了看到自定义片段变量的效果,启动应用程序,并导航到/Home/CustomVariable/HelloTheWorld网址,便会调用Home控制器中的CustomVariable动作方法,于是会通过ViewBag接收到这个自定义片段变量的值,并将其传递给视图。
由于我们己经为路由中的id片段变量提供了一个默认值,这意味着,如果导航到/Home/CustomVariable,我们将会看到如下结果。 好啦,我们通过上面一个简单的例子,我们已经大致了解了mvc中的自定义的片段变量等。下面我们来说一下具体的说法。如果以URL模式中的变量相匹配的名称,来定义动作方法的参数,MVC框架将把从URL获得的值作为参数传递给该动作方法。意即,如果把路由的片段变量名作为动作方法的参数,MVC框架会用路由系统所获得的片段变量的值自动地为动作方法参数赋值。我们修改一下home控制器中的CustomVarable方法,使其有个参数:
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 Customvariable() //{ // ViewBag.controller = "home"; // ViewBag.Action = "Customvariable"; // ViewBag.Customvarialbe = RouteData.Values["id"]; // return View(); //} //有参数 public ActionResult Customvariable(string id) { ViewBag.controller = "home"; ViewBag.Action = "Customvariable"; ViewBag.Customvarialbe = id; return View(); } }}
当路由系统根据上述代码中所定义的路由来匹配一个URL时,URL中第三个片段的值被赋值给自定义变量id。MVC框架会将片段变量列表与动作方法参数列表进行比较,如果名称匹配,便将URL的值传递给该方法。
代码中将id参数定义成一个string,但MVC框架会尝试将URL的值转换成所定义的任何参数类型。如果将id参数声明为int或DateTime,那么,从URL模式接收到的将是一个被解析成该类型实例的值。这是一个雅致而有用的特性,该特性我们不需要自己去处理这种转换。
可选URL片段是指,用户不需要指定,但又未指定默认值的片段。下面的代码中通过将默认值设置为”Urlparameter.optional”,便指明了一个片段变量是可选的。
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) { //可选的url片段 routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { Controller = "Home", Action = "Index", id = UrlParameter.Optional }); } }}
这条路由将匹配不管是否提供id的URL。下表将展示它对不同URL的解析方式。
片段 | 示例url | 映射结果 |
---|---|---|
0 | controller=Home;action=Index | |
1 | controller=Customer;action=Index | |
2 | controller=Customer;action=List | |
3 | controller=Customer;action=List id=all | |
4 | 不匹配一一片段太多 |
由表可见,只有当输入URL中存在相应片段时,id变量才会被添加到变量集合中。如果你需要了解用户是否为某个片段变量提供了一个值,这一特性是有用的。当未为可选片段变量提供值时,对应的参数值将为null,我们需要对Home控制器代码更新,以响应无提供值的id片段变量。
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace urlAndRoutes.Controllers{ public class HomeController : Controller { //参数可选 public ActionResult Customvariable(string id) { ViewBag.controller = "home"; ViewBag.Action = "Customvariable"; ViewBag.Customvarialbe = id ?? ""; return View(); } }}
如果你十分注重MVC模式中的关注分离,不喜欢将片段变量的默认值放在应用程序
的路由中,你可以使用C#的可选参数,以及路由中的可选片段变量,来定义动作 方法参数的默认值。作为一个示例,我们修改home控制器中的Customvariable动作方法,以便在URL无值的情况下,为要用的id参数定义默认值。using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace urlAndRoutes.Controllers{ public class HomeController : Controller { //有参数,强制分离 public ActionResult Customvariable(string id="DefaultId") { ViewBag.controller = "home"; ViewBag.Action = "Customvariable"; ViewBag.Customvarialbe = id ; return View(); } }}
这将使id参数总会有一个值(或是来自URL,或是取自默认值),这样我们也就不需要用处理null值的代码了。这一方法的使用结合下面的路由方式:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { Controller = "Home", Action = "Index", id = UrlParameter.Optional });
在功能上等价于这条路由:
routes.MapRoute("MyRoute","{controller}/{action}/{id}",new { Controller="Home",Action="Index",id="DefaultId"});
改变URL模式默认保守性的另一种方式是接收可变数目的URL片段。这让你能够以一个单一的路由,对任意长度的URL进行路由。通过指定一个叫作“全匹配(catchall)”的片段变量,并以星号(*)作为其前缀,便可以定义对可变片段数的支持,如下 所示
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}/{id}/{*catchall}", new { Controller = "Home", Action = "Index", id = UrlParameter.Optional }); } }}
上述代码添加了一个catchall片段变量,官方将该片段变量形象地称为”catchall(全匹配)”。现在,这条路由将匹配任何URL,无论URL包含多少片段数,也不管这些片段的值是什么。前三个片段分别用于设置controller、action和id变量的值。如果URL含有更多片段,则它们全部被赋给catchall变量,如下表所示。
片段数 | 示例url | 映射结果 |
---|---|---|
0 | controller=Home;action=Index | |
1 | controller=Customer;action=Index | |
2 | controller=Customer;action=List | |
3 | controller=Customer;action=List;id=All | |
4 | controller=Customer;action=List;id=All;catchall=Delete | |
5 | Controller=Customer;action=List;id=All;catchall=Delete/Perm |
当一个输入请求URL与一条路由进行匹配时,MVC框架取得controller变量的值,并查找相应的(控制器)名称。例如,当controller变量的值是“Home”时,那么,MVC框架会查找名称为“HomeController”的控制器。
其实这是一个不合格的类名,意即如果有两个或多个名为”Homecontroller”的类位于不同的命名空间时,MVC框架将不知道该怎么做。 为了解决这个问题,请在示例项目的根目录下创建一个名为AdditionalControllers的文件夹,并添加一个新的Home控制器。using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace urlAndRoutes.AdditionalControllers{ public class HomeController : Controller { // GET: Home public ActionResult Index() { ViewBag.Controller = "Addtional Cotrollers Home"; ViewBag.Action = "Index"; return View("ActionName"); } }}
启动程序
分析错误页面:MVC框发现有两个:了名为HomeController,一个在原始的RoutesAndUrls.Controllers命名空间中,另一个在RoutesAndUrls.AdditionalControllers命名空间中。如果阅读错误页的错误文本,便可以看出,MVC框架很有帮助性地告诉了我们,它发现了哪些类。 这个问题会经常出现,尤其是如果你在一个大型的MVC项目上工作,而该项目采用 了其他开发团队或第三方供应商所提供的控制器库。例如,命名一个与用户账号相关的控制器为”Accountcontroller”,这是很自然的。因此为了解决这一问题,可以告诉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 RouteConfig { public static void RegisterRoutes(RouteCollection routes) { //命名空间区分控制器的顺序 routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { Controller = "Home", Action = "Index", id = UrlParameter.Optional }, new[] { "urlAndRoutes.AdditionalControllers" } ); } }}
该清单把这些命名空间表示成一个字符串数组,以上代码告诉MVC框架,在考查其他空间之前,先考查URLsAndRoutes.AdditionalControllers命名空间。 因为在同一个路由中添加命名空间的优先级是相同的,所以如果需要添加多个命名空间的时候,需要如下做法。
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( name: "AdditionalControllers", url: "{controller}/{action}/{id}/{*catchall}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, namespaces: new[] { "urlAndRoutes.AdditionalControllers" }); routes.MapRoute( name: "myroute", url: "{controller}/{action}/{id}/{*catchall}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, namespaces: new[] { "urlAndRoutes.Controllers" }); } }}
当然,也可以告诉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 RouteConfig { public static void RegisterRoutes(RouteCollection routes) { Route AdditionalControllers= routes.MapRoute( name: "AdditionalControllers", url: "{controller}/{action}/{id}/{*catchall}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, namespaces: new[] { "urlAndRoutes.AdditionalControllers" }); AdditionalControllers.DataTokens["UseNamespaceFallback"]= false; } }}
源码下载: