溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

.NET/ASP.NET Routing路由(深入解析路由系統(tǒng)架構(gòu)原理)

發(fā)布時(shí)間:2020-08-03 22:10:41 來源:網(wǎng)絡(luò) 閱讀:18097 作者:王清培 欄目:編程語言

閱讀目錄:

  • 1.開篇介紹

  • 2.ASP.NET Routing 路由對(duì)象模型的位置

  • 3.ASP.NET Routing 路由對(duì)象模型的入口

  • 4.ASP.NET Routing 路由對(duì)象模型的內(nèi)部結(jié)構(gòu)

    • 4.1】UrlRoutingModule 對(duì)象內(nèi)部結(jié)構(gòu)

    • 4.2】RouteBase、Route、RouteCollection、RouteTable 路由核心對(duì)象模型

    • 4.3】RouteValueDictionary、RouteData、RequestContext 路由數(shù)據(jù)對(duì)象模型

    • 4.4】IRouteHandler 、IHttpHandler兩個(gè)接口之間的關(guān)系


  • 5.】UrlRoutingHandler 對(duì)象內(nèi)部結(jié)構(gòu)及擴(kuò)展應(yīng)用

1】開篇介紹

這篇文章讓我們愉快的學(xué)習(xí)一下ASP.NET中核心的對(duì)象模型Routing模塊,為什么說愉快呢,因?yàn)镽outing正是建立在大家都比較熟悉的ASP.NET管道模型基礎(chǔ)之上的,所以相比其他一些陌生的概念會(huì)輕松很多,不過不要緊一回生二回熟;

ASP.NET Routing 系統(tǒng)是一切通過ASP.NET進(jìn)行Uri訪問應(yīng)用程序的基礎(chǔ)(并非物理文件的直接映射);隨著Routing的出現(xiàn),我們的WEB設(shè)計(jì)已經(jīng)和以前大不一樣;越來越輕量級(jí)、簡(jiǎn)單化,都通過簡(jiǎn)便的Uri資源的方式進(jìn)行處理,將精力放在業(yè)務(wù)的設(shè)計(jì)上;現(xiàn)在主流的Rest ful api 也都是建立在這樣的一種機(jī)制下的,然而我們的ASP.NETMVC也是一種通過獨(dú)立的Uri進(jìn)行程序訪問處理的框架,所以也是建立在ASP.NET Routing;再者就是現(xiàn)在也比較熱門的ASP.NET技術(shù)(ASP.NETWEBAPI);都是建立在Routing框架之上,可見它還是蠻重要的;

所以這篇文章讓我們來分析一下Routing的工作原理,它為什么能在不影響現(xiàn)有框架的基礎(chǔ)上提供這么好的擴(kuò)展性,真的讓人很想去一探究竟;目前非??捎^是我們都了解ASP.NET現(xiàn)有的框架知識(shí),我們大概了解它肯定是在ASP.NET管道模型的哪個(gè)位置進(jìn)行了相應(yīng)的攔截;

下面我們帶著這個(gè)重要的線索來一點(diǎn)一點(diǎn)弄清楚它是如何為其他框架做支撐的,我最疑惑的是它是如何將WebPage和MVC進(jìn)行很好的區(qū)分的 ,最關(guān)鍵的是它如何做到只提供一個(gè)接口讓后續(xù)的相關(guān)框架都能基于這個(gè)公共的Routing接口進(jìn)行擴(kuò)展的,它的對(duì)象模型肯定很巧妙;我們需要去搞懂它,才能有信心去繼續(xù)我們的ASP.NET相關(guān)框架的后續(xù)學(xué)習(xí);

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

2】ASP.NETRouting路由對(duì)象模型的位置

問到ASP.NET最重要的擴(kuò)展點(diǎn)在哪里?我想我們都會(huì)異口同聲的說:在管道模型上,這也符合我們對(duì)此問題求解的一個(gè)基本思路;ASP.NET管道模型大家都懂的,在管道模型的相關(guān)事件中只要我們定義相關(guān)的事件就可以在管道的處理中插入自己的邏輯在里面;管道的最后執(zhí)行接口是IHttpHander類型,只有阻止原本默認(rèn)的IHttpHander接口創(chuàng)建才有可能改變整個(gè)的處理流程;

