天天看點

.NET/ASP.NET Routing路由(深入解析路由系統架構原理)1】開篇介紹2】ASP.NETRouting路由對象模型的位置3.】ASP.NETRouting路由對象模型的入口4.】ASP.NETRouting路由對象模型的内部結構5.】UrlRoutingHandler 對象内部結構及擴充應用

閱讀目錄:

1.開篇介紹

2.ASP.NET Routing 路由對象模型的位置

3.ASP.NET Routing 路由對象模型的入口

4.ASP.NET Routing 路由對象模型的内部結構

4.1】UrlRoutingModule 對象内部結構

4.2】RouteBase、Route、RouteCollection、RouteTable 路由核心對象模型

4.3】RouteValueDictionary、RouteData、RequestContext 路由資料對象模型

4.4】IRouteHandler 、IHttpHandler兩個接口之間的關系

5.】UrlRoutingHandler 對象内部結構及擴充應用

這篇文章讓我們愉快的學習一下ASP.NET中核心的對象模型Routing子產品,為什麼說愉快呢,因為Routing正是建立在大家都比較熟悉的ASP.NET管道模型基礎之上的,是以相比其他一些陌生的概念會輕松很多,不過不要緊一回生二回熟;

ASP.NET Routing 系統是一切通過ASP.NET進行Uri通路應用程式的基礎(并非實體檔案的直接映射);随着Routing的出現,我們的WEB設計已經和以前大不一樣;越來越輕量級、簡單化,都通過簡便的Uri資源的方式進行處理,将精力放在業務的設計上;現在主流的Rest ful api 也都是建立在這樣的一種機制下的,然而我們的ASP.NETMVC也是一種通過獨立的Uri進行程式通路處理的架構,是以也是建立在ASP.NET Routing;再者就是現在也比較熱門的ASP.NET技術(ASP.NETWEBAPI);都是建立在Routing架構之上,可見它還是蠻重要的;

是以這篇文章讓我們來分析一下Routing的工作原理,它為什麼能在不影響現有架構的基礎上提供這麼好的擴充性,真的讓人很想去一探究竟;目前非常可觀是我們都了解ASP.NET現有的架構知識,我們大概了解它肯定是在ASP.NET管道模型的哪個位置進行了相應的攔截;

下面我們帶着這個重要的線索來一點一點弄清楚它是如何為其他架構做支撐的,我最疑惑的是它是如何将WebPage和MVC進行很好的區分的 ,最關鍵的是它如何做到隻提供一個接口讓後續的相關架構都能基于這個公共的Routing接口進行擴充的,它的對象模型肯定很巧妙;我們需要去搞懂它,才能有信心去繼續我們的ASP.NET相關架構的後續學習;

注意:全文使用Routing一詞替代ASP.NETRouting一詞,特此說明,以免概念混淆;

問到ASP.NET最重要的擴充點在哪裡?我想我們都會異口同聲的說:在管道模型上,這也符合我們對此問題求解的一個基本思路;ASP.NET管道模型大家都懂的,在管道模型的相關事件中隻要我們定義相關的事件就可以在管道的進行中插入自己的邏輯在裡面;管道的最後執行接口是IHttpHander類型,隻有阻止原本預設的IHttpHander接口建立才有可能改變整個的處理流程;

圖2.1:

<a href="http://blog.51cto.com/attachment/201310/083805771.jpg" target="_blank"></a>

那麼Routing隻有在阻止IHttpHander接口的建立前先執行,才能扭轉整個處理路線的機會,上圖中顯示的Application Event(2)(IHttpHander執行)意思是說隻有在IHttpHander執行前的某個Application Event中進行Routing的執行才能在原本執行IHttpHander的地方執行其他定制的IHttpHander;而IHttpHander是ASP.NET架構的最終執行的接口,是以如果要想改變原本執行Page的Hander,需要提供自定義的IHttpHander接口對象;

換句話說,一切的執行入口其實在IHttpHander.ProcessRequest()方法中,但是現在沖突的是ASP.NET Routing卡在中間,它讓原本直接的處理流程變的有點撲簌迷離,它隔開了“ASP.NET基礎架構 " 與 "基于ASP.NET的應用架構 "(如:ASP.NETMVC\ASP.NETWEBAPI\自定義架構);

