創建傳出的URL(Outgoing URLs)
處理傳入的URL(Incoming URLs)僅僅是路由系統的一部分功能。我們也需要使用URL架構來創建Outgoing URLs,我們可以將這些URL嵌入到我們的View里面,並且能夠讓用戶點擊URL提交表單給我們的應用程序,並能夠命中合適的controller和action。
下面會展示用不同的技術來創建Outgoing URLs:
最便捷的創建Outgoing URLs就是手動定義如:<a href="/Home/About">About this application</a> 這樣的URL。這個URL會命中HomeController中的About action方法。手動定義Outgoing URLs的確非常便捷,但是這樣做也是非常危險的,因為一旦你改變了自己應用程序的URL架構,你就打破了所有手動創建的Outgoing URLs。你必須排查修改所有的View里面的Outgoing URLs,並更新所有的Controller和Action中對這些URL的引用。
Routing System能夠從URL架構創建URL,因此如果URL架構發生了變化,那么由此創建在View里面的Outgoing URLs也會隨之發生改變。顯然這種方式才是明智之舉,但也需要我們在前期做一些初始化的工作,這是非常值得的,長遠來看這會讓我們獲益匪淺。
下面會通過實例來說明,仍然借用前面的項目,
只不過做些修改:1)移除AdditionalControllers 2)修改RegisterRoutes如下:
View Code
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("MyRoute", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
最簡便的在View里面創建一個Outgoing URL就是調用Html.ActionLink方法。如:@Html.ActionLink("關於", "About").該方法是根據當前的URL架構創建URL的,查看頁面的源文件可以發現該URL為<a href="/Home/About">關於</a>。假設我們改變URL架構如下:
View Code
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 });
}
我們會得到這樣的一個URL:<a href="/App/DoAbout">關於</a>
理解輸出的URL路由匹配
通過上面的介紹,我們已經了解了路由系統是怎么根據URL架構的改變來創建的Outgoing URL的。應用程序通常會定義若干路由,所以理解路由怎樣被選中來進行URL創建就非常重要。路由系統是按照路由被添加到RouteCollection里面的順序來處理的,RouteCollection對象是要傳遞給RegisterRoutes方法。對每一個路由會根據下面三個條件來進行是否匹配的檢查:
1)提供的值必須對定義在URL模式里面的每一個segment變量是可用的。
為每一個segment尋找值時遵循如下規則:
首先,路由系統會在我們提供的值,也就是定義在匿名類型的屬性里面的值中尋找
其次,在當前請求的變量值里尋找
最后,在我們定義路由時提供的默認值里面尋找
2)我們提供的segment變量的值可能都與定義在route里面的默認變量(default-only)一致。這些變量的默認值已經提供,但是這些變量並沒有出現在我們定義的URL模式中。
例如有這樣一個路由:routes.MapRoute("MyRoute", "{controller}/{action}", new { myVar = "true" });其中myVar就是default-only變量,為了讓這個路由匹配,
我們必須注意不要為myVar提供一個值或確保我們提供的值能夠匹配默認值。
3)所有segment變量的值必須滿足路由約束
要明確的是:路由系統不是尋找最佳的匹配路由,僅僅是找第一個匹配的然后用它創建URL。只有找到了一個匹配的路由,那么路由系統就會忽略后面所有的路由。因此,我們定義路由時一定按照從具體到模糊的順序。
下面進行單元測試Outgoing URLs
View Code
[TestMethod]
public void TestOutgoingRoutes()
{
// Arrange
RouteCollection routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
RequestContext context = new RequestContext(CreateHttpContext(), new RouteData());
// Act - generate the URL
string result = UrlHelper.GenerateUrl(null, "Index", "Home", null,
routes, context, true);
// Assert
Assert.AreEqual("/", result);
}
針對其他控制器(Targeting Other Controllers)
ActionLink方法默認是假定我們針對的是同一個Controller中的action方法,由該action方法來呈現view。如果要指定其他的Controller可以多加個參數如:@Html.ActionLink("About this application", "About", "MyController")。
傳遞額外的值(Passing Extra Values)
我們可以使用匿名類型的屬性來傳遞segment變量的值。如@Html.ActionLink("About this application", "About", new { id = "MyID" })。生成的URL如下:
<a href="/Home/About/MyID">About this application</a>
理解segment變量的重用
在我們描述路由匹配到傳出的URLs時,我們提到當為每一個segment變量尋找值時,路由系統將會從當前的請求中尋找。現在我們定義這樣一個路由如下:
routes.MapRoute("MyRoute", "{controller}/{action}/{color}/{page}")
接着在Index視圖里面定義這樣一個連接:@Html.ActionLink("點我啊", "Index", "Home", new { page = 789}, null).運行程序首先輸入如下URL:
http://localhost:<port>/Home/Index/Purple/123。頁面呈現的鏈接是:<a href="/Home/Index/Purple/789">點我啊</a>.
我們定義鏈接時並沒有給color賦值,而這里的Purple是怎么來的呢?
這就是重用了剛開始傳入的URL:http://localhost:<port>/Home/Index/Purple/123中的color值Purple。而且路由系統是重用page之前的segment變量的值。當我們對ActionLink做下修改:@Html.ActionLink("Click me", "List", "Catalog", new {color="Aqua"}, null);
這里我們提供了color的值,因為color在page之前,所以這里不會重用傳入URL的color值。展示為:<a href="/?color=Red">點我啊</a>
這里強烈建議我們避免這種行為出現,而是在定義URL模式時提供你需要的所有segment變量的值。
當我們提供的值跟segment變量的值不一致時,這個值會作為querystring附加在URL的后面。
例如:@Html.ActionLink("About this application", "About", new { id = "MyID", myVariable = "MyValue" }),創建的連接顯示如下:
<a href="/Home/About/MyID?myVariable=MyValue">About this application</a> 。
如果我們提供的值碰巧跟默認值一樣,這時路由系統會忽略來自Outgoing URL的變量。例如:創建這樣一個鏈接 @Html.ActionLink("About this application", "Index", "Home") 。顯示的鏈接就是這樣:<a href="/">About this application</a>
指定HTML屬性
我們可以為創建的鏈接指定css樣式,如可以指定一個樣式類別為myCSSClass可以這樣做:
@Html.ActionLink("About this application", "Index", "Home", null, new {id = "myAnchorID", @class = "myCSSClass"})注意這里用了一個"@"符號,因為class是C#里面關鍵字,這里需要的是HTML樣式類名。這樣展示的<a>標簽會多一個屬性:<a class="myCSSClass" href="/" id="myAnchorID">About this application</a>
創建絕對路徑的URL
前面創建的都是相對路徑,我們還可以創建全路徑如下:
@Html.ActionLink("About this application", "Index", "Home", "https", "myserver.mydomain.com",
"myFragmentName",new { id = "MyId"}, new { id = "myAnchorID", @class = "myCSSClass"}) 。
這里建議我們盡量使用相對路徑。因為全路徑依賴於呈現給用戶的應用程序的基礎架構,有太多依賴絕對路徑的大的應用程序最后因為網絡基礎設施或域名策略的改變而被破壞,而這種變化常常是程序員無法控制的。
有時我們需要將URL作為文本顯示在頁面上,這是可以是用Url.Action(),用法跟Html.ActionLink類似。
根據Routing Data創建鏈接或URL
如@Html.RouteLink("Routed Link", new { controller = "Home", action = "About", id="MyID"})。
在頁面展示的鏈接如下:
<a href="/Home/About/MyID">Routed Link</a>
使用 Url.RouteUrl方法可以讓創建的鏈接以文本形式顯示。
在Action方法里面創建Outgoing URLs
大多數情況下,我們會在View里面創建Outgoing URLs,但有些時候我們也需要在action方法里面做類似的事情。方法一樣,如下所示:
View Code
public ActionResult MyActionMethod()
{
string myActionUrl = Url.Action("Index", new { id = "MyId" });
string myRouteUrl = Url.RouteUrl(new { controller = "Home", action = "About" });
return Redirect(myRouteUrl);
}
根據一個具體的Route創建URL
我們在使用routes.MapRoute()時一般傳了一個名字作為參數,如下面這樣的:
routes.MapRoute("MyRoute", "{controller}/{action}");
routes.MapRoute("MyOtherRoute", "App/{action}", new { controller = "Home" });
給route命名有如下兩個理由:
1.可以用來提示該路由的作用 2.在創建Outgoing URL時可以選擇具體的route
我們在使用@Html.ActionLink("Click me","About")時具體會選擇那一個route呢?
答案就是:定義在RegisterRoutes()里面的第一個route。這里選擇的就是MyRoute。當然我們也可以自己指定一個route,如@Html.RouteLink("Click me", "MyOtherRoute", new { action = "About" });
不建議使用命名路由的情形
創建Outgoing URL依賴route命名會打破"分解關注點"(這是MVC設計模式的核心思想)。當在view或action里面創建URL或鏈接時,我們的關注點是用戶將被定向到的action和Controller,不是要使用的URL格式。所以我們傾向於避免給route命名,而是是用null代替。
好了,今天的筆記做到這里!
周末愉快!
