天天看點

ASP.NET Core管道深度剖析(4):管道是如何建立起來的?一、ApplicationBuilder 二、StartupLoader 三、WebHost四、WebHostBuilder 五、總結

Core的請求處理管道的構成以及它對請求的處理流程進行了詳細介紹,接下來我們需要了解的是這樣一個管道是如何被建構起來的。這樣一個管道由一個伺服器和一個HttpApplication構成,前者負責監聽請求并将接收的請求傳遞給給HttpAppkication對象處理,後者則将請求處理任務委托給注冊的中間件來完成。中間件的注冊是通過ApplicationBuilder對象來完成的,是以我們先來了解一下這究竟是個怎樣的對象。

目錄 ApplicationBuilder StartupLoader WebHost WeHostBuilder 總結

我們所說的ApplicationBuilder是對所有實作了IApplicationBuilder接口的所有類型及其對象的統稱。注冊到WebHostBuilder上的啟動類型具有一個用于管道定值的Configure方法,它利用作為參數的ApplicationBuilder對象進行中間件的注冊。由于ApplicationBuilder與組成管道的中間件具有直接的關系,是以我們得先來說說中間件在管道中究竟展現為一個怎樣的對象。

中間件在請求處理流程中展現為一個類型為Func<RequestDelegate,RequestDelegate>的委托對象,對于很多剛剛接觸請求處理管道的讀者朋友們來說,可能一開始對此有點難以了解,是以容來略作解釋。我們上面已經提到過RequestDelegate這麼一個委托,它相當于一個Func<HttpContext,

Task>對象,該委托對象表示針對提供的HttpContext所做進行一項處理操作,這項操作代表某個中間件針對請求的處理。那為何我們不直接用一個RequestDelegate對象來表示一個中間件,而将它表示成一個Func<RequestDelegate,RequestDelegate>對象呢?

ASP.NET Core管道深度剖析(4):管道是如何建立起來的?一、ApplicationBuilder 二、StartupLoader 三、WebHost四、WebHostBuilder 五、總結

在大部分應用中,我們會針對具體的請求處理需求注冊多個不同的中間件,這些中間件按照注冊時間的先後順序進行排列進而構成我們所謂的請求處理管道。對于某個中間件來說,在它完成了自身的請求處理任務之後,需要将請求傳遞給下一個中間件作後續的處理。Func<RequestDelegate,RequestDelegate>中作為輸入參數的RequestDelegate對象代表一個委托鍊,展現了後續中間件對請求的處理,目前中間件将自身實作的請求處理任務添加到這個委托鍊中,而傳回RequestDelegate對象代表最新的委托鍊。

以右圖所示的管道為例,如果用一個Func<RequestDelegate,RequestDelegate>來表示中間件B,那麼作為輸入參數的RequestDelegate對象代表的是C對請求的處理操作,而傳回值則代表B和C先後對請求處的處理操作。如果一個Func<RequestDelegate,RequestDelegate>代表第一個從伺服器接收請求的中間件(比如A),那麼執行該委托對象傳回的RequestDelegate實際上展現了整個管道對請求的處理。

在對中間件有了充分的了解之後,我們來看看用于注冊中間件的IApplicationBuilder接口的定義。如下所示的是經過裁剪後的IApplicationBuilder接口的定義,我們隻保留了兩個核心的方法,其中Use方法實作了針對中間件的注冊,另一個Build方法則将所有注冊的中間件轉換成一個RequestDelegate對象。

從程式設計便利性考慮,很多預定義的中間件都具有用于注冊的擴充方法,比如我們調用擴充方法UseStaticFiles來注冊處理靜态檔案請求的中間件。對于我們示範的釋出圖檔的應用來說,它也是通過調用一個具有如下定義的擴充方法UseImages來注冊處理圖檔請求的中間件。

ASP.NET

Core預設使用的是一個類型為ApplicationBuilder的對象來注冊中間件,我們采用如下的代碼片斷來模拟它的實作邏輯。我們采用一個List<Func<RequestDelegate,

RequestDelegate>>對象來存放所有注冊的中間件,并調用Aggregate方法将它轉換成一個RequestDelegate對象。

Core并不會直接建立ApplicationBuilder對象來注冊中間件,而是利用對應的工廠來建立它。建立愛你ApplicationBuilder的工廠通過接口IApplicationBuilderFactory表示,在模拟的管道中我們将這個接口簡化成如下的形式,該接口的預設實作者ApplicationBuilderFactory會直接建立一個ApplicationBuilder類型的對象。

一個伺服器和一組中間件組成了ASP .NET

Core的HTTP請求處理管道,中間件的注冊通過調用ApplicationBuilder的Use方法來完成,而這一切實作在注冊為啟動類型的Configure方法中,我們可以将針對這個方法的調用抽象成一個類型為Action

<IApplicationBuilder>

的委托對象。在管道初始化過程中,WebHost必須擷取并執行這個委托以完成中間件的注冊工作。具體來說這個委托對象的擷取是利用一個名為StatupLoader對象來完成的。

這裡的StartupLoader是對所有實作了IStartupLoader接口的所有類型機器對象的統稱,我們在模拟管道中将這個接口作了如下所示的簡化。IStartupLoader接口具有的唯一方法GetConfigureDelegate根據指定的啟動類型生成一個Action

。對于預設實作該接口的StartupLoader類來說,它的GetConfigureDelegate方法傳回的委托會以反射的方式執行定義在指定啟動類型的Configure方法。簡單起見,我們假設這個Configure方法為執行個體方法,啟動對象可以直接調用預設無參構造函數來建立。