注意:“ASP.NET基礎架構”指ASP.NET本身的架構可以了解為傳統的WEBFROM;而“基于ASP.NET的應用架構”是指基于ASP.NET基礎架構而設計的如:MVC\WEBPAGE\WEBAPI之類的上層輕量級應用架構;

圖2.2:

<a href="http://blog.51cto.com/attachment/201310/083827327.jpg" target="_blank"></a>

其實這幅圖很明了的表達式了ASP.NETRouting的位置,它是用來為ASP.NET與ASP.NETMVC、ASP.NETWEBAPI承上啟下的關鍵紐帶;根據上面我們的分析思路,Routing是ASP.NET架構直接互動的對象模型,是以站在ASP.NET的角度它是不知道背後究竟發生了什麼事情,其實ASP.NETRouting已經在ASP.NETApplication某個生命事件中将原本的建立邏輯移花接木了;

Routing起到中間人的作用,将ASP.NET的相關邏輯透明包裝,我們雖然能在Routing的上層同樣可以使用相關的ASP.NET對象,但是概念已經發生了根本上的變化;我們可以随意的引入自定義的IHttpHander實作類,根據前端傳過來的Uri進行政策執行,也就是說你完全可以定義一套自己内部使用的Uri規則和處理架構,建立在Routing基礎之上會很容易;

根據IHttpModule、IHttpHander 的相關的知識,我們很容易就能知道從哪裡可以找到Routing的入口線索,如果我們都沒有猜錯的話在系統的Web.config檔案中肯定有一個專門處理Routing的IHttpModule,利用來它将ASP.NETRouting對象植入到ASP.NET架構之中; 我們找到.NET Framework環境配置的地方:C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config 在該檔案中我們可以找到系統級别的配置資訊; 其實這裡面配置的都是系統級别的選項,而我們程式裡面使用的Web.config檔案隻是用來配置跟應用程式相關的選項,這樣的好處是我們可以在應用程式級别很友善的改變系統的預設配置; 我們找到httpModules配置節,在倒數第二行發現一個name為UrlRoutingModule-4.0的IHttpModule配置,應該就是它了,最關鍵的是它的type資訊是System.Web.Routing.UrlRoutingModule 毋庸置疑了;

現在就好辦多了,我們隻要順藤摸瓜就能找到UrlRoutingModule是如何工作的了,不過先不能急,還有些思路并不清晰,我們繼續慢慢分析;按照這樣的一個思路,基本上我們可以斷定UrlRoutingModule就是協調ASP.NETRouting架構的紐帶;

圖3.1:

<a href="http://blog.51cto.com/attachment/201310/083850978.jpg" target="_blank"></a>

此圖總結了我們到目前為止的一個基本思路,底層ASP.NET架構處理HTTP的對象化,然後通過ASP.NETRouting Module建立IHttpHandler接口對象,再然後就是執行IHttpHander接口,共三個步驟;

作為應用架構也就是最上層的代碼,如何才能決定ASP.NETRouting架構在處理ASP.NET的調用的時候能使用自己的IHttpHander接口對象,這個問題就需要我們深入的看一下ASP.NETRouting路由對象的内部對象模型了;

這裡我将使用ASP.NETMVC作為應用架構來講解本例(目前我并不了解ASP.NETWEBAPI);那麼ASP.NETMVC作為應用層架構,是如何讓ASP.NETRouting幫助轉換IHttpHander接口的呢,這就不得不去分析Routing一些列的對象之間的組成關系及互相作用了;

根據3.】小節,我們已經了解ASP.NETRouting是使用UrlRoutingModuel對象來作為ASP.NET管道的監聽者,然後根據一系列的内部處理得出最終的IHttpHander接口對象;那麼要想搞清楚UrlRoutingModule是如何具體的協調這一切的,必須得深入的去分析源代碼才行,盡管我們隻需要了解一個80%那也少不了這個環節;

首當其沖需要搞清楚的就是UrlRoutingModule對象,根據源碼訓示我們基本上能确定幾個基本的原理,首先UrlRoutingModule繼承自IHttpModule接口,訂閱了Application.PostResolveRequstCache事件,在該事件中主要是通過全局路由對象表RouteTable對象擷取提供給上層使用的依賴注入接口IRouteHander接口;