圖2.1:

.NET/ASP.NET Routing路由(深入解析路由系統(tǒng)架構(gòu)原理)

那么Routing只有在阻止IHttpHander接口的創(chuàng)建前先執(zhí)行,才能扭轉(zhuǎn)整個(gè)處理路線的機(jī)會(huì),上圖中顯示的Application Event(2)(IHttpHander執(zhí)行)意思是說只有在IHttpHander執(zhí)行前的某個(gè)Application Event中進(jìn)行Routing的執(zhí)行才能在原本執(zhí)行IHttpHander的地方執(zhí)行其他定制的IHttpHander;而IHttpHander是ASP.NET框架的最終執(zhí)行的接口,所以如果要想改變?cè)緢?zhí)行Page的Hander,需要提供自定義的IHttpHander接口對(duì)象;

換句話說,一切的執(zhí)行入口其實(shí)在IHttpHander.Proce***equest()方法中,但是現(xiàn)在矛盾的是ASP.NET Routing 卡在中間,它讓原本直接的處理流程變的有點(diǎn)撲簌迷離,它隔開了“ASP.NET基礎(chǔ)框架 " "基于ASP.NET的應(yīng)用框架 "(如:ASP.NETMVC\ASP.NETWEBAPI\自定義框架);

注意:“ASP.NET基礎(chǔ)框架”指ASP.NET本身的框架可以理解為傳統(tǒng)的WEBFROM;而“基于ASP.NET的應(yīng)用框架”是指基于ASP.NET基礎(chǔ)框架而設(shè)計(jì)的如:MVC\WEBPAGE\WEBAPI之類的上層輕量級(jí)應(yīng)用框架;

圖2.2:

.NET/ASP.NET Routing路由(深入解析路由系統(tǒng)架構(gòu)原理)

其實(shí)這幅圖很明了的表達(dá)式了ASP.NETRouting的位置,它是用來為ASP.NETASP.NETMVC、ASP.NETWEBAPI承上啟下的關(guān)鍵紐帶;根據(jù)上面我們的分析思路,Routing是ASP.NET框架直接交互的對(duì)象模型,所以站在ASP.NET的角度它是不知道背后究竟發(fā)生了什么事情,其實(shí)ASP.NETRouting已經(jīng)在ASP.NETApplication某個(gè)生命事件中將原本的創(chuàng)建邏輯移花接木了;

3.】ASP.NETRouting路由對(duì)象模型的入口

Routing起到中間人的作用,將ASP.NET的相關(guān)邏輯透明包裝,我們雖然能在Routing的上層同樣可以使用相關(guān)的ASP.NET對(duì)象,但是概念已經(jīng)發(fā)生了根本上的變化;我們可以隨意的引入自定義的IHttpHander實(shí)現(xiàn)類,根據(jù)前端傳過來的Uri進(jìn)行策略執(zhí)行,也就是說你完全可以定義一套自己內(nèi)部使用的Uri規(guī)則和處理框架,建立在Routing基礎(chǔ)之上會(huì)很容易;

根據(jù)IHttpModule、IHttpHander 的相關(guān)的知識(shí),我們很容易就能知道從哪里可以找到Routing的入口線索,如果我們都沒有猜錯(cuò)的話在系統(tǒng)的Web.config文件中肯定有一個(gè)專門處理Routing的IHttpModule,利用來它將ASP.NETRouting對(duì)象植入到ASP.NET框架之中;

我們找到.NET Framework環(huán)境配置的地方:C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config 在該文件中我們可以找到系統(tǒng)級(jí)別的配置信息;

其實(shí)這里面配置的都是系統(tǒng)級(jí)別的選項(xiàng),而我們程序里面使用的Web.config文件只是用來配置跟應(yīng)用程序相關(guān)的選項(xiàng),這樣的好處是我們可以在應(yīng)用程序級(jí)別很方便的改變系統(tǒng)的默認(rèn)配置;

我們找到httpModules配置節(jié),在倒數(shù)第二行發(fā)現(xiàn)一個(gè)nameUrlRoutingModule-4.0IHttpModule配置,應(yīng)該就是它了,最關(guān)鍵的是它的type信息是System.Web.Routing.UrlRoutingModule 毋庸置疑了;

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

圖3.1:

