ASP.NET路由模型解析

發表於2016-03-05

大話ASP.NET模型

首先我們先來了解下一個請求的悲歡離合的命運,看看它的一生中所走過的蜿蜒曲折的道路。如下圖所示:

(在這裡感謝馬倫老師唾沫橫飛的講解,終於讓我理解它的一生。)

ASPNET執行時模型圖解

在如上所示的風光旖旎的畫卷中,我們可以看到一個“請求”從客戶端瀏覽器出發,經歷千山萬水到達伺服器,伺服器的核心模組的HTTP.SYS熱情款待了它,對它進行簡單的修飾之後,就和它依依惜別了,因為HTTP.SYS知道它是一個有夢想的“請求”,它應該去它該去的地方,於是就把它送到了IIS。

IIS是片神奇的土地,這裡有一位偉大的神靈叫做inetinfo.exe,於是它便去神靈的居所W3SVC服務(windows服務)祈禱,希望能給他一些指示,神靈通過查閱天書(IIS的配置檔案),知道了它不是一般的靜態檔案,不能把它直接送回去,應該讓它去它的族人開辦的加工廠(即對應網站的工作程式中)好好修習一番。

現任加工廠老大叫w3wp.exe,在IIS6以前是aspnet_wp.exe,其因為沒有管理好各個加工廠之間的地盤問題被罷免了(asp.net_wp.exe用一個程式寄宿所有的網站,用應用程式域進行分割的,結果導致網站之間相互影響),現任老大w3wp.exe通過一個網站一個程式的方式把問題解決了,因此順利上位。

初入加工廠的“請求”拜訪了門衛asp.net_isapi.dll,門衛發現它是第一個過來的“請求”,於是為它開啟了工廠的生產車間(即第一個請求到達時,啟動了asp.net執行的環境,後來的請求就可以直接進入這個環境裡。),並請車間主任ISAPIRuntime來負責它,主任興高采烈的來歡迎它(即ISAPIRuntime呼叫ProcessRequest(簡稱PR)方法,訪問當前請求所在的ecb控制程式碼),並讓土裡土氣的它換上了統一服裝HttpWorkRequest(即把請求進行簡單的封裝),然後叫來班長HttpRuntime,讓班長安排它的工作。

班長說:”車間裡面有危險,你先穿上安全制服HttpContext。”(即通過PR方法把HttpWorkRequest封裝成HttpContext),然後去組長宿舍(HttpApplicationFactory)準備叫一個組長(HttpApplication)來帶領它,結果發現還沒有組長,班長只好去招聘一個新組長。

每一個組長都是經過嚴格訓練才能上崗的,先要熟讀入廠準則Global.asax(即先編譯Global.asax檔案),再通過準則中Application_Start方法考驗(即呼叫Application_Start方法),如此這般方成為一代組長。每位新任組長第一件事就是把所有的車間模組裝配好,並建立好車間管道(通過讀取配置檔案,載入所有的IHttpModule,並呼叫他們的Init方法,一般init方法都是註冊管道事件,之後通過BuidStepManager方法,根據經典模式或者整合模式生成對應的StepManager)。

新任組長見到“請求”,二話不說直接啟動車間管道,將其丟進去。穿著安全制服HttpContext的“請求”要依次通過管道中所有的關卡(asp.net管道模型),其中在第7個關卡之後,生成了IHttpHandler型別的物件,並在第11個關卡之後執行該物件的ProcessRequest方法處理請求,在這裡“請求”得到完美的加工塑造,生成了HttpResponse,再通過剩下的管道,實現了夢想的請求就沿著原路返回了。上圖中第11、12個事件之間描述的是WebForm的Page物件處理請求的流程(即頁面生命週期)。

至此,一個請求的跌宕起伏的人生就說完了,各位觀眾欲知路由模組具體怎麼發揮作用的,還請先捧個人場,右下角點個贊

路由模型解析

通過上文我們知道組長HttpApplication物件會負責組裝所有的IHttpModule,它是如何載入的呢?我們觀察反編譯的程式碼:

RuntimeConfig.GetAppConfig().HttpModules.CreateModules();通過這行程式碼,我們可以清楚的發現它讀取了執行時的配置檔案,那麼我們開啟執行時的配置檔案以觀究竟。

asp.net mvc

果然在這裡add了一個System.WebRouting.UrlRoutingModule型別。接下來我們再用反編譯工具看這個型別的原始碼:

asp.net mvc

如我們所料UrlRoutingModule實現了IHttpModule介面,我們看看它的Init方法幹了些什麼?

對第7個事件PostResolveRequestCache註冊方法OnApplicationPostResolveRequestCache,那麼這個方法又是幹啥的呢?

程式碼已經加了註釋,3步走:匹配路由→獲取處理當前請求的IHttpHandler物件→對映:用當前IHttpHandler物件處理請求。之後會在第11、12個事件之間呼叫IHttpHandler物件的PR方法處理當前請求。

我們再整理下思路:ASP.NET先註冊了UrlRoutingModule模組,他就是一個實現了IHttpModule介面的類,其Init方法就是在第7個事件上註冊一個方法,該方法先匹配路由,如果匹配成功了,則用匹配結果RouteData中的IHttpHandler物件對映到當前上下文中,這樣在之後第11、12個事件之間就會呼叫這個IHttpHandler物件處理請求。

那麼問題來了,Route物件是什麼時候注入進去的,IHttpHandler物件又是誰?

還記得路由規則是怎麼新增的嗎?如下面程式碼所示:

這是我們經常用的兩種方式新增路由規則,方式一中有我們自己編寫的MyRouteHandler型別的例項作為引數,其實就是通過IRouteHandler介面返回一個IHttpHandler物件。

其實這兩種方式沒有本質上的區別,因為方式二中路由規則引數都會例項化一個Route物件的。

我們分析方式二的原始碼:

發現所有的路由規則引數都用來例項化一個Route物件了,其中引數physicalFile和checkPhysicalUrlAccess用來例項化PageRouteHandler物件了,其原始碼如下:

這是一個實現了IRouteHandler介面的型別,而這個介面只有一個作用就是返回IHttpHandler物件,原始碼如下:

到這裡我們的疑問就解開了,原來我們註冊的路由規則都例項化成了Route物件,Route的GetRouteData方法用來匹配路由(參考部落格園大神Artech的書籍),路由規則中的physicalFile和checkPhysicalUrlAccess用來例項化一個IHttpHandler例項,用來處理請求。

總結:ASP.NET的路由模型如下圖所示688499-20160227124947458-241974434

原始碼下載:猛戳此處

 

相關文章