【依賴注入接口】 這裡需要解釋一下什麼叫依賴注入接口,可以簡單的将依賴注入接口了解成提供給外界一個具體實作的機會;其實就是設計原則中的“依賴倒置原則”,在RouteData的内部不是直接依賴具體的對象;接口就是契約,提供一個接口就是約定雙方之間的契約;這裡是約定了Routing架構将使用IRouteHander接口來擷取最後的處理IHttpHander接口;

下面我們将對UrlRoutingModule對象進行分析,由于我們分析源代碼是想搞清楚對象模型之間的操作流程及關系,是以不可能分析所有的代碼,我們的重點是搞清楚他們的執行順序及原理;由于UrlRoutingModule對象是導火線,它的出現将接二連三的牽連其他的對象出現,我們将分小節進行分析,交界處将一帶而過;

根據我們前面的分析思路,我們首先要找到UrlRoutingModule綁定Application事件的地方;

1

2

3

4

<code>protected</code> <code>virtual</code> <code>void</code> <code>Init (HttpApplication application)</code>

<code>        </code><code>{</code>

<code>            </code><code>application.PostResolveRequestCache += PostResolveRequestCache;</code>

<code>        </code><code>}</code>

在PostResolverRequestCache方法中,我們将看到該方法調用了本地内部的一個同名方法:

5

<code>void</code> <code>PostResolveRequestCache (</code><code>object</code> <code>o, EventArgs e)</code>

<code>            </code><code>var</code> <code>app = (HttpApplication) o;</code>

<code>            </code><code>PostResolveRequestCache (</code><code>new</code> <code>HttpContextWrapper (app.Context));</code>

然後執行個體化了一個HttpContextWrapper包裝對象,傳入該同名方法;

6

7

8

9

10

11

<code>public</code> <code>virtual</code> <code>void</code> <code>PostResolveRequestCache (HttpContextBase context)</code>

<code>            </code><code>var</code> <code>rd = RouteCollection.GetRouteData (context);</code>

<code>           </code><code>//(1)比對RouteData對象,後面分析;</code>

<code>            </code><code>var</code> <code>rc = </code><code>new</code> <code>RequestContext (context, rd);</code>

<code>            </code><code>//(2)封裝計算出來的RouteData對象和目前HttpRequest對象;</code>

<code>            </code><code>IHttpHandler http = rd.RouteHandler.GetHttpHandler (rc);</code>

<code>            </code><code>//(3)使用(1)步驟計算出來的目前RouteData對象中的RouteHander屬性擷取路由處理程式IHttpHander接口;</code>

<code>            </code><code>context.Request.RequestContext = rc;</code>

<code>            </code><code>context.RemapHandler (http);</code>

<code>       </code><code>}</code>

當然我已經省略了部分不太相關的代碼,畢竟要想說清楚所有的代碼一篇文章顯然是不夠的;上述代碼中我用紅色标記出重要的部分;

首先是第一個重要點(1),比對RouteData對象;其實就是我們在程式裡面配置的Url模闆資料,當請求來的時候我們需要去根據目前請求的Url到路由表去比對是否有符合目前Url的路由對象;

