ASP.NET路由模型解析

你們好,我又來吹牛逼了html

~-_-~windows

轉載請註明出處:來自吹牛逼之《ASP.NET路由模型解析》api

背景:不少人知道Asp.Net中路由怎麼用的,殊不知道路由模型內部的運行原理,今天我就給你們吹下ASP.NET的路由模塊是如何工做的。瀏覽器

ps:這是針對ASP.NET4.5版本的,好像在最新的5.0版本中加入了OWIN,完全解耦了和Web服務器的耦合,我尚未研究過,不敢妄言4.5的模型適用5.0。(是否是被我嚴謹的態度震懾了_-_)安全

action*0x1:大話ASP.NET模型服務器

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

(在這裏感謝馬倫老師唾沫橫飛的講解,終於讓我理解它的一輩子。)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,再經過剩下的管道,實現了夢想的請求就沿着原路返回了。上圖中第十一、12個事件之間描述的是WebForm的Page對象處理請求的流程(即頁面生命週期)。

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

action*0x2:路由模型解析

經過上文咱們知道組長HttpApplication對象會負責組裝全部的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();經過這行代碼,咱們能夠清楚的發現它讀取了運行時的配置文件,那麼咱們打開運行時的配置文件以觀究竟。

image

果真在這裏add了一個System.WebRouting.UrlRoutingModule類型。接下來咱們再用反編譯工具看這個類型的源碼:

image

如咱們所料UrlRoutingModule實現了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);//匹配路由,獲得匹配結果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);//獲取處理當前請求的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);//映射:用當前IHttpHandler對象處理請求。
            }
        }
    }
}

代碼已經加了註釋,3步走:匹配路由→獲取處理當前請求的IHttpHandler對象→映射:用當前IHttpHandler對象處理請求。以後會在第十一、12個事件之間調用IHttpHandler對象的PR方法處理當前請求。

咱們再整理下思路:ASP.NET先註冊了UrlRoutingModule模塊,他就是一個實現了IHttpModule接口的類,其Init方法就是在第7個事件上註冊一個方法,該方法先匹配路由,若是匹配成功了,則用匹配結果RouteData中的IHttpHandler對象映射到當前上下文中,這樣在以後第十一、12個事件之間就會調用這個IHttpHandler對象處理請求。

那麼問題來了,Route對象是何時注入進去的,IHttpHandler對象又是誰?

還記得路由規則是怎麼添加的嗎?以下面代碼所示:

public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            var defaults = new RouteValueDictionary();
            defaults.Add("name", "*");
            //方式一:
            //經過RouteTable的靜態對象Routes新增一個Route類型的對象。
            RouteTable.Routes.Add("app", new Route("app/{name}", defaults, new MyRouteHandler()));
            //方式二:
            //經過RouteTable的靜態對象Routes的擴展方法新增一個路由規則。
            RouteTable.Routes.MapPageRoute("default", "app/{name}", "~/WebForm1.aspx", false, defaults);
        }
    }

這是咱們常常用的兩種方式添加路由規則,方式一中有咱們本身編寫的MyRouteHandler類型的實例做爲參數,其實就是經過IRouteHandler接口返回一個IHttpHandler對象。

/// <summary>
    /// 實現了IRouteHandler接口的類型
    /// </summary>
    internal class MyRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            //返回一個Page對象,用於處理請求。
            return new WebForm1();
        }
    }

其實這兩種方式沒有本質上的區別,由於方式二中路由規則參數都會實例化一個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;
}

發現全部的路由規則參數都用來實例化一個Route對象了,其中參數physicalFile和checkPhysicalUrlAccess用來實例化PageRouteHandler對象了,其源碼以下:

public class PageRouteHandler : IRouteHandler
{
}

這是一個實現了IRouteHandler接口的類型,而這個接口只有一個做用就是返回IHttpHandler對象,源碼以下:

[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
public interface IRouteHandler
{
    // Methods
    IHttpHandler GetHttpHandler(RequestContext requestContext);
}

到這裏咱們的疑問就解開了,原來咱們註冊的路由規則都實例化成了Route對象,Route的GetRouteData方法用來匹配路由(參考博客園大神Artech的書籍),路由規則中的physicalFile和checkPhysicalUrlAccess用來實例化一個IHttpHandler實例,用來處理請求。

總結:ASP.NET的路由模型以下圖所示

ASP.ENT路由模型圖解

 

ka

完美,收工。

源碼下載:猛戳此處

轉載請註明出處:來自吹牛逼之《ASP.NET路由模型解析》

相關文章
相關標籤/搜索