.NET/ASP.NET Routing路由(深入解析路由系統(tǒng)架構(gòu)原理)

此圖總結(jié)了我們到目前為止的一個(gè)基本思路,底層ASP.NET框架處理HTTP的對(duì)象化,然后通過ASP.NETRouting Module創(chuàng)建IHttpHandler接口對(duì)象,再然后就是執(zhí)行IHttpHander接口,共三個(gè)步驟;

作為應(yīng)用框架也就是最上層的代碼,如何才能決定ASP.NETRouting框架在處理ASP.NET的調(diào)用的時(shí)候能使用自己的IHttpHander接口對(duì)象,這個(gè)問題就需要我們深入的看一下ASP.NETRouting路由對(duì)象的內(nèi)部對(duì)象模型了;

4.】ASP.NETRouting路由對(duì)象模型的內(nèi)部結(jié)構(gòu)

這里我將使用ASP.NETMVC作為應(yīng)用框架來講解本例(目前我并不了解ASP.NETWEBAPI);那么ASP.NETMVC作為應(yīng)用層框架,是如何讓ASP.NETRouting幫助轉(zhuǎn)換IHttpHander接口的呢,這就不得不去分析Routing一些列的對(duì)象之間的組成關(guān)系及互相作用了;

根據(jù)3.】小節(jié),我們已經(jīng)了解ASP.NETRouting是使用UrlRoutingModuel對(duì)象來作為ASP.NET管道的監(jiān)聽者,然后根據(jù)一系列的內(nèi)部處理得出最終的IHttpHander接口對(duì)象;那么要想搞清楚UrlRoutingModule是如何具體的協(xié)調(diào)這一切的,必須得深入的去分析源代碼才行,盡管我們只需要了解一個(gè)80%那也少不了這個(gè)環(huán)節(jié);

注意:需要源代碼的朋友可以直接去一下站點(diǎn)獲取,微軟官方開源網(wǎng)站:http://www.codeplex.com/,開源中國(guó):http://www.oschina.net/都可以找到源代碼;

4.1】UrlRoutingModule對(duì)象內(nèi)部結(jié)構(gòu)

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

【依賴注入接口】

這里需要解釋一下什么叫依賴注入接口,可以簡(jiǎn)單的將依賴注入接口理解成提供給外界一個(gè)具體實(shí)現(xiàn)的機(jī)會(huì);其實(shí)就是設(shè)計(jì)原則中的“依賴倒置原則”,在RouteData的內(nèi)部不是直接依賴具體的對(duì)象;接口就是契約,提供一個(gè)接口就是約定雙方之間的契約;這里是約定了Routing框架將使用IRouteHander接口來獲取最后的處理IHttpHander接口;

下面我們將對(duì)UrlRoutingModule對(duì)象進(jìn)行分析,由于我們分析源代碼是想搞清楚對(duì)象模型之間的操作流程及關(guān)系,所以不可能分析所有的代碼,我們的重點(diǎn)是搞清楚他們的執(zhí)行順序及原理;由于UrlRoutingModule對(duì)象是導(dǎo)火線,它的出現(xiàn)將接二連三的牽連其他的對(duì)象出現(xiàn),我們將分小節(jié)進(jìn)行分析,交界處將一帶而過;

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

protected virtual void Init (HttpApplication application)
        {
            application.PostResolveRequestCache += PostResolveRequestCache;
        }

在PostResolverRequestCache方法中,我們將看到該方法調(diào)用了本地內(nèi)部的一個(gè)同名方法:

void PostResolveRequestCache (object o, EventArgs e)
        {
            var app = (HttpApplication) o;
            PostResolveRequestCache (new HttpContextWrapper (app.Context));
        }

然后實(shí)例化了一個(gè)HttpContextWrapper包裝對(duì)象,傳入該同名方法;

public virtual void PostResolveRequestCache (HttpContextBase context)
        {
            var rd = RouteCollection.GetRouteData (context);
           //(1)匹配RouteData對(duì)象,后面分析;
            var rc = new RequestContext (context, rd);
            //(2)封裝計(jì)算出來的RouteData對(duì)象和當(dāng)前HttpRequest對(duì)象;
            IHttpHandler http = rd.RouteHandler.GetHttpHandler (rc);
            //(3)使用(1)步驟計(jì)算出來的當(dāng)前RouteData對(duì)象中的RouteHander屬性獲取路由處理程序IHttpHander接口;
            context.Request.RequestContext = rc;
            context.RemapHandler (http);
       }