<code>routes.MapRoute(</code>

<code>                </code><code>name: </code><code>"Default"</code><code>,</code>

<code>                </code><code>url: </code><code>"{controller}/{action}/{id}"</code><code>,</code>

<code>                </code><code>defaults: </code><code>new</code> <code>{ controller = </code><code>"Home"</code><code>, action = </code><code>"Index"</code><code>, id = UrlParameter.Optional }</code>

其實就是對應着本段代碼的配置,這段代碼處理後将是一個Route對象執行個體,而上面的RouteCollection就很好了解了,它是Route的強類型集合;

到目前為止,已經出現了好幾個跟Route相關的對象,沒關系,當我們将整條線分析到頭時将很清楚他們的作用;

第二個重要點(2),封裝RequestContext對象,其實我們從類型的名稱上就能确定它的用途,它是請求上下文,也是有界上下文;這裡面封裝了在下面擷取IHttpHander接口時将需要當作參數;

第三個重點(3),利用前面的比對得到的RouteData對象,其實RouteData是路由資料的意思,那麼什麼叫路由資料:就是路由比對成功後所生成的和路由相關的資料;還記得我們在3】節分析的原理嗎,UrlRoutingModule對上層提供基本的路由功能,但是具體的處理是在應用層面上;

那麼就是這裡通過RouteData.RouteHandler.GetHttpHandler(RequestContext requestContext) 方法擷取到的最終頂層應用處理器;

圖4.1:

<a href="http://blog.51cto.com/attachment/201310/084020788.jpg" target="_blank"></a>

上面的解釋可以使用這幅圖來簡單的表達;

UrlRoutingModule對象通過RouteData路由資料對象擷取IRouteHander接口,然後通過IRouteHander接口擷取最終的IHttpHander接口;

小結:其實可以将UrlRoutingModule對象了解成是ASP.NETRouting子產品的基礎部分,而擴充的地方則在我們應用程式配置的地方,也就是我們通常在Global.asax.cs檔案中配置的路由資料;當我們在配置Route對象的時候其實已經指定了IRouteHander接口,然後這個接口會被放入RouteData同名屬性中,而不是作為零散的對象被UrlRoutingModule直接擷取;

在4.1 】節中,UrlRoutingModule是路由架構的基礎設施部分,内置于. NETFramework系統及ASP.NET配置之中web.config;在ASP.NET進行版本更新的時候該部分工作已經由系統自動幫我們更新,我們在使用的時候隻需要建立ASP.NET3.5 SP1以上的版本都會自動擁有路由系統功能,因為根據微軟官方MSDN介紹,路由系統是在ASP.NET3.5 SP1中引入的;其實我們大部分使用的ASP.NET版本已經是4.5的,就算以前是2.0、3.0的版本也會陸續更新到最新的版本;因為新版本的架構提供了無數個讓你無法拒絕的優勢;

那麼當基礎部分有了之後我們能做到就是應用程式設計接口的程式設計,其實這部分才是我們接觸的地方;而這一小節我們将重點分析路由系統提供給我們應用層面的程式設計接口,也就是上面标題列出的幾個核心對象;

先基本介紹一下這幾個對象的意思和彼此之間的關系: RouteBase:很明顯是Route的基類,提供了作為自定義路由對象的頂層抽象,所有的路由架構的内部均使用抽象的RouteBase為依賴; Route:路由系統預設實作的路由對象繼承自RouteBase抽象基類,用來作為我們預設的路由配置對象,當然你可以可是實作自己的Route對象; RouteCollection:Route作為單個Url的配置,那麼系統中肯定會有多個Url規則的配置,是以RouteCollection對象是表示Route的強類型集合,該類繼承自 Collection&lt;RouteBase&gt; 類型;是以RouteCollection是用來作為Route的集合管理用的;注意這裡的泛型Collection&lt;T&gt;中的RouteBase,再一次提醒我們要“依賴倒置”; RouteTable:用來存放RouteCollection對象,路由表中有一系列的路由對象,而這一系列的對象就是RouteCollection管理的;在RouteTable中用Routes靜态屬性表示目前系統全局的路由映射表; 這裡很明顯能看出來對路由的一層一層抽象,從簡單的Route表示一個路由映射,再到表示Route的集合RouteCollection,再到最後的RouteTable的,抽象的很OO;

為了讓大家對上面這些對象的解釋有一個直覺的認識,我們用一張圖來解釋他們如何關聯和執行流程;

圖4.2:

<a href="http://blog.51cto.com/attachment/201310/084223220.jpg" target="_blank"></a>

下面我們将深入到各個對象的内部去摸索一下他們之間的互動,我們根據這種引用關系來分析,首先是Route對象;

【Route、RouteBase】

Route對象繼承自RouteBase代表一個Url模闆的配置,包括Url的模闆的字元串,如:api/order/102304,還有一些輔助性的内容,這不是本節的重點,我們隻要知道它是用來做Url的配置即可; Route對象不是直接被我們執行個體化的,而是通過應用層的擴充方法進行執行個體化,為什麼要這麼做,其實這裡就是路由為什麼能轉到上層的關鍵點;

根據ASP.NETMVC中的路由集合擴充類,也就是System.Web.Mvc.RouteCollectionExtensions靜态類中的擴充方法,這些擴充方法就是用來包裝我們在應用ASP.NET的時候配置Route使用的;是否還記得我們第4】節的一開始介紹了一個依賴注入接口的原理,這裡将通過依賴注入接口達到外挂自定義實作的目的;

在Route源碼中,我們将看到它有一個IRouteHander接口類型的屬性RouteHander;

<code>public</code> <code>class</code> <code>Route : RouteBase</code>

<code>{</code>

<code>  </code><code>public</code> <code>IRouteHandler RouteHandler { </code><code>get</code><code>; </code><code>set</code><code>; }</code>

<code>}</code>

這個IRouteHandler接口類型的屬性就是我們ASP.NETMVC将要實作的一個IRouteHandler接口;而這個接口的定義:

<code>public</code> <code>interface</code> <code>IRouteHandler</code>

<code>    </code><code>IHttpHandler GetHttpHandler (RequestContext requestContext);</code>

很簡單,就是為了建立出ASP.NET管道引擎最後執行的IHttpHandler接口; Route類有一個重寫了RouteBase的核心方法:

<code>public</code> <code>override</code> <code>RouteData GetRouteData (HttpContextBase httpContext)</code>

該方法是用來擷取目前路由的一些比對資料的,關于RouteData在4.1】節介紹過,詳細我們将看下面關于對它的詳細分析,這裡将不做介紹了;

小結:其實Route對象還算簡單,關鍵的兩點就是GetRouteData方法和IRouteHander接口,前者是用來擷取目前路由比對成功後的路由資訊,而後者是用來傳回最終要執行的IHttpHandler接口;

【RouteCollection、RouteTable】

RouteCollecton和RouteTable對象比較簡單;我們先來看RouteCollection對象,首先你可能會有疑問,為什麼不用一個簡單的Collection類型的對象來存放Route執行個體,非要實作了一個RouteCollection;不看源碼還真不知道它内部做了很多工作,首先最重要的就是線程并發情況下的Look機制;由于我們的RouteCollection對象是全局靜态對象,會同時存在着多個線程并發的讀取這個對象,是以必須在對集合通路的時候進行互斥控制;比如說這段代碼:

<code>public</code> <code>void</code> <code>Add (</code><code>string</code> <code>name, RouteBase item)</code>

<code>    </code><code>lock</code> <code>(GetWriteLock ()) {</code>

<code>        </code><code>base</code><code>.Add (item);</code>

<code>        </code><code>if</code> <code>(!String.IsNullOrEmpty (name))</code>

<code>            </code><code>d.Add (name, item);</code>

<code>    </code><code>}</code>

在添加路由的時候首先鎖住寫入對象,然後才能安全的進行操作;我們接着RouteTable對象,這個對象最簡單,就是一個靜态屬性Routes用來存放全局路由表;

<code>public</code> <code>class</code> <code>RouteTable</code>

<code>    </code><code>static</code> <code>RouteTable ()</code>

<code>    </code><code>{</code>

<code>        </code><code>Routes = </code><code>new</code> <code>RouteCollection ();</code>

<code>    </code><code>public</code> <code>static</code> <code>RouteCollection Routes { </code><code>get</code><code>; </code><code>private</code> <code>set</code><code>; }</code>

當首次擷取Routes屬性時,會在靜态構造函數中執行個體化RouteCollection對象;

在第4.2】小節中,我們分析了路由系統的幾個核心對象,但是核心對象要想運作起來中間必須有一些資料封裝的對象為他們消除資料傳遞的問題;而這小節的三個核心對象真是路由系統能成功工作的必不可少的資料存放、資料傳輸容器的核心對象;

RouteValueDictionary:路由對象内部存放中間值使用的對象,比如Url模闆的預設值,命名空間,位址欄傳過來的參數等等;當然也可以用來存放任何Key-Value形式的任何值; RouteData:路由資料,用來包裝根據路由Url比對成功後的路由資料封裝,最重要的是将IRouteHander接口傳遞到UrlRoutingModule中去; RequestContext:請求上下文,将HttpRequest、RouteData包裝起來傳入IRouteHander接口擷取IHttpHander接口;因為IRouteHandler接口方法GetHttpHandler需要知道目前請求的一些資訊和根據目前Url處理後的路由資料才能計算出目前的IHttpHandler接口;

圖4.3:

<a href="http://blog.51cto.com/attachment/201310/084422505.jpg" target="_blank"></a>

下面詳細的分析每個對象的内部原理;

【RouteValueDictionary】

RouteValueDirctionary對象是在路由對象内部存放資料用的,比如:我們在配置路由的時候,可以指定一些預設值、命名空間等等;

看RouteValueDictionary源碼定義:

<code>public</code> <code>class</code> <code>RouteValueDictionary : IDictionary&lt;</code><code>string</code><code>, </code><code>object</code><code>&gt;</code>

該類型繼承自字典接口IDictionary&lt;string,object&gt;,繼承自字典接口而不是繼承自字典基類目的隻是想使用字典的行為而不是它的預設實作;在RouteValueDictionary内部使用了一個Dictionary&lt;string,object&gt;類型作為最終容器;

<code>Dictionary&lt;</code><code>string</code><code>,</code><code>object</code><code>&gt; d = </code><code>new</code> <code>Dictionary&lt;</code><code>string</code><code>,</code><code>object</code><code>&gt; (CaseInsensitiveStringComparer.Instance);</code>

在構造函數中使用了一個内部類CaseInsensitiveStringComparer進行Key的相等比較:

12

<code>internal</code> <code>class</code> <code>CaseInsensitiveStringComparer : IEqualityComparer&lt;</code><code>string</code><code>&gt;</code>

<code>            </code><code>public</code> <code>static</code> <code>readonly</code> <code>CaseInsensitiveStringComparer Instance = </code><code>new</code> <code>CaseInsensitiveStringComparer ();</code>

<code>            </code><code>public</code> <code>int</code> <code>GetHashCode (</code><code>string</code> <code>obj)</code>

<code>            </code><code>{</code>

<code>                </code><code>return</code> <code>obj.ToLower (CultureInfo.InvariantCulture).GetHashCode ();</code>

<code>            </code><code>}</code>

<code>            </code><code>public</code> <code>bool</code> <code>Equals (</code><code>string</code> <code>obj1, </code><code>string</code> <code>obj2)</code>

<code>                </code><code>return</code> <code>String.Equals (obj1, obj2, StringComparison.OrdinalIgnoreCase);</code>

IEqualityComparer接口還是很不錯的,不過現在基本上不這麼用了,而是直接提供了一個Lambda做為比較函數;

【RouteData】

路由資料對象,它的大概意思我想大家應該知道了,上面提到過很多次,這裡就不介紹了;我們直接看一下RouteData内部核心代碼段:

<code>public</code> <code>RouteData (RouteBase route, IRouteHandler routeHandler)</code>

<code>    </code><code>// arguments can be null.</code>

<code>    </code><code>Route = route;</code>

<code>    </code><code>RouteHandler = routeHandler;</code>

<code>    </code><code>DataTokens = </code><code>new</code> <code>RouteValueDictionary ();</code>

<code>    </code><code>Values = </code><code>new</code> <code>RouteValueDictionary ();</code>

<code>public</code> <code>RouteValueDictionary DataTokens { </code><code>get</code><code>; </code><code>private</code> <code>set</code><code>; }</code>

<code>public</code> <code>RouteBase Route { </code><code>get</code><code>; </code><code>set</code><code>; }</code>

<code>public</code> <code>IRouteHandler RouteHandler { </code><code>get</code><code>; </code><code>set</code><code>; }</code>

<code>public</code> <code>RouteValueDictionary Values { </code><code>get</code><code>; </code><code>private</code> <code>set</code><code>; }</code>

通過構造函數我們能了解到,儲存了對Route對象的引用和IRouteHander接口的引用,為什麼将IRouteHandler作為構造函數參數,那是因為RouteBase根本沒有對IRouteHander接口的屬性定義;IRouteHandler接口在不在RouteBase或Route中不重要,因為Route可以是自定義的,這裡的強制性是在RouteData中,它的構造函數必須接受IRouteHandler類型接口;

我們接着看,在構造函數的下面兩行代碼中分别是執行個體化了DataTokens、Values兩個屬性,而類型是RouteValueDictionary,這也剛好和我們上面分析的對上了;RouteValueDictionary是内部用來儲存這些零散鍵值對資料容器,在Route、RouteData還有其他地方均需要用到;就是因為RouteValueDictionary的Value是Object類型,是以可以用來存放任何類型的值,比較通用;

【RequestContext 】

RequestContext在上面也已經接觸很多次了,表示請求上下文,也就是跟當請求相關的所有資料都封裝在裡面;在後面的文章中,我們将接觸很多類似Context的對象,如:ControlContext,ViewContext之類的,都是用來控制上下文的邊界,而不是直接傳遞零散的參數;

IRouteHandler接口是路由架構起作用的核心,隻有提供了IRouteHandler實作才能順利的得到背後的IHttpHandler接口;ASP.NETMVC提供了MvcRouteHandler對象來實作IRouteHandler接口,MvcRouteHandler在内部執行個體化實作了IHttpHandler接口的MvcHandler對象;MvcHandler然後通過RequestContext對象擷取RouteData對象,接着得到相應的Control資訊,進行後續的執行處理;

在ASP.NETRouting路由架構中有一個很重要的IHttpHandler接口對象UrlRoutingHanlder,我想你肯定很疑惑,為什麼需要這樣一個對象;其實它的存在是為了提供給我們繞過UrlRoutingModule子產品的機會;根據上面的詳細的分析,我們知道路由的入口在UrlRoutingModule,所有的路由相關的映射工作都在該類中完成,但是有時候我們很想繞過UrlRoutingModule進行簡單的處理或者性能方面的優化考慮,這就派上用場了;我能想到的使用場景目前來看是對ASP.NET第版本的項目做Url重寫是比較友善,首先我們的項目需要建立在低版本的ASP.NET之上,但是需要添加Url.ReWriter的功能,就需要我們自己去實作這樣的功能;

但是工作量和性能都很難控制好,如果使用這裡提供的UrlRoutingHandler進行實作就很友善了,UrlRoutingHandler給我們使用ASP.NETRouting架構的機會同時也不需要關心是否配置了UrlRoutingModule;

<code>public</code> <code>abstract</code> <code>class</code> <code>UrlRoutingHandler : IHttpHandler</code>

根據代碼看出它是一個抽象類,直接實作IHttpHanlder接口,但是重要的是ProcessRequest方法;

13

14

<code>protected</code> <code>virtual</code> <code>void</code> <code>ProcessRequest (HttpContextBase httpContext)</code>

<code>            </code><code>if</code> <code>(httpContext == </code><code>null</code><code>)</code>

<code>                </code><code>throw</code> <code>new</code> <code>ArgumentNullException (</code><code>"httpContext"</code><code>);</code>

<code>            </code><code>var</code> <code>rd = RouteCollection.GetRouteData (httpContext);</code>

<code>            </code><code>if</code> <code>(rd == </code><code>null</code><code>)</code>

<code>                </code><code>throw</code> <code>new</code> <code>HttpException (</code><code>"The incoming request does not match any route"</code><code>);</code>

<code>            </code><code>if</code> <code>(rd.RouteHandler == </code><code>null</code><code>)</code>

<code>                </code><code>throw</code> <code>new</code> <code>InvalidOperationException (</code><code>"No  IRouteHandler is assigned to the selected route"</code><code>);</code>

<code>            </code><code>RequestContext rc = </code><code>new</code> <code>RequestContext (httpContext, rd);</code>

<code>            </code><code>var</code> <code>hh = rd.RouteHandler.GetHttpHandler (rc);</code>

<code>            </code><code>VerifyAndProcessRequest (hh, httpContext);</code>

<code>        </code><code>protected</code> <code>abstract</code> <code>void</code> <code>VerifyAndProcessRequest (IHttpHandler httpHandler, HttpContextBase httpContext);</code>

該方法的邏輯跟UrlRoutingModule裡的PostResolveRequestCache方法是差不多的,都會通過全局RouteCollection集合進行比對目前的RouteData對象;那就足夠說明這個過程不會再通過UrlRoutingModule子產品;方法的最後一行是執行一個模闆方法:VerifyAndProcessRequest ,該方法是留給子類去實作的;

那麼這裡将路由和執行合在一起了,基類負責路由子類負責執行,很不錯的設計方法;

總結:這篇文章基本上介紹了跟路由相關的核心對象,但是還有一些其他輔助的類這裡并沒有進行講解,當然如果你有興趣可以自己去看看;這篇文章是為了讓我們能對路由的處理流程及結構有個了解,做到能在适當的時候進行擴充和查找問題;

 本文轉自 王清培 51CTO部落格,原文連結:http://blog.51cto.com/wangqingpei557/1312422,如需轉載請自行聯系原作者