以路由控制URL


      至此為止,我們一直在使用ASP.NET MVC新項目隨帶的默認路由配置。現在我們將深入探討路由系統,並學習如何創建應用程序的自定義路由,以確保URL既是用戶友好又是搜索引擎可訪問的。

      路由的全部內容都是關於URL以及如何將URL作為應用程序的外部輸入的。當使用其他開發工具,如PHP、Web Form或是經典的ASP時,URL通常對應於磁盤上的物理文件。一個http://example.com/Products.aspx這樣的URL會導致執行負責處理該請求的名為Products.aspx的文件。

      通過使用URL路由,ASP.NET MVC解除了URL與物理文件的耦合。路由提供了一種把無擴展名的URL映射到控制器動作的方式,讓開發人員對URL方案有完全的控制。

      在本章中,我們將介紹路由概念及其與MVC應用程序的關系,還將簡要介紹它們如何用於ASP.NET Web Form項目。我們將考察如何設計應用程序的URL方案,然后將這些概念運用於創建一個示例應用程序的路由。最后看看如何測試路由,以確保它們按預期工作。

一、介紹URL路由

    與將URL綁定到磁盤上的物理文件不同,ASP.NET MVC引入的URL路由的底層結構能夠將URL映射到控制器動作,而無須在服務器上有物理文件作為URL的目標。在本節中,我們將考察新建MVC項目隨帶的默認路由的結構,以及這些路由是如何與控制器和動作的概念相關聯的。    

1.默認路由

    當創建一個新的ASP.NET MVC應用程序時,默認的項目模板會在Global.asax文件中調用一個名稱為RegisterRoutes的方法。該方法負責為應用程序配置路由,並定義了最初兩條路由——一條忽略路由和一條遵循{controller}/{action}/{id}模式的默認路由,如下所示。

      路由是通過調用MapRoute方法而定義的,該方法有個過載。在這個例子中,默認路由是通過調用三個參數的過載來配置的。第一個參數是路由名("Default")。第二個參數是用來匹配URL的URL模式。本例中,URL模式被定義為具有三個片段——控制器、動作和ID。第三個參數是一個匿名類型,它為這些片段定義了默認值。

      如果用戶訪問http://example.com/users/edit/5這樣的URL,這將匹配默認路由,因為它有三個片段,如圖所示。

      在這個例子中,字符串users映射到controller參數,edit映射到actio,而5映射到id。由於這個URL顯然與路由相匹配,MVC框架會嘗試查找名稱為UsersController的類、調用Edit方法,並為其id參數傳遞5。如果找不到控制器或動作,框架會產生一個404錯誤。

      添加到路由定義中的默認參數意味着URL不必精確地匹配三片段URL模式。如果指定了默認控制器Home以及默認動作Index,當控制器片段省略時,路由將默認控制器為HomeController。同樣,如果動作片段未指定,則路由會默認尋找Index動作。Id參數的默認值UrlParameter.Optional,意指不論是否指定第三個片段,路由都可以被匹配。下表給出了幾個能匹配默認路由的例子。

URL 路由參數 被選中的動作方法
http://example.com/Users/Edit/5 Controller=User, Action=Edit, id=5 UsersController.Edit(5)
http://example.com/Users/Edit Controller=User, Action=Edit UsersController.Edit()
http://example.com/Users Controller=User, Action=Index UsersController.Index()
http://example.com Controller=User, Action=Index HomeController.Index()

      在IgnoreRoute方法中,模式{resource}.axd/{*pathInfo}確保文件擴展名為.axd的任何URL不會被路由引擎所處理,這樣才能確保任何自定義HTTP處理程序(其擴展名為.axd)以正確方式唄處理,而不會被路由引擎攔截。