當(dāng)然我已經(jīng)省略了部分不太相關(guān)的代碼,畢竟要想說清楚所有的代碼一篇文章顯然是不夠的;上述代碼中我用紅色標(biāo)記出重要的部分;

首先是第一個(gè)重要點(diǎn)(1),匹配RouteData對(duì)象;其實(shí)就是我們?cè)诔绦蚶锩媾渲玫腢rl模板數(shù)據(jù),當(dāng)請(qǐng)求來的時(shí)候我們需要去根據(jù)當(dāng)前請(qǐng)求的Url到路由表去匹配是否有符合當(dāng)前Url的路由對(duì)象;

routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

其實(shí)就是對(duì)應(yīng)著本段代碼的配置,這段代碼處理后將是一個(gè)Route對(duì)象實(shí)例,而上面的RouteCollection就很好理解了,它是Route的強(qiáng)類型集合;

到目前為止,已經(jīng)出現(xiàn)了好幾個(gè)跟Route相關(guān)的對(duì)象,沒關(guān)系,當(dāng)我們將整條線分析到頭時(shí)將很清楚他們的作用;

第二個(gè)重要點(diǎn)(2),封裝RequestContext對(duì)象,其實(shí)我們從類型的名稱上就能確定它的用途,它是請(qǐng)求上下文,也是有界上下文;這里面封裝了在下面獲取IHttpHander接口時(shí)將需要當(dāng)作參數(shù);

第三個(gè)重點(diǎn)(3),利用前面的匹配得到的RouteData對(duì)象,其實(shí)RouteData是路由數(shù)據(jù)的意思,那么什么叫路由數(shù)據(jù):就是路由匹配成功后所生成的和路由相關(guān)的數(shù)據(jù);還記得我們?cè)?】節(jié)分析的原理嗎,UrlRoutingModule對(duì)上層提供基本的路由功能,但是具體的處理是在應(yīng)用層面上;

那么就是這里通過RouteData.RouteHandler.GetHttpHandler(RequestContext requestContext) 方法獲取到的最終頂層應(yīng)用處理器;

圖4.1:

.NET/ASP.NET Routing路由(深入解析路由系統(tǒng)架構(gòu)原理)

上面的解釋可以使用這幅圖來簡(jiǎn)單的表達(dá);

UrlRoutingModule對(duì)象通過RouteData路由數(shù)據(jù)對(duì)象獲取IRouteHander接口,然后通過IRouteHander接口獲取最終的IHttpHander接口;

小結(jié):其實(shí)可以將UrlRoutingModule對(duì)象理解成是ASP.NETRouting模塊的基礎(chǔ)部分,而擴(kuò)展的地方則在我們應(yīng)用程序配置的地方,也就是我們通常在Global.asax.cs文件中配置的路由數(shù)據(jù);當(dāng)我們?cè)谂渲肦oute對(duì)象的時(shí)候其實(shí)已經(jīng)指定了IRouteHander接口,然后這個(gè)接口會(huì)被放入RouteData同名屬性中,而不是作為零散的對(duì)象被UrlRoutingModule直接獲??;

4.2】RouteBase、Route、RouteCollection、RouteTable路由核心對(duì)象模型

在4.1 】節(jié)中,UrlRoutingModule是路由框架的基礎(chǔ)設(shè)施部分,內(nèi)置于. NETFramework系統(tǒng)及ASP.NET配置之中web.config;在ASP.NET進(jìn)行版本升級(jí)的時(shí)候該部分工作已經(jīng)由系統(tǒng)自動(dòng)幫我們升級(jí),我們?cè)谑褂玫臅r(shí)候只需要?jiǎng)?chuàng)建ASP.NET3.5 SP1以上的版本都會(huì)自動(dòng)擁有路由系統(tǒng)功能,因?yàn)楦鶕?jù)微軟官方MSDN介紹,路由系統(tǒng)是在ASP.NET3.5 SP1中引入的;其實(shí)我們大部分使用的ASP.NET版本已經(jīng)是4.5的,就算以前是2.0、3.0的版本也會(huì)陸續(xù)升級(jí)到最新的版本;因?yàn)樾掳姹镜目蚣芴峁┝藷o數(shù)個(gè)讓你無法拒絕的優(yōu)勢(shì);

