处理发来的URL只是MVC中的一部分,我们也需要生成一些URL植入到我们的view中,让用户点击,并提交表单到目标controller和action,下面会介绍一些生成URL的技巧。
最快速直接的定义外链URL的方法就是手动拼写。比如下面的URL会被放置在view中:
<a href="/Home/About">About this application</a>
这个HTML元素创建了一个链接,当点击该链接,会定位到Home controller的About action。手动定义URL快速减掉。但也是很危险。每次你要修改URL模式的时候,你要修改所有的URL。你不得不搜罗出所以的view,更新涉及到的所有的controller和action方法。
路由系统可以结构化的生成URL,当URL结构变化,view中生成的URL也会改变。这是一个非常明智的方法,只需要做一些工作,会给后期带来巨大的便利。
准备项目
我们使用之前的项目演示,在上面做些修改。
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("MyRoute", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
在视图中生成友好的URL
最简单的在view中生成URL的方式是使用Html.ActionLink方法,如下:
@Html.ActionLink("About this application", "About")
ActionLink方法的参数就是link的文本内容,action方法的名字就是这个link的链接目标。ActionLink方法生成HTML基于当前的路由模式。比如,使用上面定义的路由(假设这个view是由对Home controller请求生成的),那么我们得到的HTML就是如下
<a href="/Home/About">About this application</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 });
}
那么从ActionLink方法中得到的HTML就如下:
<a href="/App/DoAbout">About this application</a>
你能看到如何用这种方法生成链接来处理维护问题。我们可以改变路由模式,让视图中生成的URL链接自动的改变。
应用程序通常都有多个路由,理解如何选择路由生成URL非常重要。路由系统处理路由,按照他们被加入RouteCollection对象的顺序。每一个路由都会被检查是否匹配,是否满足下面的3个条件:
1.URL模式中定义的片段变量的值都存在,路由系统首先会从匿名对象的属性值中查找值,然后再是当前请求的变量值,最后是路由中定义的默认值。
2.我们提供的片段变量的值必须符合路由中的默认变量,这些变量存在默认值,但是在URL模式中没出现,比如下面的,myVar是一个默认变量
routes.MapRoute("MyRoute", "{controller}/{action}",
new { myVar = "true" });
要匹配这个路由,我们必须小心,要么不提供myVar的值,要么确定我们提供的值就是默认值。
3.所有的片段变量的值必须满足路由约束。
必须清楚,路由系统不会尝试找出最佳匹配的路由,它只会找到第一个匹配的,使用此路由生成URL。后续的路由则被忽略了。由此,定义路由的时候,越是具体特定的路由就要放在越前面。如果你生成的URL没有路由匹配,那么你的链接属性就是空的,如下:
<a href="">About this application</a>。此链接会在view中显示,但是用户点击的时候没有任何反应
第一个满足标准路由会生成一个非空URL,然后会终结URL生成进程,参数值也会被赋值。如果你显示的定义了一下不符合片段参数的参数或者默认参数,此方法会把这些变量附加为query sring。
指向其他controller
默认的ActionLink方法将你的action方法指向的是和当前view相同的controller。要生成一个指向不同controller的url,可以使用它的重载方法,如下:
@Html.ActionLink("About this application", "About", "MyController")
上面的代码会生成如下的HTML
<a href="/MyController/About">About this application</a>
注意,路由系统在生成URL的时候,对我们的application也是毫不关注。这意味着,你提供的action和controller值可能是无效的,必须自己确定他们确实存在。
传递额外的值
你可以传递使用匿名类型为片段变量传值,其中匿名变量中的属性表示片段。如下:
@Html.ActionLink("About this application", "About", new { id = "MyID" })
上例中,我们已经提供了一个片段变量id。因此会得到以下的HTML
<a href="/Home/About/MyID">About this application</a>
注意看到,我们提供的值,已作为匹配的URL模式的片段加上了。
当我们在描述路由匹配生成URL的方法时,我们提到,在尝试查找每个路由URL模式中的片段时,路由系统会从当前的请求中查找值,这个行为让很多程序困惑。假设我们的应用程序有个单一的路由
routes.MapRoute("MyRoute", "{controller}/{action}/{color}/{page}");
再假设用户当前在URL /Catalog/List/Purple/123上,当我们以下面的方式生成link的时候
@Html.ActionLink("Click me", "List", "Catalog", new {page=789}, null)
你可能觉得路由系统不会匹配路由,因为我们没有提供color片段的值,并且也没有定义它的默认值。但是你错了,路由系统会根据我们定义的路由匹配。生成如下的HTML:
<a href="/Catalog/List/Purple/789">Click me</a>
路由系统非常渴望能根据路由得到一个匹配,他会重用请求的URL中的片段变量值,上例中,我们的color中是Purple,因为用户之前的URL中color是Purple。这还不只,路由系统将这一技术作为他自己匹配方法的一部分。路由系统会为某些片段值重用URL模式中出现的值,这些片段变量必须是比 Html.ActionLink 方法中提供的其他参数先出现。假如我们创建了一个link:
@Html.ActionLink("Click me", "List", "Catalog", new {color="Aqua"}, null)
我们为color提供了一个值,但没有为page赋值。由于color在page前面出现,路由系统不会重用之前URL中的值,路由不会匹配。
处理这种行为最佳的方法是尽量避免这种事情的发生。我们强烈建议你要依赖这种行为,为每个URL模式中的每个片段变量赋值。依靠上述行为可能让你的代码难以读懂。
当传递的值不符合片段变量时,这些值会被作为query string,例如下面的调用ActionLink的方法
@Html.ActionLink("About this application", "About",
new { id = "MyID", myVariable = "MyValue" })
生成如下的HTML:
<a href="/Home/About/MyID?myVariable=MyValue">About this application</a>
如果提供的变量值碰巧匹配路由提供的默认值,那么路由系统生成的URL会忽略变量,例如:
@Html.ActionLink("About this application", "Index", "Home")
为action和controller传递的参数值匹配了默认值,HTML的生成如下:
<a href="/">About this application</a>
路由系统忽略了默认值,以最少片段生成URL。
设置HTML属性
我们可以为<a>元素设置属性,只需要提供一个匿名类型,其中它的属性和我们需要的属性一致就行。下面演示了设置id属性和css类:
@Html.ActionLink("About this application", "Index", "Home", null,
new {id = "myAnchorID", @class = "myCSSClass"})
我们创建的一个新的匿名方法,包含id和class属性,传递给ActionLink方法。我们传递null给另外的片段变量值,表示我们没有值可以提供。此方法对应的函数签名如下:
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes);
注意属性前面的@字符,C#允许使用保留字作为类成员,只要前面加个@就行。
对应的生成HTML如下:
<a class="myCSSClass" href="/" id="myAnchorID">About this application</a>
以上生成的URL都是相对的URL,但是我们可以使用ActionLink生成完整路径的URL,如下:
@Html.ActionLink("About this application", "Index", "Home",
"https", "myserver.mydomain.com", " myFragmentName",
new { id = "MyId"},
new { id = "myAnchorID", @class = "myCSSClass"})
这个是参数最多的ActionLink的重载方法,允许我们提供协议值,(例子中是https),目标服务器,URL片段和其他参数。生成的HTML如下:
<a class="myCSSClass" href="https://myserver.mydomain.com/Home/Index/MyId#myFragmentName"
id="myAnchorID">About this application</a>
推荐尽可能的使用相对URL,完整的URL的创建依赖于应用程序架构。我们看到过很多依赖绝对路径的大型应用程序,在切换网络结构或者调整域名的时候,就非常困难了。
Html.ActionLink方法生成完整的<a>元素,大多数情况下这真是我们需要的。但是有时我们只需要URL,只想显示url。这时,我们可以使用 Url.Action方法,只生成URL不生成<a>
...
My URL is: @Url.Action("Index", "Home", new { id = "MyId" })
...
Url.Action的使用方法和Html.ActionLink一样,除了他只是生成URL。两个方法的重载方法接受的参数也都一致,之前演示的Html.ActionLink的例子,使用Url.Action也同样可以。
之前提过,路由系统在处理URL的时候对controller和action没有任何概念,这就可以让路由系统可以更广泛的使用在其他ASP.NET应用程序。有时,象处理其他变量那样处理controller和action是很有用的,通过提供一组键值对来生成link。我们可以使用一些方法来实现。这些方法不是MVC专用的。
@Html.RouteLink("Routed Link", new { controller = "Home", action = "About", id="MyID"})
RouteLink 方法没有参数来表示controller和action的值。我们必须把他们包在匿名类型中。上述代码生成如下HTML:
<a href="/Home/About/MyID">Routed Link</a>
同样的,我们可以使用Url.RouteUrl方法来生成URL
@Url.RouteUrl(new { controller = "Home", action = "About", id = "MyID" })
这些方法很少使用,因为我们通常知道并且想要显示的设定controller和action的值。但是知道这个方法的存在也能让我们更简单的编程。
在Action方法中生成URL
多数情况下,我们会在view中生成URL,但是有时也会要在action方法中生成。如果只是要生成URL,可以使用和view中相同的方法
public ViewResult MyActionMethod() {
string myActionUrl = Url.Action("Index", new { id = "MyID" });
string myRouteUrl = Url.RouteUrl(new { controller = "Home", action = "Index" });
... do something with URLs...
}
更常见的需求是把客户端定向到其他的URL,我们可以返回RedirectToAction方法的返回值,如下:
public ActionResult MyActionMethod() {
return RedirectToAction("Index");
}
RedirectToAction方法的返回值是RedirectToRouteResult,指示MVC Framework生成一个重定向的URL,以此调用指定的action。
此方法的重载版本有许多。如果你需要从object属性中生成一个URL并且重定向 发送一个重定向URL,可是使用RedirectToRoute方法
如下:
public ActionResult MyOtherActionMethod() {
return RedirectToRoute(new { controller = "Home", action = "Index", id = "MyID" });
}
此方法也返回RedirectToRouteResult,和调用RedirectToAction的效果相同。
从指定的路由中生成URL
We have specified a name for each route we have defined in this chapter. For example, when we define
routes like this:
对我们有如下定义的路由:
routes.MapRoute("MyRoute", "{controller}/{action}");
routes.MapRoute("MyOtherRoute", "App/{action}", new { controller = "Home" });
路由的名字正是MapRoute方法的第一个参数,在此例中为MyRoute和MyOtherRoute,命名路由有2个原因
1.作为对路由目标的提示
2.可以选择指定的路由来生成URL
我们把最常规的路由放在list的第一个。这意味着,我们使用ActionLink方法的时候会按如下方式生成URL:
@Html.ActionLink("Click me", "About");
URL会由MyRoute生成。你可以通过Html.RouteLink方法覆盖默认路由,该方法提供一个参数来指示你想使用的路由,如下:
@Html.RouteLink("Click me", "MyOtherRoute", new { action = "About" });
你同样能通过Url.RouteUrl方法生成URL。如果你不想担心路由的顺序,那么这个特点很有用。