2.入站與出站路由

      入站路由(Inbound Routing):將URL映射到控制器或動作及任何附加參數。

      出站路由(Outbound Routing):通過一組給定的路由數據(通常是控制器和動作)生成相應的URL。

     上圖所示的入站路由描述了一個控制器動作的URL調用。HTTP請求進入ASP.NET管道,並通過ASP.NET MVC應用程序注冊的路由進行發送。每個路由都有處理請求的機會,而匹配路由隨后會指定被使用的控制器和動作。

二、設計URL模式(schema)

1.建立簡單、整潔的URL

                    傳統URL:http://example.com/eventmanagement/events_by_month.aspx?year=2011&month=4

      使用路由系統的URL:http://example.com/events/2011/04

      這種URL帶來的好處是,其中的日期有了一種明確的層次格式。

2.建立可破解的URL

      在設計URL方案時,考慮最終用戶為了改變所顯示的數據要如何操縱或“破解”URL是有價值的。例如,也許可以合理地假設,從以下URL移去參數“04”,可能表示2011年發生的全部事件:

      http://example.com/events/2011/04

      同樣的邏輯可以形成下表所示的更全面的路由列表。

URL 描述
http://example.com/events 顯示全部事件
http://example.com/events/<year> 顯示某年事件
http://example.com/events/<year>/<month> 顯示某月事件
http://example.com/events/<year>/<month>/<day> 顯示某日事件

      讓URL模式具有這種靈活性是很棒的,但這可能會導致應用程序中具有大量潛在的URL。在建立應用程序視圖時,你總是要改出相應的導航。記住,可能在各個頁面上不必對每個可能的URL組合都包含一個鏈接。在用戶試圖破解URL並使其生效時,讓用戶有一些驚喜的發現反而是件好事。

      如果不希望用戶破解,可考慮使用連接字符來替代斜線,如/events/2008-04-01。

3.使用URL參數區分請求

      讓我們對此路由加以擴展,並允許按類別列出事件。從用戶的觀點來看,最有用的URL可能像這樣:

http://example.com/events/aspnet-usergroup-meeting

      但現在有問題了!我們已經有了一個與/events/<something>形式匹配的路由,用來列出特定年、月、日的事件,那么現在如何用/events/<something>也匹配類別?第二個路由片段現在意味着完全不同的含義,這與現有的路由不協調了。如果把這種URL交給路由系統,它應該把這種參數可能作類別還是日期?
      幸運的是,ASP.NET MVC的路由系統允許我們運用條件,使用正則表達式來確保路由只與某個模式的參數相匹配就夠了。這意味着我們可以只用一條路由,就能讓/events/2011-01-01形式的請求傳遞給按日期顯示事件的動作,而讓/events/asp-net-mvc-in-action形式的請求傳遞給按類別顯示事件的動作。

4.盡可能避免暴露數據庫ID

       一個用於托管開發人員事件的網站可能會定義這樣的URL:

http://example.com/events/87

      87是從數據庫獲得的每一個對象都有一個主鍵形式的唯一標識符,但是除了數據庫管理員之外,數字87對任何人都毫無意義。因此,應該盡可能避免在URL中使用數據庫生成的ID,盡量讓它們有意義、可讀、易於理解。

http://example.com/events/houstonTechFest2010

5.考慮添加多余信息

      如果必須在URL中使用數據庫ID,可考慮添加除了使URL可讀外沒什么目的的附加信息。

http://example.com/events/houstonTechFest2010/session-87
http://example.com/events/houstonTechFest2010/session-87/an-introduction-to-mvc

--------------------------------------------------------------------

搜索引擎優化(SEO)

      當涉及網站的搜索引擎優化方面時,有必要提一提設計良好的URL的價值,在URL中放置一個相關的關鍵字提升搜索引擎排序。設計要點如下:

  • 為控制器和動作使用描述性的、簡單的、普遍使用的單詞。力求盡可能相關並使用可能用於所建頁面的關鍵詞。
  • 當在路由中包含文本參數時,用連接字符替換所有的空格符。
  • 去掉字符串參數中不重要的標點和不必要的文本。
  • 在URL可能的地方包含附加的、有意義的信息,如標題和描述等。