那么當(dāng)基礎(chǔ)部分有了之后我們能做到就是應(yīng)用編程接口的編程,其實(shí)這部分才是我們接觸的地方;而這一小節(jié)我們將重點(diǎn)分析路由系統(tǒng)提供給我們應(yīng)用層面的編程接口,也就是上面標(biāo)題列出的幾個(gè)核心對(duì)象;

先基本介紹一下這幾個(gè)對(duì)象的意思和彼此之間的關(guān)系:

RouteBase:很明顯是Route的基類,提供了作為自定義路由對(duì)象的頂層抽象,所有的路由框架的內(nèi)部均使用抽象的RouteBase為依賴;

Route:路由系統(tǒng)默認(rèn)實(shí)現(xiàn)的路由對(duì)象繼承自RouteBase抽象基類,用來作為我們默認(rèn)的路由配置對(duì)象,當(dāng)然你可以可是實(shí)現(xiàn)自己的Route對(duì)象;

RouteCollection:Route作為單個(gè)Url的配置,那么系統(tǒng)中肯定會(huì)有多個(gè)Url規(guī)則的配置,所以RouteCollection對(duì)象是表示Route的強(qiáng)類型集合,該類繼承自 Collection<RouteBase> 類型;所以RouteCollection是用來作為Route的集合管理用的;注意這里的泛型Collection<T>中的RouteBase,再一次提醒我們要“依賴倒置”;

RouteTable:用來存放RouteCollection對(duì)象,路由表中有一系列的路由對(duì)象,而這一系列的對(duì)象就是RouteCollection管理的;在RouteTable中用Routes靜態(tài)屬性表示當(dāng)前系統(tǒng)全局的路由映射表;

這里很明顯能看出來對(duì)路由的一層一層抽象,從簡(jiǎn)單的Route表示一個(gè)路由映射,再到表示Route的集合RouteCollection,再到最后的RouteTable的,抽象的很OO;

為了讓大家對(duì)上面這些對(duì)象的解釋有一個(gè)直觀的認(rèn)識(shí),我們用一張圖來解釋他們?nèi)绾侮P(guān)聯(lián)和執(zhí)行流程;

圖4.2:

.NET/ASP.NET Routing路由(深入解析路由系統(tǒng)架構(gòu)原理)

下面我們將深入到各個(gè)對(duì)象的內(nèi)部去摸索一下他們之間的交互,我們根據(jù)這種引用關(guān)系來分析,首先是Route對(duì)象;

【Route、RouteBase】

Route對(duì)象繼承自RouteBase代表一個(gè)Url模板的配置,包括Url的模板的字符串,如:api/order/102304,還有一些輔助性的內(nèi)容,這不是本節(jié)的重點(diǎn),我們只要知道它是用來做Url的配置即可; Route對(duì)象不是直接被我們實(shí)例化的,而是通過應(yīng)用層的擴(kuò)展方法進(jìn)行實(shí)例化,為什么要這么做,其實(shí)這里就是路由為什么能轉(zhuǎn)到上層的關(guān)鍵點(diǎn);

根據(jù)ASP.NETMVC中的路由集合擴(kuò)展類,也就是System.Web.Mvc.RouteCollectionExtensions靜態(tài)類中的擴(kuò)展方法,這些擴(kuò)展方法就是用來包裝我們?cè)趹?yīng)用ASP.NET的時(shí)候配置Route使用的;是否還記得我們第4】節(jié)的一開始介紹了一個(gè)依賴注入接口的原理,這里將通過依賴注入接口達(dá)到外掛自定義實(shí)現(xiàn)的目的;

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

public class Route : RouteBase
{
  public IRouteHandler RouteHandler { get; set; }
}

這個(gè)IRouteHandler接口類型的屬性就是我們ASP.NETMVC將要實(shí)現(xiàn)的一個(gè)IRouteHandler接口;而這個(gè)接口的定義:

public interface IRouteHandler
{
    IHttpHandler GetHttpHandler (RequestContext requestContext);
}

