ASP.NET MVC3 學習心得------路由機制


9.1 理解URL

URL滿足的要求:

域名易於記憶和拼寫

簡短、易輸入

可以反應出站點的結構

可破解,用戶可以通過移除URL的末尾,進而達到更高層次的信息體系結構

持久、不能變化

9.2路由機制的概述

ASP.NET MVC中路由機制的兩種用途:

匹配傳入的請求,並映射到控制器的操作

構造傳出的URL,用來相應控制器的操作

9.2.1 對比路由和URL重寫

URL重寫是將URL映射到另一個URL

路由是將URL映射到資源

兩者的區別:路由機制也使用它在匹配傳入URL時同樣的映射規則來幫助生成URL,而URL重寫只能用於傳入的請求URL,而不能幫助生成原始的URL

9.2.2 路由的定義

路由的定義是從URL模式開始的,因為它指定了與路由相匹配的模式,路由可以指定它的URL及其默認值,還可以約束URL的各個部分,提供關於路由如何、何時、傳入的請求URL相匹配的嚴格控制

路由URL

我們在新建的應用程序中的Global.asax.cs文件的Application_Start方法中調用了一個RegisterRoutes方法,如下:

 public static void RegisterRoutes(RouteCollection routes)

        {

            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

 

            routes.MapRoute(

                "Default", // 路由名稱

                "{controller}/{action}/{id}", // 帶有參數的 URL

                new

                {

                    controller = "Home",

                    action = "Index",

                    id = UrlParameter.Optional

                } // 參數默認值

            );

        }

如:routes.MapRoute("Default","{first}/{second}/{third}");

在兩個斜杠之前的花括號為占位符,這些占位符即是URL參數,該示例定義的規則可以匹配任何帶有3個段的URL,我們可以將這些參數命名為任何想要的名稱,當客戶端請求到達服務器時,路由將解析請求的URL,並將解析出的路由參數值放入字典中

2、路由值

我們可以使用任何想要的名稱定義路由,但是為了程序的正確的運行ASP.NET MVC 框架要求使用一些特定的參數名--{controller}和{action}

{controller}為處理請求的控制器,ASP.NET MVC 規定將Controller后添加到{Controller}URL參數值的后面構成一個類型名稱,然后根據名稱查找實現了System.Web.Mvc.IController接口的類型

如下示例:routes.MapRoute{"Default","{controller}/{action}/{id}");

路由URL在段中也允許包含字面值,如將MVC集成到一個現有的站點中並想要所有的MVC請求一site開頭 則如下:site/{controller}/{action}/{id}

所以該路由只能匹配site/Home/index/123 而不是匹配/Home/index/231

此外,URL段中還允許字面值和參數混在一起,但是它不允許有兩個連續的URL參數,

如下:{language}-{country}/{controller}/{action}

      {controller}.{action}.{id}

有時只需要匹配兩個參數,這個時候我們不需要在重寫路由了,只需要將第三個參數換為可選參數就行

如下:routes.MapRoute("Default","{controller}/{action}/{id}",new {id=Urlparameter.Optional});

我們也可以為多個參數提供默認值:

