ps:這是針對ASP.NET4.5版本的,好像在最新的5.0版本中加入了OWIN,徹底解耦了和Web服務(wù)器的耦合,我還沒有研究過,不敢妄言4.5的模型適用5.0。
action*0x1:大話ASP.NET模型
首先我們先來了解下一個請求的悲歡離合的命運,看看它的一生中所走過的蜿蜒曲折的道路。如下圖所示:
在如上所示的風(fēng)光旖旎的畫卷中,我們可以看到一個“請求”從客戶端瀏覽器出發(fā),經(jīng)歷千山萬水到達(dá)服務(wù)器,服務(wù)器的內(nèi)核模塊的HTTP.SYS熱情款待了它,對它進行簡單的修飾之后,就和它依依惜別了,因為HTTP.SYS知道它是一個有夢想的“請求”,它應(yīng)該去它該去的地方,于是就把它送到了IIS。
IIS是片神奇的土地,這里有一位偉大的神靈叫做inetinfo.exe,于是它便去神靈的居所W3SVC服務(wù)(windows服務(wù))祈禱,希望能給他一些指示,神靈通過查閱天書(IIS的配置文件),知道了它不是一般的靜態(tài)文件,不能把它直接送回去,應(yīng)該讓它去它的族人開辦的加工廠(即對應(yīng)網(wǎng)站的工作進程中)好好修習(xí)一番。
現(xiàn)任加工廠老大叫w3wp.exe,在IIS6以前是aspnet_wp.exe,其因為沒有管理好各個加工廠之間的地盤問題被罷免了(asp.net_wp.exe用一個進程寄宿所有的網(wǎng)站,用應(yīng)用程序域進行分割的,結(jié)果導(dǎo)致網(wǎng)站之間相互影響),現(xiàn)任老大w3wp.exe通過一個網(wǎng)站一個進程的方式把問題解決了,因此順利上位。
初入加工廠的“請求”拜訪了門衛(wèi)asp.net_isapi.dll,門衛(wèi)發(fā)現(xiàn)它是第一個過來的“請求”,于是為它打開了工廠的生產(chǎn)車間(即第一個請求到達(dá)時,啟動了asp.net運行的環(huán)境,后來的請求就可以直接進入這個環(huán)境里。),并請車間主任ISAPIRuntime來負(fù)責(zé)它,主任興高采烈的來歡迎它(即ISAPIRuntime調(diào)用ProcessRequest(簡稱PR)方法,訪問當(dāng)前請求所在的ecb句柄),并讓土里土氣的它換上了統(tǒng)一服裝HttpWorkRequest(即把請求進行簡單的封裝),然后叫來班長HttpRuntime,讓班長安排它的工作。
班長說:”車間里面有危險,你先穿上安全制服HttpContext?!保赐ㄟ^PR方法把HttpWorkRequest封裝成HttpContext),然后去組長宿舍(HttpApplicationFactory)準(zhǔn)備叫一個組長(HttpApplication)來帶領(lǐng)它,結(jié)果發(fā)現(xiàn)還沒有組長,班長只好去招聘一個新組長。
每一個組長都是經(jīng)過嚴(yán)格訓(xùn)練才能上崗的,先要熟讀入廠準(zhǔn)則Global.asax(即先編譯Global.asax文件),再通過準(zhǔn)則中Application_Start方法考驗(即調(diào)用Application_Start方法),如此這般方成為一代組長。每位新任組長第一件事就是把所有的車間模塊裝配好,并創(chuàng)建好車間管道(通過讀取配置文件,加載所有的IHttpModule,并調(diào)用他們的Init方法,一般init方法都是注冊管道事件,之后通過BuidStepManager方法,根據(jù)經(jīng)典模式或者集成模式生成對應(yīng)的StepManager)。
新任組長見到“請求”,二話不說直接啟動車間管道,將其丟進去。穿著安全制服HttpContext的“請求”要依次通過管道中所有的關(guān)卡(asp.net管道模型),其中在第7個關(guān)卡之后,生成了IHttpHandler類型的對象,并在第11個關(guān)卡之后執(zhí)行該對象的ProcessRequest方法處理請求,在這里“請求”得到完美的加工塑造,生成了HttpResponse,再通過剩下的管道,實現(xiàn)了夢想的請求就沿著原路返回了。上圖中第11、12個事件之間描述的是WebForm的Page對象處理請求的流程(即頁面生命周期)。
至此,一個請求的跌宕起伏的人生就說完了,各位觀眾欲知路由模塊具體怎么發(fā)揮作用的,還請先捧個人場,右下角點個贊。
action*0x2:路由模型解析
通過上文我們知道組長HttpApplication對象會負(fù)責(zé)組裝所有的IHttpModule,它是如何加載的呢?我們觀察反編譯的代碼:
private void InitModules() { HttpModuleCollection modules = RuntimeConfig.GetAppConfig().HttpModules.CreateModules(); HttpModuleCollection other = this.CreateDynamicModules(); modules.AppendCollection(other); this._moduleCollection = modules; this.InitModulesCommon(); }
RuntimeConfig.GetAppConfig().HttpModules.CreateModules();通過這行代碼,我們可以清楚的發(fā)現(xiàn)它讀取了運行時的配置文件,那么我們打開運行時的配置文件以觀究竟。
果然在這里add了一個System.WebRouting.UrlRoutingModule類型。接下來我們再用反編譯工具看這個類型的源碼:
如我們所料UrlRoutingModule實現(xiàn)了IHttpModule接口,我們看看它的Init方法干了些什么?
protected virtual void Init(HttpApplication application) { if (application.Context.Items[_contextKey] == null) { application.Context.Items[_contextKey] = _contextKey; application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache); } }
對第7個事件PostResolveRequestCache注冊方法OnApplicationPostResolveRequestCache,那么這個方法又是干啥的呢?
public virtual void PostResolveRequestCache(HttpContextBase context) { RouteData routeData = this.RouteCollection.GetRouteData(context);//匹配路由,得到匹配結(jié)果RouteData。 if (routeData != null) { IRouteHandler routeHandler = routeData.RouteHandler; if (routeHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0])); } if (!(routeHandler is StopRoutingHandler)) { RequestContext requestContext = new RequestContext(context, routeData); context.Request.RequestContext = requestContext; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);//獲取處理當(dāng)前請求的IHttpHandler對象。 if (httpHandler == null) { object[] args = new object[] { routeHandler.GetType() }; throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), args)); } if (httpHandler is UrlAuthFailureHandler) { if (!FormsAuthenticationModule.FormsAuthRequired) { throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3")); } UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); } else { context.RemapHandler(httpHandler);//映射:用當(dāng)前IHttpHandler對象處理請求。 } } } }
代碼已經(jīng)加了注釋,3步走:匹配路由→獲取處理當(dāng)前請求的IHttpHandler對象→映射:用當(dāng)前IHttpHandler對象處理請求。之后會在第11、12個事件之間調(diào)用IHttpHandler對象的PR方法處理當(dāng)前請求。
我們再整理下思路:ASP.NET先注冊了UrlRoutingModule模塊,他就是一個實現(xiàn)了IHttpModule接口的類,其Init方法就是在第7個事件上注冊一個方法,該方法先匹配路由,如果匹配成功了,則用匹配結(jié)果RouteData中的IHttpHandler對象映射到當(dāng)前上下文中,這樣在之后第11、12個事件之間就會調(diào)用這個IHttpHandler對象處理請求。
那么問題來了,Route對象是什么時候注入進去的,IHttpHandler對象又是誰?
還記得路由規(guī)則是怎么添加的嗎?如下面代碼所示:
public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { var defaults = new RouteValueDictionary(); defaults.Add("name", "*"); //方式一: //通過RouteTable的靜態(tài)對象Routes新增一個Route類型的對象。 RouteTable.Routes.Add("app", new Route("app/{name}", defaults, new MyRouteHandler())); //方式二: //通過RouteTable的靜態(tài)對象Routes的擴展方法新增一個路由規(guī)則。 RouteTable.Routes.MapPageRoute("default", "app/{name}", "~/WebForm1.aspx", false, defaults); } }
這是我們經(jīng)常用的兩種方式添加路由規(guī)則,方式一中有我們自己編寫的MyRouteHandler類型的實例作為參數(shù),其實就是通過IRouteHandler接口返回一個IHttpHandler對象。
/// <summary> /// 實現(xiàn)了IRouteHandler接口的類型 /// </summary> internal class MyRouteHandler : IRouteHandler { public IHttpHandler GetHttpHandler(RequestContext requestContext) { //返回一個Page對象,用于處理請求。 return new WebForm1(); } }
其實這兩種方式?jīng)]有本質(zhì)上的區(qū)別,因為方式二中路由規(guī)則參數(shù)都會實例化一個Route對象的。
我們分析方式二的源代碼:
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens) { if (routeUrl == null) { throw new ArgumentNullException("routeUrl"); } Route item = new Route(routeUrl, defaults, constraints, dataTokens, new PageRouteHandler(physicalFile, checkPhysicalUrlAccess)); this.Add(routeName, item); return item; }
發(fā)現(xiàn)所有的路由規(guī)則參數(shù)都用來實例化一個Route對象了,其中參數(shù)physicalFile和checkPhysicalUrlAccess用來實例化PageRouteHandler對象了,其源碼如下:
public class PageRouteHandler : IRouteHandler { }
這是一個實現(xiàn)了IRouteHandler接口的類型,而這個接口只有一個作用就是返回IHttpHandler對象,源碼如下:
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] public interface IRouteHandler { // Methods IHttpHandler GetHttpHandler(RequestContext requestContext); }
到這里我們的疑問就解開了,原來我們注冊的路由規(guī)則都實例化成了Route對象,Route的GetRouteData方法用來匹配路由,路由規(guī)則中的physicalFile和checkPhysicalUrlAccess用來實例化一個IHttpHandler實例,用來處理請求。
總結(jié):ASP.NET的路由模型如下圖所示
有關(guān)ASP.NET路由模型工作原理小編就給大家介紹到這里,希望對大家有所幫助!
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com