很簡(jiǎn)單,就是為了創(chuàng)建出ASP.NET管道引擎最后執(zhí)行的IHttpHandler接口; Route類有一個(gè)重寫了RouteBase的核心方法:

public override RouteData GetRouteData (HttpContextBase httpContext)

該方法是用來獲取當(dāng)前路由的一些匹配數(shù)據(jù)的,關(guān)于RouteData在4.1】節(jié)介紹過,詳細(xì)我們將看下面關(guān)于對(duì)它的詳細(xì)分析,這里將不做介紹了;

小結(jié):其實(shí)Route對(duì)象還算簡(jiǎn)單,關(guān)鍵的兩點(diǎn)就是GetRouteData方法和IRouteHander接口,前者是用來獲取當(dāng)前路由匹配成功后的路由信息,而后者是用來返回最終要執(zhí)行的IHttpHandler接口;

【RouteCollection、RouteTable】

RouteCollecton和RouteTable對(duì)象比較簡(jiǎn)單;我們先來看RouteCollection對(duì)象,首先你可能會(huì)有疑問,為什么不用一個(gè)簡(jiǎn)單的Collection類型的對(duì)象來存放Route實(shí)例,非要實(shí)現(xiàn)了一個(gè)RouteCollection;不看源碼還真不知道它內(nèi)部做了很多工作,首先最重要的就是線程并發(fā)情況下的Look機(jī)制;由于我們的RouteCollection對(duì)象是全局靜態(tài)對(duì)象,會(huì)同時(shí)存在著多個(gè)線程并發(fā)的讀取這個(gè)對(duì)象,所以必須在對(duì)集合訪問的時(shí)候進(jìn)行互斥控制;比如說這段代碼:

public void Add (string name, RouteBase item)
{
    lock (GetWriteLock ()) {
        base.Add (item);
        if (!String.IsNullOrEmpty (name))
            d.Add (name, item);
    }
}

在添加路由的時(shí)候首先鎖住寫入對(duì)象,然后才能安全的進(jìn)行操作;我們接著RouteTable對(duì)象,這個(gè)對(duì)象最簡(jiǎn)單,就是一個(gè)靜態(tài)屬性Routes用來存放全局路由表;

public class RouteTable
{
    static RouteTable ()
    {
        Routes = new RouteCollection ();
    }
    public static RouteCollection Routes { get; private set; }
}

當(dāng)首次獲取Routes屬性時(shí),會(huì)在靜態(tài)構(gòu)造函數(shù)中實(shí)例化RouteCollection對(duì)象;

4.3】RouteValueDictionary、RouteData、RequestContext 路由數(shù)據(jù)對(duì)象模型

在第4.2】小節(jié)中,我們分析了路由系統(tǒng)的幾個(gè)核心對(duì)象,但是核心對(duì)象要想運(yùn)行起來中間必須有一些數(shù)據(jù)封裝的對(duì)象為他們消除數(shù)據(jù)傳遞的問題;而這小節(jié)的三個(gè)核心對(duì)象真是路由系統(tǒng)能成功工作的必不可少的數(shù)據(jù)存放、數(shù)據(jù)傳輸容器的核心對(duì)象;

先基本介紹一下這幾個(gè)對(duì)象的意思和彼此之間的關(guān)系:

RouteValueDictionary:路由對(duì)象內(nèi)部存放中間值使用的對(duì)象,比如Url模板的默認(rèn)值,命名空間,地址欄傳過來的參數(shù)等等;當(dāng)然也可以用來存放任何Key-Value形式的任何值;

RouteData:路由數(shù)據(jù),用來包裝根據(jù)路由Url匹配成功后的路由數(shù)據(jù)封裝,最重要的是將IRouteHander接口傳遞到UrlRoutingModule中去;

RequestContext:請(qǐng)求上下文,將HttpRequest、RouteData包裝起來傳入IRouteHander接口獲取IHttpHander接口;因?yàn)镮RouteHandler接口方法GetHttpHandler需要知道當(dāng)前請(qǐng)求的一些信息和根據(jù)當(dāng)前Url處理后的路由數(shù)據(jù)才能計(jì)算出當(dāng)前的IHttpHandler接口;

為了讓大家對(duì)上面這些對(duì)象的解釋有一個(gè)直觀的認(rèn)識(shí),我們用一張圖來解釋他們?nèi)绾侮P(guān)聯(lián)和執(zhí)行流程;