如:routes.MapRoute("Default","{controller}/{action}/{id}",new {id=Urlparameter.Optional,action=“index"});

路由URL模式、默認值及其匹配

路由URL模式                       默認值                   匹配URL模式示例

{controller}/{action}/{id}  new {id=UrlParameter.Optional} /albums/diplay/123

                                                            /albums/diplay

{controller}/{action}/{id}  new {id=UrlParameter.Optional, /albums/display/123

                                 Action="index",           /albums/display

                                 Id=UrlParameter.Optional} /albums

                                                           /                                  

4、路由約束

有時候需要對指定URL段的數量來說,我們需要對URL有更多的控制

如下URL:

http://example.com/2008/01/16

http://example.com/posts/categories/aspnetmvc/

以上的兩個URL都包含3個段,且都可以和默認路由匹配,但是如第一個URL要匹配一個2008controller的控制器,這樣是錯誤的,因此需要對路由進行約束,:

如:

 routes.MapRoute("blog", "{year}/{month}/{day}", new

            {

                controller = "blog",

                action = "index"

            }, new

            {

                year = @"\d{4}",

                month = @"\d{2}",

                day = @"\d{2}"

            });

            routes.MapRoute("simple", "{controller}/{action}/{id}");

路由機制自動的使用^和$符號包裝指定的約束表達式,以確保表達式能夠精確的匹配參數值

在本示例中,我們在默認的simple路由之前添加的新路由,路由按先后順序與傳入的URL進行匹配,直到匹配成功,因為/2008/01/16的請求將與兩個定義的路由都匹配,所以我們把更具體的路由放在了前面

9.2.3 命名路由

為路由指定名稱可以解決URL的路由二義性問題

如:

routes.MapRoute(name: "Test", url: "code/p/{action}/{id}", defaults: new

            {

                controller = "Section",

                action = "Index",

                id = ""

            });

            routes.MapRoute(name: "Default", url: "{controller}/{action}/{id}", defaults: new

            {

                controller = "Home",

                action = "Index",

                id = ""

            });

在視圖中生成指向路由的超鏈接如下:

    @Html.RouteLink("Test", new

{

    controller = "section",

    action = "Index",

    id = 123

});

    @Html.RouteLink("Default", new

{

    controller = "Home",

    action = "Index",

    id = "123"

});

該方法不能指定由哪個路由來生成鏈接

為此我們做如下修改:

 @Html.RouteLink(linkText: "Route:Test", routeName: "Test", new

{

    controller = "section",

    action = "Index",

    id = "123"

});

    @Html.RouteLink(linkText: "Route:Default", routeName: "Default", new

{

    controller = "Home",

    action = "Index",

    id = "123"

});

這樣我們便可以為生成的URL指定路由名來生成相應的URL

9.2.4 MVC區域

在路由沒有指定名稱空間的情況下,很容易產生二義性,即兩個同名的控制器同時匹配一個路由,我們可以通過在項目中使用唯一的控制器名稱來防止二義性,如下:對特定的路由指定一組用來定位控制器的名稱空間

            routes.MapRoute{"Default","{controller}/{action}/{id}",new {controller="Home",action="Index",id=""},new []{ "AreasWeb.Controllers"});

第四個參數指定一個名稱空間數組

9.2.5 Catch-All 參數

Catch-all參數允許路由匹配具有任意數量的段的URL,參數中的值是不含查詢字符串的URL剩余部分

如:

        routes.MapRoute("CatchAllRoute", "/query/{query-name}/{*extrastuff}");

匹配如下:

URL                           參數值

/query/select/a/b/c         extrastuff="a/b/c"

/query/select/a/b/c/        extrastuff="a/b/c"

/query/select/              extrastuff=""(路由仍然匹配,“catch-all捕獲了空字符串

9.2.6 段中的多個URL參數

路由的URL中可以存在多個參數,如下:

{title}-{artist}

Album{title}and{artist}

{filename}.{ext}

為避免二義性,參數不能臨近,

如下:均為無效的URL

{title}{atrist}

DownLoad{filename}{ext}

路由URL在與傳入的請求匹配時,它的字面值與請求精確匹配,而其中的URL參數是貪婪匹配,路由使每個URL參數盡可能的匹配更多的文本

如下:                      多參數路由匹配

路由URL             請求的URL                   路由數據的結果

{finename}.{ext}    /Foo.xml.aspx               filename="Foo.xml" ext="aspx"

My{title}-{cat}     /MyHouser-dwelling          title="Houser" ext="dwelling"

{foo}xyz{bar}       /xyzxyzxyzblah              foo="xyzxyz" bar="blah"

9.2.7 StopRoutingHandler和IgnoreRoute

默認情況下,路由機制將忽略那些映射到磁盤物理文件的請求。

方法如下:

        public static void RegisterRoutes(RouteCollection routes)

        {

            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");//第一種方法

            routes.Add(new Route("{resource}.axd/{@pathInfo}",new StopRoutingHandler()));//第二種方法

}

9.2.9 揭秘路由如何生成URL

1、URL生成的高層次概述

路由機制的核心是一個基於RouteCollection類和RouteBase類組成的簡單抽象對象組成的算法。有許多中生成URL的方法,但是這些方法都是以調用RouteCollection.GetVirtualPath方法的重載版本而結束的,該方法的重載版本如下:

Public virtualPathData GetVirtualPath(RequestContext requestContext,RouteValueDictionary values)

Public virtualpathData GetVirtualPath(RequestContext requestContext,string name,RouteValueDictionary values)

第一個重載版本接受當前的RequestContext以及由用戶指定用來選擇想要的路由值

路由集合通過Route.GetVirtualPath方法遍歷每個路由,類似於在路由與傳入的請求匹配時所運用的匹配邏輯

如果一個路由匹配,則返回一個包含了URL的VirtualPathData實例,及其他匹配信息,否則將返回空值,路由機制移向列表中的下一個路由

第二個重載版本接受了三個參數,第二個參數為路由名稱,在路由集合中路由名稱是唯一的,指定了路由名稱之后便不需要遍歷每個路由,直接找到指定名稱的路由進行匹配,如果匹配則返回VirtualPathData實例,否則返回空值

2、URL生成詳解

① 用戶調用RouteCollection.GetVirtualPath方法,並向它傳遞一個RequestContext對象、一個包含值的字典,以及用來選擇生成URL路由的名稱(可選參數)

② 路由機制查看要求的路由URL參數,並確保提供的路由值字典為每一個要求的參數提供一個值,否則URL生成程序立即中止,返回空值

③ 一些路由可能包含沒有對應的URL參數的默認值。如:路由可能為category見提供了 一個默認值"pastries",但是category不是路由URL的一個參數,這時,如果用戶傳入的路由值字典為category提供了一個值,那么該值必須匹配category的默認值

④ 路由機制然后應用路由的約束,如果有的話,要查找約束

⑤ 路由匹配成功,可以通過查看每一個URL參數,並嘗試用字典中的對應值填充它,進而生成URL

3、外界路由值

在一些情形中,URL生成程序還可以利用那些不是通過調用GetVirtualPath方法顯示提供的值

4、溢出參數

指在URL生成過程中使用、但是在路由定義中卻沒有指定的路由值。具體指的是路由的URL、默認的字典和約束字典

如:路由定義如下:

            routes.MapRoute(

                "Default", // 路由名稱

                "{controller}/{action}/{id}", // 帶有參數的 URL

                new

                {

                    controller = "Home",

                    action = "Index",

                    id = UrlParameter.Optional

                } // 參數默認值

            );

上面路由的定義中不包括名為“page”的URL參數,下面我們使用Url.RouteUrl方法渲染了URL,而不是生成連接:

@Url.RouteUrl(new {controller="Report",action="List",page="123"});

5、該行代碼生成的URL是:/Request/List?Page=2,但是我們指定的參數比需要的參數還要多,這個時候額外的參數會作為查詢字符串附加在生成的URL之后,路由機制在選擇匹配的路由時並不是精確的匹配,它只是選擇足夠的匹配的路由,只要指定的參數滿足路由需要,額外參數無關緊要

6、Route類生成URL的若干示例

routes.MapRoute("report", "reports/{year}/{month}/{day}", new{day = 1});

則GetVirtualPath方法調用示例如下:

參數                        返回URL                          說明

Year=2007,month=1,day=1   /reports/2007/1/1               直接匹配

Year=2007,month=1          /reports/2007/1                 默認day=1

Year=2007,month=1,        /reports/2007/1/1?Category=123 溢出參數進入生成的URL                                                 

day=1,category=123 

Year=2007                   返回空值                 因為沒有提供足夠的匹配參數

9.3 揭秘路由如何綁定到操作

9.3.1 高層次請求的路由管道

主要由以下幾步組成:

① UrlRoutingModule 嘗試使用在RouteTable中注冊的路由匹配當前請求

② 如果一個路由匹配成功,那么路由模塊將從匹配成功的路由中獲取IRouteHandler接口對象

③ 路由模塊由IRouteHandler接口的GetHandler方法,並返回用來處理請求的IHttpHandler對象

④ 調用HTTP處理程序中的ProcessRequest方法,將要處理的值傳給它

⑤ 在ASP.NET MVC 中,IRouteHandler是MvcRouteHandler類的一個實例,MvcRouteHandler轉而返回一個實現了IHttpHandler接口的MvcHandler對象,返回的MvcHandler對象主要用來實例化控制器,並調用該實例化控制器上的方法

9.3.2 路由數據

調用GetRouteData方法將返回一個RouteData的實例,RouteData包含了關於匹配請求的路由的信息,如果匹配成功則會創建一個字典,其中包含從URL中解析出的信息,並且路由還會為URL中的每一個參數向字典中添加一個鍵,如:{controller}/{action}/{id},所在的字典中至少包含三個鍵分別為controller、action、id

9.4 自定義路由約束

之前我們講過可以使用正則表達式對路由進行細粒度的匹配控制,除此之前路由機制提供了一個具有單一Match方法的IRouteConstraint接口,該接口定義如下:

public interface IRouteConstraint

        {

            bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);

        }

當路由機制評估路由約束時,如果約束值實現了IRouteConstraint接口,那么這將導致路由機制引擎調用路由約束上的IRouteConstraint.Match方法,以確定約束是否滿足給定的請求。

路由機制本身以HttpMethodConstraint類的形式提供了IRouteConstraint接口的一個實現。如下:定義一個路由只相應GET請求,其他請求不響應

routes.MapRoute("name", "{controller}", null, new

                      {

                          httpMethod = new HttpMethodConstraint("GET")

                      });

自定義的約束沒有必要關聯URL參數,因此可以提供一個基於多個URL參數或一些其他約束


免責聲明!

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



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