之所以稱ASP.NET Core是一個Web開發平臺,源于它具有一個極具擴展性的請求處理管道,我們可以通過這個管道的定制來滿足各種場景下的HTTP處理需求。ASP. NET Core應用的很多特性,比如路由、認證、會話、緩存等,也同時定制消息處理管道來實現的。我們甚至可以通過管道定制在ASP.NET Core平臺上創建我們自己的Web框架,實際上MVC和SingalR這兩個重要的Web框架也是采用這樣的方式創建的。
HTTP協議自身的特性決定了任何一個Web應用的工作方式都是監聽、接收并處理HTTP請求,并在最終對請求予以響應,HTTP請求處理是管道式設計典型的應用場景。我們根據HTTP請求的處理流程定制出一個消息處理管道,讓接收到的HTTP請求消息想水一樣流入這個管道,組成這個管道的各個環節一次對它作相應的處理。處理的結果同樣轉變成消息逆向流入這個管道進行處理,并最終轉變成回復給客戶端的HTTP響應。ASP.NET Core的消息處理管道從設計的角度來講是非常簡單的,但是從具體實現的角度則相對難以理解,為了讓讀者朋友們通過本章對此具有深刻的理解,我們從簡單的部分講起。
一、從Hello World說起
為了使讀者朋友們能夠以最直觀的感受認識ASP.NET Core的消息處理管道,我們來創建一個最簡單的Hello World程序。這是一個僅僅由兩個類型構成的控制臺程序,作為程序入口的Main方法定義在Program類中,Startup則作為初始化類型。這個程序被啟動之后將會綁定到默認端口5000進行HTTP請求的監聽,任何針對基地址 “http://localhost:5000/” 的請求后,該程序都將響應 “Hello World” 。
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: new WebHostBuilder()
6: .UseKestrel()
7: .UseStartup<Startup>
8: .Build()
9: .Start();
10: }
11: }
12:
13:
14: public class Startup
15: {
16: public void Configure(IApplicationBuilder app)
17: {
18: app.Run(context=>context.Response.WriteAsync("Hello World");
19: }
20: }
這個程序涉及到一個重要的對象WebHost,它通過WebHostBuilder的Build方法創建。WebHost可以看成是Web應用的宿主,啟動Web應用本質上就是啟動它的宿主。當我們調用WebHost的Start方法啟動應用的時候,用于監聽、接收、處理和響應HTTP請求的消息處理管道隨之被建立。那么在這個過程中,通過調用UseStartup<T>方法注冊到WebHostBuilder上的初始化類型將用來對這個管道進行定制。總的來說,ASP.NET Core的請求處理管道由WebHost在啟動的時候構建,WebHostBuilder則是后者的創建者,右圖揭示了三者之間的關系。
二、管道的構成
HTTP請求處理流程始于對請求的監聽與接收,終于對請求的響應,這兩項工作由同一個對象來完成,我們稱之為 “服務器(Server)” ,盡管ASP.NET Core的請求處理管道可以被自由地訂制,但是該管道必須有一個服務器,服務器是整個管道的 “龍頭” 。在上面的這個Hello World應用中,在調用WebHostBuilder的Build方法創建一個WebHost之前,我們調用了它的一個擴展方法UseKestrel,這個方法的作用就是為后續構建的管道注冊一個名為KestrelServer的服務器。
隨著WebHost的Start方法的調用,按照具體需求進行定制的請求處理管道被構建出來,作為第一個節點的服務器會綁定到一個預設的端口(比如KestrelServer默認采用5000作為監聽端口)開始監聽來自客戶端的HTTP請求。一旦請求抵達,服務器會接收請求并將其標準化后向管道后續的節點進行轉發,我們將管道中位于服務器之后的請求處理節點成為“中間件(Middleware)”。每個中間件都具有各自獨立的功能,比如我們有專門實現路由功能的中間件,由專門實施用戶認證的中間,所謂的對請求處理管道的定制體現在根據具體的需求選擇對應的中間件組成最終處理請求的管道。左圖揭示了由一個服務器和一組中間件構成的請求處理管道。
一個建立在ASP.NET Core之上的應用一般都是根據某個框架開發的,開發框架基本上是建立在某個特殊的中間件上。以ASP.NET Core MVC這個最著名的框架為例,它實際上是利用一個叫做 “路由” 的中間件實現了請求地址與Controller/Action之間的映射,并在此基礎實現了激活Controller、執行Action以及呈現View等一系列的功能。所以應用程序可以視為某個中間件的一部分,如果一定要將它獨立出來,整個請求處理管道將呈現出如右圖所示的結構。
三、管道的定制
在演示的Hello World程序中,我們在調用WebHostBuilder的Build方法創建WebHost之前先調用了它的擴展方法UseStartup<T>方法注冊了一個類型為Startup的啟動類型。從請求處理管道的角度來講,注冊的這個啟動類型的目的在于對構建的管道進行定制,說得更加具體一點,我們利用這個類型為管道注冊需要的中間件。一般來說,被注冊的啟動類型必須具有一個類似于下面代碼片斷所示的Configure方法,這個方法可以是靜態方法和也可以實例方法。這個方法的參數并沒有嚴格的限制,但是第一個參數類型必須是IApplicationBuilder接口。
1: public class Startup
2: {
3: public void Configure(IApplicationBuilder app);
4: }
對中間件的注冊就是實現在這樣一個Configure方法之中。在演示的實例中,我們調用了IApplicationBuilder接口的擴展方法Run注冊了一個中間件,它承載的請求處理邏輯很簡單,即使直接響應一個“Hello World”字符串。在真實的項目中,我們會根據具體的應用場景在這樣一個方法中利用ApplicationBuilder注冊相應的中間件進而構建一個適合當前請求處理需求的管道。
1: public class Startup
2: {
3: public void Configure(IApplicationBuilder app)
4: {
5: app.UseExceptionHandler("/Home/Error");
6: app.UseStaticFiles();
7: app.UseIdentity();
8:
9: app.UseMvc();
10: }
11: }
比如在一個ASP.NET Core MVC應用中我們除了按照如上的方式調用擴展方法UseMvc注冊了支撐MVC框架的中間件(實際上是一個實現路由的中間件)之外,我們還通過調用其它的擴展方法注冊了相應的中間件實現了對靜態文件的訪問(UseStaticFiles)、錯誤頁面的呈現(UseExceptionHandler)以及基于ASP.NET Identity Framework的認證(UseIdentity)。
一、采用管道處理HTTP請求
二、創建一個“迷你版”的管道來模擬真實管道請求處理流程
三、管道如何處理HTTP請求的
四、管道是如何被創建出來的
文章列表