圖4.3:

.NET/ASP.NET Routing路由(深入解析路由系統(tǒng)架構(gòu)原理)

下面詳細(xì)的分析每個(gè)對(duì)象的內(nèi)部原理;

【RouteValueDictionary】

RouteValueDirctionary對(duì)象是在路由對(duì)象內(nèi)部存放數(shù)據(jù)用的,比如:我們?cè)谂渲寐酚傻臅r(shí)候,可以指定一些默認(rèn)值、命名空間等等;

看RouteValueDictionary源碼定義:

public class RouteValueDictionary : IDictionary<string, object>

該類型繼承自字典接口IDictionary<string,object>,繼承自字典接口而不是繼承自字典基類目的只是想使用字典的行為而不是它的默認(rèn)實(shí)現(xiàn);在RouteValueDictionary內(nèi)部使用了一個(gè)Dictionary<string,object>類型作為最終容器;

Dictionary<string,object> d = new Dictionary<string,object> (CaseInsensitiveStringComparer.Instance);

在構(gòu)造函數(shù)中使用了一個(gè)內(nèi)部類CaseInsensitiveStringComparer進(jìn)行Key的相等比較:

internal class CaseInsensitiveStringComparer : IEqualityComparer<string>
        {
            public static readonly CaseInsensitiveStringComparer Instance = new CaseInsensitiveStringComparer ();
            public int GetHashCode (string obj)
            {
                return obj.ToLower (CultureInfo.InvariantCulture).GetHashCode ();
            }
            public bool Equals (string obj1, string obj2)
            {
                return String.Equals (obj1, obj2, StringComparison.OrdinalIgnoreCase);
            }
        }

IEqualityComparer接口還是很不錯(cuò)的,不過現(xiàn)在基本上不這么用了,而是直接提供了一個(gè)Lambda做為比較函數(shù);

【RouteData】

路由數(shù)據(jù)對(duì)象,它的大概意思我想大家應(yīng)該知道了,上面提到過很多次,這里就不介紹了;我們直接看一下RouteData內(nèi)部核心代碼段:

public RouteData (RouteBase route, IRouteHandler routeHandler)
{
    // arguments can be null.
    Route = route;
    RouteHandler = routeHandler;
    DataTokens = new RouteValueDictionary ();
    Values = new RouteValueDictionary ();
}
public RouteValueDictionary DataTokens { get; private set; }
public RouteBase Route { get; set; }
public IRouteHandler RouteHandler { get; set; }
public RouteValueDictionary Values { get; private set; }

通過構(gòu)造函數(shù)我們能了解到,保存了對(duì)Route對(duì)象的引用和IRouteHander接口的引用,為什么將IRouteHandler作為構(gòu)造函數(shù)參數(shù),那是因?yàn)镽outeBase根本沒有對(duì)IRouteHander接口的屬性定義;IRouteHandler接口在不在RouteBase或Route中不重要,因?yàn)镽oute可以是自定義的,這里的強(qiáng)制性是在RouteData中,它的構(gòu)造函數(shù)必須接受IRouteHandler類型接口;

我們接著看,在構(gòu)造函數(shù)的下面兩行代碼中分別是實(shí)例化了DataTokens、Values兩個(gè)屬性,而類型是RouteValueDictionary,這也剛好和我們上面分析的對(duì)上了;RouteValueDictionary是內(nèi)部用來保存這些零散鍵值對(duì)數(shù)據(jù)容器,在Route、RouteData還有其他地方均需要用到;就是因?yàn)镽outeValueDictionary的Value是Object類型,所以可以用來存放任何類型的值,比較通用;

【RequestContext 】

RequestContext在上面也已經(jīng)接觸很多次了,表示請(qǐng)求上下文,也就是跟當(dāng)請(qǐng)求相關(guān)的所有數(shù)據(jù)都封裝在里面;在后面的文章中,我們將接觸很多類似Context的對(duì)象,如:ControlContext,ViewContext之類的,都是用來控制上下文的邊界,而不是直接傳遞零散的參數(shù);

4.4】IRouteHandler 、IHttpHander兩個(gè)接口之間的關(guān)系