Core的請求處理管道是由作為應用宿主的WebHost對象建立出來的,後者是對所有實作了IWebHost接口的所有類型及其對象的統稱,我們在模拟管道中将這個接口作了如下的簡化,僅僅保留了唯一的方法Start。随着WebHost因Start方法的調用而被開啟,整個管道也随之被建立起來。

通過上面的介紹我們知道請求處理管道可以了解為一個伺服器和一個HttpApplication的組合,當我們建立出一個伺服器并指定一個具體的HttpApplication對象調用其Start方法将其啟動時,這個管道就被建立起來。伺服器的建立是利用ServerFactory來完成的,而預設采用的HttpApplication類型為HostingApplication。

當我們建立一個HostingApplication對象的時候,需要指定一個類型為RequestDelegate的委托對象,後者通過調用ApplicationBuilder的Build方法獲得,代表了所有注冊的中間件針對目前請求的處理。是以HostingApplication的建立需要一個ApplicationBuilder對象,這個對象通過ApplicationBuilderFactory來建立。在調用ApplicationBuilder的Build方法将注冊的中間件轉換成RequestDelegate委托之前,需要完成針對中間件的注冊工作。實作在啟動類型的Configure方法中針對中間件的注冊可以展現為一個Action

<IApplicationBuilder>對象,這對委托對象可以通過StartupLoader來擷取。

綜上所述,為了建立并啟動一個伺服器,WebHost至少需要一個ServerFactory和ApplicationBuilderFactory來建立伺服器和ApplicationBuilder,還需要一個StartupLoader來最終完成對中間件的注冊。除此之外,還需要知道注冊到WebHostBuilder上的啟動類型。由于依賴注入被廣泛應用到了ASP.NET

Core的請求處理管道中,對于前面三個對象,會先以服務的形式注冊到DI容器中,那麼WebHost隻需要利用ServiceProvider對象根據對應的服務接口得到這三個對象。

由上面代碼片段提供的這個極簡版的WebHost類通過構造函數的參數提供包含原始服務注冊的ServiceCollection對象和啟動類型,我們利用前者建立對應的ServiceProvider。在Start方法中,我們利用ServiceProvider得到一個ApplicationBuilder對象和一個StartupLoader對象。我們将啟動類型作為參數調用StartupLoader的GetConfigureDelegate方法得到一個Action<IApplicationBuilder>對象。接下來,我們将ApplicationBuilder對象作為參數調用這個Action<IApplicationBuilder>委托對象,後者會執行定義在啟動類型中的Configure方法并最終完整對中間件的注冊。

在這之後,我們利用ServiceProvider得到一個ServiceFactory對象并利用它建立出代碼伺服器的Server對象。為了調用其Start方法,我們需要建立一個HostingApplication對象作為參數,而後者的建立需要一個代表所有中間件針對目前請求進行處理的RequestDelegate對象,這個對象直接通過調用ApplicationBuilder對象的Build方法得到。當伺服器因Start方法的調用而被啟動後,整個請求處理管道被正式建立起來。

作為應用宿主的WebHost建立了ASP.NET

Core的請求處理管道,而WebHost又是由它的工廠WebHostBuilder建立的。WebHostBuilder是對所有實作了IWebHostBuilder接口的所有類型及其對象的統稱,我們在模拟管道中對這個接口做了極大的簡化,僅僅保留了如下面代碼片段所示的三個方法成員。針對WebHost的建立通過Build方法實作,額外兩個方法(UseStartup和UseServer)分别用于注冊啟動類型和用于建立伺服器的ServerFactory。

依賴注入在ASP.NET Core

請求處理管道中得到了極大的應用,建立WebHost提供的ServiceCollection對象最初由WebHostBuilder提供。WebHost在建構管道時使用的一系列服務對象(ApplicationBuilderFactory和StartupLoader)最初都由WebHostBuilder注冊到這個ServiceCollection對象中,這一切都展現如下所示的這個預設使用的WebHostBuilder類型中。

ASP.NET Core管道深度剖析(4):管道是如何建立起來的?一、ApplicationBuilder 二、StartupLoader 三、WebHost四、WebHostBuilder 五、總結

綜上所述,我們已經對ASP.NET

Core應用如何利用WebHostBuilder最終建構出請求處理管道的流程以及管道自身處理請求的流程具有了一定的了解,現在我們來做一個簡單的總結。請求處理管道涉及到四個核心的對象,它們分别是WebHostBuilder、WebHost、Server和HttpApplication,它們之間具有如圖11所示的關系。我們通過WebHostBuilder來建立WebHost,并領用後者來建構請求處理管道。

請求處理管道通過一個Server和一個HttpApplication對象組成,後者是對所有注冊的中間件的封裝。當WebHost被啟動的時候,它會建立Server和HttpApplication對象,并将後者作為參數調用Server的Start方法以啟動伺服器。啟動後的Server開啟監聽請求并利用HttpApplication來處理接收到請求。當HttpApplication完成了所有請求處理工作之後,它會利用Server完成對請求的最終響應。

上面所述的所有内容都是針對我們自定義的模拟管道來介紹的,雖然我們對這個模拟管道做了極大的簡化,但是它依然展現了ASP.NET

Core管道處理請求的真實流程,而且真實管道的建立方式也與模拟管道基本一緻。如果讀者朋友們能夠對這個模拟管道具有深刻的了解,我相信對真實管道的把握就會變得非常容易。

作者:蔣金楠

微信公衆賬号:大内老A

如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識别二維碼)關注個人公衆号(原來公衆帳号蔣金楠的自媒體将會停用)。

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

<a href="http://www.cnblogs.com/artech/p/how-pipeline-is-built.html" target="_blank">原文連結</a>

繼續閱讀