----------------------------------------------------------------------

三、在ASP.NET MVC中實現路由

      默認項目模板創建了兩個默認路由,但你可以不接受這兩個默認路由的限制,添加自己的路由,以實現完全自定義的URL模式。以下將對此加以演示,以一個簡單的在線商店為例,實現幾個路由。我們將考察如何創建簡單、靜態的路由,以及創建更復雜的使用參數的路由和全匹配路由。

1.在線商店的URL模式(重要)

路由號 URL 描述
1 http://example.com/ 首頁,重定向到分類列表
2 http://example.com/privacy 顯示包含網站私有策略的靜態頁面
3 http://example.com/products/<productcode> 顯示相應產品代碼的產品詳情頁面
4 http://example.com/products/<productcode>/buy 將相應產品添加到購物籃
5 http://example.com/basket 顯示當前用戶的購物籃
6 http://example.com/checkout 啟動當前用戶的結算過程

      注意:路由4中的URL不是設計給用戶看的,它通過表單遞交進行鏈接,在動作處理完成后會立即進行重定向,因而這種URL不會在地址欄中出現。

2.添加自定義靜態路由

      路由1是由默認路由處理的。

      路由2,是一個純靜態路由,將http://example.com/privacy映射到HomeController的Privacy動作。

routes.MapRoute("privacy_policcy","privacy", new { controller="Home", action="Privacy"});

      警告:添加到路由表中的路由次序決定了查找匹配時的路由搜索順序。這意味着,源代碼中列出的路由,應當從帶有最具體條件的最高優先級降低到最低優先級,或全匹配路由。

3.添加自定義的動態路由

      當有少量偏離一般規則的URL時,靜態路由是有用的。如果路由包含與頁面顯示的數據相關的信息,就需要動態路由了。

      路由3和路由4是用兩個路由參數實現的:

routes.MapRoute("Product", "products/{productCode}/{action}", new { controller="Catalog", action="Show"});

      兩個占位符將匹配URL中用斜線分隔的片段,productCode參數是必需的,但action是可選的。如果action未指定,該路由會默認指向CatalogController上的Show動作,並傳遞productCode參數。詳細代碼如下。

public class CatalogController : Controller
    {
        private ProductRepository _productRepository = new ProductRepository();

        public ActionResult Show(string productCode)
        {
            var product = _productRepository.GetByCode(productCode);

            if (product == null)
            {
                return new NotFoundResult();
            }
            return View(product);
        }

    }

     實現一個執行時能生成HTTP 404的自定義動作結果:

public class NotFoundResult:ActionResult
    {
        public override void ExecuteResult(ControllerContext context)
        {
            context.HttpContext.Response.StatusCode = 404;
            new ViewResult { ViewName = "NotFound" }.ExecuteResult(context);
        }
    }
}

      NotFoundResult通過集成ActionResult,在其中提供了必須實現的ExecuteResult方法。該方法將響應狀態碼設置為404,然后渲染一個名為NotFound的視圖,該視圖位於Views/Shared目錄。

      注意:HttpNotFoundResult動作,也將響應轉台碼設置為404,但它未提供顯示自定義錯誤頁面的機制,因此總是會給最終用戶顯現一個空屏。

      最后,我們可以添加模式中的路由5和路由6。

routes.MapRoute{"catalog", "{action}",
new { controller="Catalog" },
new { action=@"basket|checkout"});

      這些路由幾乎是靜態路由,只不過它們是用一個參數和一個路由約束來實現的,以保持較少的路由數目。這么做的主要原因有兩個。第一,每個請求都必須掃描路表進行匹配,所以大的路由集合會影響到性能。第二,路由越多,路由優先級問題出現的風險也越高。較少數目的路由規則更易於維護。

      MapRoute方法的第四個參數包含了路由約束。約束參數是一個匿名類型形式的字典,可以用於指定如何約束特定的路由參數。在本例中,我們使用了一個正則表達式來指明,僅當片段字符串為“basket”或“checkout”時,才匹配action參數。這種約束能夠適當地阻止把未知動作傳遞給控制器。