IRouteHandler接口是路由框架起作用的核心,只有提供了IRouteHandler實(shí)現(xiàn)才能順利的得到背后的IHttpHandler接口;ASP.NETMVC提供了MvcRouteHandler對(duì)象來實(shí)現(xiàn)IRouteHandler接口,MvcRouteHandler在內(nèi)部實(shí)例化實(shí)現(xiàn)了IHttpHandler接口的MvcHandler對(duì)象;MvcHandler然后通過RequestContext對(duì)象獲取RouteData對(duì)象,接著得到相應(yīng)的Control信息,進(jìn)行后續(xù)的執(zhí)行處理;

5.】UrlRoutingHandler 對(duì)象內(nèi)部結(jié)構(gòu)及擴(kuò)展應(yīng)用

在ASP.NETRouting路由框架中有一個(gè)很重要的IHttpHandler接口對(duì)象UrlRoutingHanlder,我想你肯定很疑惑,為什么需要這樣一個(gè)對(duì)象;其實(shí)它的存在是為了提供給我們繞過UrlRoutingModule模塊的機(jī)會(huì);根據(jù)上面的詳細(xì)的分析,我們知道路由的入口在UrlRoutingModule,所有的路由相關(guān)的映射工作都在該類中完成,但是有時(shí)候我們很想繞過UrlRoutingModule進(jìn)行簡(jiǎn)單的處理或者性能方面的優(yōu)化考慮,這就派上用場(chǎng)了;我能想到的使用場(chǎng)景目前來看是對(duì)ASP.NET第版本的項(xiàng)目做Url重寫是比較方便,首先我們的項(xiàng)目需要建立在低版本的ASP.NET之上,但是需要添加Url.ReWriter的功能,就需要我們自己去實(shí)現(xiàn)這樣的功能;

但是工作量和性能都很難控制好,如果使用這里提供的UrlRoutingHandler進(jìn)行實(shí)現(xiàn)就很方便了,UrlRoutingHandler給我們使用ASP.NETRouting框架的機(jī)會(huì)同時(shí)也不需要關(guān)心是否配置了UrlRoutingModule;

public abstract class UrlRoutingHandler : IHttpHandler

根據(jù)代碼看出它是一個(gè)抽象類,直接實(shí)現(xiàn)IHttpHanlder接口,但是重要的是Proce***equest方法;

protected virtual void Proce***equest (HttpContextBase httpContext)
        {
            if (httpContext == null)
                throw new ArgumentNullException ("httpContext");
            var rd = RouteCollection.GetRouteData (httpContext);
            if (rd == null)
                throw new HttpException ("The incoming request does not match any route");
            if (rd.RouteHandler == null)
                throw new InvalidOperationException ("No  IRouteHandler is assigned to the selected route");
            RequestContext rc = new RequestContext (httpContext, rd);
            var hh = rd.RouteHandler.GetHttpHandler (rc);
            VerifyAndProce***equest (hh, httpContext);
        }
        protected abstract void VerifyAndProce***equest (IHttpHandler httpHandler, HttpContextBase httpContext);

該方法的邏輯跟UrlRoutingModule里的PostResolveRequestCache方法是差不多的,都會(huì)通過全局RouteCollection集合進(jìn)行匹配當(dāng)前的RouteData對(duì)象;那就足夠說明這個(gè)過程不會(huì)再通過UrlRoutingModule模塊;方法的最后一行是執(zhí)行一個(gè)模板方法:VerifyAndProce***equest ,該方法是留給子類去實(shí)現(xiàn)的;

那么這里將路由和執(zhí)行合在一起了,基類負(fù)責(zé)路由子類負(fù)責(zé)執(zhí)行,很不錯(cuò)的設(shè)計(jì)方法;

總結(jié):這篇文章基本上介紹了跟路由相關(guān)的核心對(duì)象,但是還有一些其他輔助的類這里并沒有進(jìn)行講解,當(dāng)然如果你有興趣可以自己去看看;這篇文章是為了讓我們能對(duì)路由的處理流程及結(jié)構(gòu)有個(gè)了解,做到能在適當(dāng)?shù)臅r(shí)候進(jìn)行擴(kuò)展和查找問題;


作者:王清培

出處:http://wangqingpei557.blog.51cto.com/

本文版權(quán)歸作者和51CTO共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI