004_URL 路由 - 高级路由特性
在视图中生成输出URL
使用路由系统来生成输出URL,能够确保URL方案动态地产生URL。
在视图中生成输出URL的最简单做法是在视图中调用Html.ActionLink辅助器方法,如:
<div>@Html.ActionLink("This is an outgoing URL", "CustomVariable")</div>
注意,ActionLink方法生成的HTML是基于当前路由配置的。如果路由方案是下面这样,
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }); }
则将得到这样的:HTML:<a href=”/Home/CustomVarible”>This is an outgoing URL</a>
但是,如果将路由方案改为下面这种方式:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("NewRoute", "App/Do{action}", new { controller = "Home" }); routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }); }
则将会得到:<a href=”/App/DoCustomVarible”>This is an outgoing URL</a>
因此,可以看出使用路由系统生成输出URL的方式可以方便的实现自动对路由配置的更改进行响应。这也方便了维护——通过修改路由方案,在视图中的输出链接会自动地反映出这种修改。
知识点:
路由系统是按照路由被添加的顺序来处理路由的,路由会被添加到传递给RegisterRoutes方法的RuteCollection对象中。每一条路由再被检测是否是一个匹配时,都需要满足三个条件:
- URL模式中定义的每一个片段变量都必须有一个可用的值。为了找到每个片段变量的值,路由系统首先查看已经提供的值(采用匿名类型的属性),然后查看当前请求的变量值,最后查看该路由中定义的默认值。
- 在给片段变量提供的值中,应当没有违背这条路由所定义的只默认变量的值。只默认变量是为其提供了默认值,但未在URL模式中出现的变量。如在匹配这条路由(routes.MapRoute("MyRoute", "{controller}/{action}",new { myVar = "True" });)时就要小心,不要给myVar(只默认变量:为其提供了默认值“true”,但未在URL模式中出现——如果未在URL模式中为其提供值)提供值,或要确保所提供的值与这个默认值是匹配的。
- 所有片段变量的值均必须满足路由约束。一定注意,路由系统不会试图查找最佳匹配路由,它只会找出最先匹配者,然后用这条路由来生成URL,任何后续的路由都会被忽略。所以,应该首先定义最具体的路由。
为各个片段参数所选择参数值将在其中被替换,即在Html.ActionLink给片段提供的值,会替换掉生成URL中的相关默认值——这些默认值是在路由配置中设定的。同时,将忽略尾部的默认值序列。如果明确地提供一些参数,但它们与片段参数或默认参数不符,那么方法将把这些参数以“名字/值”对的查询字符串形式进行追加。
以其他控制器为目标
ActionLink方法默认以当前控制器为目标创建输出URL,如果需要创建一个以不同控制器为目标的输出URL,则可以使用它的一个重载版本,它允许指定控制器名称。需要注意的是,路由系统不会对动作方法和控制器的值进行检验,不要指定不存在的目标。
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>ActionName</title> </head> <body> <div>The controller is:@ViewBag.Controller</div> <div>The action is:@ViewBag.Action</div> <div>@Html.ActionLink("This is an outgoing URL", "CustomVariable")</div> <div> <!--通过指定控制器来实现一个以不同控制器为目标的输出 URL --> @Html.ActionLink("This targets another controller", "Index", "Admin") </div> </body> </html>
上述加粗部分使用了ActionLink的一个重载版本,其指定了目标控制器及动作,当视图渲染时,最终会生成以下HTML结果:<a href=”/Admin”>This targets another controller</a>。由于Index为默认动作方法,因此,可以省略默认的动作片段,这也是路由系统所允许的。
传递额外的值
可以使用一个匿名类型为一些片段变量传值,在这个匿名类型中一其属性表示片段。如下代码所示:
<div> <!--使用匿名类型给片段变量提供值--> @Html.ActionLink("This is an outgoing URL", "CustomVariable", new { id = "Hello" }) </div>
上面这段代码将会产生这样的HTML:
<a href="/App/DoCustomVariable?id=Hello">This is an outgoing URL</a>
根据前面的路由模式设置,为id提供的值被作为查询字符串添加到了URL,其原因是为了实现与路由设置的URL模式相匹配。前面的路由设置如下:
routes.MapRoute("NewRoute", "App/Do{action}",new { controller ="Home" });
因此,如果路由设置中只有一个与之匹配的路由,那将会得到不同的结果——复制给id属性的值被作为URL片段包括在URL中。即如果只有一条这样的与之匹配的路由:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
HTML结果:<a href="/App/CustomVariable/Hello">This is an outgoing URL</a>
变化之处已用粗体标出。
知识点:
片段变量的重用
在为一条路由URL模式中的每一个片段变量查找值的过程中,路由系统将考查当前请求的值,就如前面谈到输出URL时所说的,路由系统为了找到每个片段变量的值,会首先查看已经提供的值(采用匿名类型的属性),然后查看当前请求的变量值,最后查看该路由定义的默认值。
假设只有一条路由,如下:
routes.MapRoute("MyRoute", "{controller}/{action}/{color}/{page}");
现在假设一个用户的当前URL为/Catalog/List/Purple/123,并渲染一个这样的链接:
@Html.Action(“Click me”,”List”,”Catalog”,new {page=789},null)
虽然,未提供color片段变量的值,同时也未定义默认值,但路由系统将会根据已经的定义的路由进行匹配,并生成这样的HTML:
<a href=”/Catalog/List/Purple/789”>Click me</a>
原因是,路由系统查找了当前请求的变量值——它重用了输入URL的片段变量值,color的值最终被赋成了Purple。
需要注意的是,在开发的过程中不要依赖路由系统的这种行为,因为,路由系统将只对某些片段变量使用重用值,这些片段变量在URL模式中的出现早于提供给Html.ActionLink方法的参数。如果下面这样的链接将不会被匹配:
@Html.Action(“Click me”,”List”,”Catalog”,new {color=”Aqua”},null)
这里未提供值的片段page在URL模式中的出现晚于已提供值的color片段,因此不能使用重用值,于是这条路由将不被匹配。
因此,这里强烈建议不要依赖与路由系统的这种行为,并应为URL模式中的所有片段变量都提供值。
指定HTML标签属性
通过提供一个匿名类型,可以为元素设置标签属性,该匿名类型的属性与所需要的标签属性相对应。如下面示例中对id标签属性的设置,并为其所在的a元素赋值了一个CSS的class:
<div> <!--生成一个带有标签属性的锚点元素。class 之前使用@字符作为前缀,这是一条 C# 语言特性,它让用户能够用保留关键字作为 class 成员的名字(即以 class 作为该匿名类型的一个成员)--> @Html.ActionLink("This is an outgoing URL", "Index", "Home", null, new { id = "myAchorID", @class = "myCSSClass" }) </div>
上述这段语句将被渲染成这样的HTML:
<a class=”myCSSClass” href=”/” id=”myAnchorID”>This is an outgoing URL</a>
生成链接中的全限定URL
前面介绍的生成的链接都是相对URL,但我们还可以使用ActionLink辅助器方法生成全限定的URL。如:
<div> <!--生成权限定 URL --> @Html.ActionLink("This is an outgoing URL", "Index", "Home", "https", "myserver.mydomain.com", " myFragmentName", new { id = "myId" } , new { id = "myAchorID", @class = "myCSSClass" }) </div>
最终会生成这样的HTML:
<a class=”myCSSClass” href=”https://myserver.mydomain.com/Home/Index/MyId#myFragmentName” id=”myAnchorID”>This is an outgoing URL</a>
建议:在实际项目中尽可能使用相对URL,权限定URL会形成对应用程序的基础架构方式产生依赖性。
生成URL(而不是链接)
使用Html.ActionLink辅助器方法可以生成完整的HTML的<a>元素,但如果需要,还可以使用Url.Action方法只生成URL,而不产生HTML元素。如下代码所示:
<div> This is a URL: @Url.Action("Index", "Home", new { id = "MyId" }) </div>
除了只生成URL外,它与Html.ActionLink方法的工作方式相同。
在动作方法中生成输出URL
有时我们需要在动作方法中输出URL,如果仅需生成URL,可以采取与在视图中同样的辅助器方法,如下:
/// <summary> /// 在动作方法中生成输出 URL /// </summary> /// <returns></returns> public ViewResult MyActionMethod() { string myActionUrl = Url.Action("Index", new { id = "MyID" }); string myRouteUrl = Url.RouteUrl(new { controller = "Home", action = "Index" }); // ... 用此 URL 做些事情... return View(); }
对于上面这段代码,myActionUrl 的值将是“/Home/Index/MyID”,而myRouteUrl 将为“/”。
通常,需要将客户端浏览器重定向到另一个URL。使用RedirectToAction方法可以重定向到另一个动作,该方法指示MVC框架把一条重定向指令发布给一个URL,由这个URL调用指定的动作。如:
/// <summary> /// 重定向到另一个动作 /// </summary> /// <returns></returns> public RedirectToRouteResult MyActionMethod() { return RedirectToAction ("Index"); }
该方法的一些重载版本可以给生成的URL中片段变量指定控制器和值。
如果需要用一个URL发送一个重定向,而这个URL只是根据对象的属性生成,则还可以使用RedirectToRoute方法,如:
/// <summary> /// 重定向到根据匿名类型中的属性生成的URL /// </summary> /// <returns></returns> public RedirectToRouteResult MyActionMethod() { return RedirectToRoute(new { controller = "Home", action = "Home", id = "MyID" }); }
根据指定路由生成URL
下面介绍一下如何通过指定的路由生成URL或链接,当然在实际的项目中根据指定路由生成URL是不可取的,主要是因为在视图或动作方法中使用指定的路由名生成URL,等同于在建立视图或动作方法与路由自己的依赖性,而这与MVC思想是不一致的。好了,这里咱们只需要通过这种方式来理解这一过程的实现原理:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}"); routes.MapRoute("MyOtherRoute", "App/{action}", new { controller = "Home" }); }
上面为两条路由指定了名称——MyRoute和MyOtherRoute。对路由命名的原因主要有两个:
- 作为路由目的的一种说明
- 便于选择特点的路由,用以生成输出URL
由于前面路由配置已给定了路由顺序,所以,当使用下面的Html.ActionLink方法生成的链接将总是由MyRoute生成,如:@Html.ActionLink(“Click me”,”Index”,”Customer”)
得到的结果:<a href=”/Customer/Index”>Click me</a>
可以用Html.RouteLink方法(该方法可以指定路由生成链接)覆盖这种默认路由匹配行为,这种方法需要指定一条路由(如下面粗体的“MyOtherRoute”,而斜体的粗体字“Customer”被重写了),如:
@Html.RouteLink(“Click me”,”MyOtherRoute”,”Index”,”Customer”)
结果:
<a length=”8” href=”/App/Index?Length=5”>Click me</a>
说明:就像开头说明的这种以指定路由生成URL不是明智之举,但是如果我们需要明确路由的意图时最好还是改用注释来提示。