4.全匹路由

      我們現在已經添加了靜態和動態路由,以便為網站的不同URL提供內容。但假設有一個與所有路由都不匹配的請求,會發生什么?結果會拋出一個異常,這是實際應用程序中不希望發生的事情。為了對此加以處理,我們可以使用與ASP.NET的錯誤處理基礎架構結合在一起的全匹配路由。

      我們將添加一個全匹配路由,用它匹配尚未被其他路由匹配的任何URL,顯示HTTP404的錯誤消息,它應該是最后一條被定義的路由。

routes.MapRoute("404-catch-all", "{*catchall}",
new { controller="Error", action="NotFound"});

     值catchall為全匹配路由要拾取的值提供了一個名稱。與規則路由參數不同,全匹配參數(以星號為前綴)會捕獲包括正斜線在內的整個URL部分,正斜線通常用於分隔路由參數。在上述示例中,該路由被映射到ErrorController的NotFound動作。

public class ErrorController: Controller { public ActionResult NotFound() { return new NotFoundResult(); } }

      現在,可以刪去默認的{controller}/{action}/{id}路由,因為我們已經完全定制了路由,以匹配我們的URL模式。或者,你也許會選擇保留它,以作為訪問其他控制器的一種默認方式。

四、使用路由系統生成URL

    每當網站中需要一個URL時,我們都要求框架給出,而不是采用硬編碼。我們需要制定一種控制器、動作以及參數的組合,剩下的由ActionLink方法完成。ActionLink是MVC框架中HtmlHelper類上的一個擴展方法,它會生成一個插入了正確URL的完整的HTML<a>元素,該URL與傳遞進來的對象參數所指定的路由相匹配。以下是調用ActionLink的一個例子:

@Html.ActionLink ( "MVC3 in Action", "Show", "Catalog", 
new { productCode = "mvc-in-action" }, null )

       第一個是超鏈接的顯示文本;第二個和第三個指定了要被鏈接到的動作和控制器;第四個采用了一個匿名類型形式的字典,以指定任意的附加路由參數;最后一個是仍以匿名類型形式指定的任意的附加HTML屬性。

      使用前面定義的路由,這個例子會生成一個鏈接,指向CatalogController上的Show動作,並帶有為productCode指定的附加參數。以下是其輸出:

<a href="/products/mvc-in-action">MVC3 in Action</a>

      類似地,如果使用HtmlHelper的BeginForm方法來建立表單標簽,它會為你生成URL。有時,能夠將路由部分未指定的參數傳遞給動作時有用的:

@Html.ActionLink ( "MVC3 in Action", "Show", "Catalog", 
new { productCode = "mvc-in-action", currency="USD" }, null )

      如果該參數與路由中的某個部分匹配,它將成為URL的一部分。否則,它將被附加到查詢字符串。比如,以下是上述代碼生成的鏈接:

<a href="/products/mvc-in-action?currency=USD">MVC3 in Action</a>

       在使用ActionLink時,被選中的路由是路由集合中所定義的第一個匹配路由。大多數情況下,這是足夠的,但如果你希望請求一條特定的路由,可以使用RouteLink,它接受一個標識被請求路由的參數,像這樣:

@Html.RouteLink ( "MVC3 in Action", "Show", "Catalog", 
new { productCode = "mvc-in-action" }, null )

      這個代碼將查找一個帶有product名稱的路由,而不是指定的控制器和動作。

      有時候你需要獲得一個URL,但不是為了鏈接或表單。這通常發生在編寫Ajax代碼需要設置一個請求URL時。UrlHelper類能夠直接生成URL,由ActionLink方法和其他方法所使用。以下是一個例子:

@Url.Action ( "Show", "Catalog", 
new { productCode = "mvc-in-action" } )

      這個代碼也返回/products/mvc-in-action,但沒有任何包圍標簽。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM