1. 程式人生 > >學習ASP.NET Core, 怎能不瞭解請求處理管道[6]: 管道是如何隨著WebHost的開啟被構建出來的?

學習ASP.NET Core, 怎能不瞭解請求處理管道[6]: 管道是如何隨著WebHost的開啟被構建出來的?

註冊的伺服器和中介軟體共同構成了ASP.NET Core用於處理請求的管道, 這樣一個管道是在我們啟動作為應用宿主的WebHost時構建出來的。要深刻了解這個管道是如何被構建出來的,我們就必須對WebHost和它的建立者WebHostBuilder這個重要的物件具有深刻的理解。[本文已經同步到《ASP.NET Core框架揭祕》之中]

目錄
一、WebHost
    WebHostOptions
    構建管道的三個步驟
二、WebHostBuilder
    WebHost的建立
    幾個常用的擴充套件方法

一、WebHost

顧名思義,WebHost被作為Web應用的宿主,應用的啟動和關閉都是通過啟動或者關閉對應WebHost的方式來實現的。這裡所說的WebHost是對所有實現了IWebHost介面的所有型別及其對應物件的統稱。IWebHost介面具有如下三個基本成員,其中Start方法用於啟動宿主程式。我們程式設計中通常會呼叫它的一個擴充套件方法Run

來啟動WebHost,實際上背後呼叫的其實還是這個Start方法。當WebHost啟動之後,註冊的伺服器變開始了針對請求的監聽,所以WebHost需要具有與伺服器相關的一些特性,這些特性就儲存在通過屬性ServerFeatures返回的特性集合中。

   1: public interface IWebHost : IDisposable
   2: {    
   3:     void Start();
   4:     IFeatureCollection     ServerFeatures { get; }
   5:     IServiceProvider       Services { get; }
   6: }


我們多次提到ASP.NET Core管道在構建和進行請求處理過程中廣泛使用到了依賴注入。依賴注入只要體現在:ASP.NET Core框架以及應用程式會根據需要註冊一系列的服務,這些服務會在WebHost啟動的時候被用來建立一個ServiceProvider物件,管道在進行請求處理過程所需的任何服務物件都可以從這個ServiceProvider物件中獲取。IWebHost介面的Services屬性返回的就是這麼一個ServiceProvider物件。

具有如下定義的WebHost類是對IWebHost介面的預設實現,我們預設使用的WebHost就是這麼一個物件。一般來說,WebHost是通過對應的WebHostBuilder建立的,當後者通過呼叫建構函式建立一個WebHost物件的時候,需要提供四個引數,它們分別是直接註冊到WebHostBuilder上面的服務(appServices)和由此建立的ServiceProvider(hostingServiceProvider),針對WebHost的選項設定(options)和配置(config)。

   1: public class WebHost : IWebHost
   2: {
   3:     public IFeatureCollection     ServerFeatures { get; }
   4:     public IServiceProvider       Services { get; }
   5:  
   6:     public WebHost(
   7:         IServiceCollection     appServices,
   8:         IServiceProvider       hostingServiceProvider,
   9:         WebHostOptions         options,
  10:         IConfiguration         config);
  11:  
  12:     public void Dispose();
  13:     public void Start();
  14: }

WebHostOptions

顧名思義,一個WebHostOptions物件為構建的WebHost物件提供一些預定義的選項設定。這些選項設定很重要,它們決定由WebHost構建的管道進行內容載入以及異常處理等方面的行為。至於它具體攜帶著哪些選項設定,我們只需要看看這個型別具有怎樣的屬性成員。

   1: public class WebHostOptions
   2: {
   3:     public string     ApplicationName { get; set; }
   4:     public bool       DetailedErrors { get; set; }
   5:     public bool       CaptureStartupErrors { get; set; }
   6:     public string     Environment { get; set; }        
   7:     public string     StartupAssembly { get; set; }
   8:     public string     WebRoot { get; set; }
   9:     public string     ContentRootPath { get; set; }
  10:  
  11:     public WebHostOptions()
  12:     public WebHostOptions(IConfiguration configuration) 
  13: }

如下面的程式碼片段所示,WebHostOptions具有七個屬性成員。這些屬性都是可讀可寫的,我們可以呼叫預設無參建構函式建立一個空的WebHostOptions物件,通過手工為這些屬性賦值的方式來設定對應的選項。除此之外,我們可以將這些選項設定定義在配置中,並利用對應的Configuration物件來建立一個WebHostOptions物件。 

構建管道的三個步驟

一般我們開啟了作為應用宿主的WebHost,由註冊的伺服器和中介軟體構成的整個管道被構建起來,伺服器開始繫結到基地址進行請求的監聽。接下來我們就來著重聊聊WebHost在開啟過程中都做了些什麼。總的來說,WebHost的整個開啟過程大體上可以分為如下三個步驟:

  • 註冊服務:獲取Startup物件並利用它完成服務的註冊。
  • 中介軟體註冊:利用獲取的Startup物件完成中介軟體的註冊。
  • 設定並開啟伺服器:獲取註冊到WebHostBuilder上的伺服器併為之設定監聽地址,最後啟動伺服器。

接下來我們按照這個步驟定義一個同名的型別來模式真實WebHost的實現邏輯。如下面的程式碼片段所示,這個模擬的WebHost和真正的WebHost的建構函式具有完全一致的引數列表,我們定義了對應的欄位來儲存這些引數值。除此之外,我們會建立一個ApplicationLifetime物件並將其註冊到提供個ServiceCollection,在WebHost開啟和關閉之後我們會利用它傳送相應的通知。

   1: public class WebHost : IWebHost
   2: {
   3:     private IServiceCollection   _appServices;
   4:     private IServiceProvider     _hostingServiceProvider;
   5:     private WebHostOptions       _options;
   6:     private IConfiguration       _config;
   7:     private ApplicationLifetime  _applicationLifetime;
   8:  
   9:     public WebHost(IServiceCollection appServices, IServiceProvider hostingServiceProvider, WebHostOptions options, IConfiguration config)
  10:     {
  11:         _appServices                 = appServices;
  12:         _hostingServiceProvider      = hostingServiceProvider;
  13:         _options                     = options;
  14:         _config                      = config;
  15:         _applicationLifetime         = new ApplicationLifetime();
  16:         appServices.AddSingleton<IApplicationLifetime>(_applicationLifetime);
  17:     }
  18:
  19: }
  20:  

我們接下來看WebHost除Start方法之外的其他成員的定義。只讀屬性Services返回一個ServiceProvider物件,我們將在完成所有服務註冊工作之後利用ServiceCollection物件建立這個物件,所以只要實現具有相關的服務註冊,我們就能夠利用它得到對應的服務物件。只讀屬性ServerFeatures返回伺服器的特性集合,而伺服器本身則直接利用上述這個ServiceProvider獲得。當MyWebHost物件因Dispose方法的呼叫而被回收之後,我們會對ServiceProvider實施回收 工作。在實施回收的前後,我們利用ApplicationLifetime傳送相應的訊號。

   1: public class WebHost : IWebHost
   2: {    
   3:     private ApplicationLifetime _applicationLifetime;
   4:     public IServiceProvider Services { get; private set; }
   5:     public IFeatureCollection ServerFeatures
   6:     {
   7:         get { return this.Services.GetRequiredService<IServer>()?.Features; }
   8:     }
   9:     public void Dispose()
  10:     {
  11:         _applicationLifetime.StopApplication();
  12:         (this.Services as IDisposable)?.Dispose();
  13:         _applicationLifetime.NotifyStopped();
  14:     }
  15: }
  16:  


真正開啟WebHost的實現體現在如下所示的程式碼片段中。我們直接利用WebHostBuilder提供ServiceProvider獲取一個Startup物件,並呼叫其ConfigureServices方法完成服務的註冊,作為引數的ServiceCollection物件也是由WebHostBuilder提供的。當所有的服務註冊工作完成之後,我們利用最新的ServiceCollection物件建立一個ServiceProvider物件,並利用此物件對Services屬性進行賦值。在後續管道構建過程,以及管道在處理請求過程中所使用的服務均是從這個ServiceProvider中提取的。

   1: public class WebHost : IWebHost
   2: {
   3:     private IServiceCollection   _appServices;
   4:     private IServiceProvider     _hostingServiceProvider;
   5:     private WebHostOptions       _options;
   6:     private IConfiguration       _config;
   7:     private ApplicationLifetime  _applicationLifetime;
   8:  
   9:     public void Start()
  10:     {
  11:         //註冊服務
  12:         IStartup startup = _hostingServiceProvider.GetRequiredService<IStartup>();
  13:         this.Services = startup.ConfigureServices(_appServices);
  14:            
  15:         //註冊中介軟體
  16:         Action<IApplicationBuilder> configure = startup.Configure;
  17:         configure = this.Services.GetServices<IStartupFilter>().Reverse().Aggregate(configure, (next, current) => current.Configure(next));
  18:         IApplicationBuilder appBuilder = this.Services.GetRequiredService<IApplicationBuilder>();
  19:         configure(appBuilder);
  20:  
  21:         //為伺服器設定監聽地址
  22:         IServer server = this.Services.GetRequiredService<IServer>();
  23:         IServerAddressesFeature addressesFeature = server.Features.Get<IServerAddressesFeature>();
  24:         if (null != addressesFeature && !addressesFeature.Addresses.Any())
  25:         {
  26:             string addresses = _config["urls"] ?? "http://localhost:5000";
  27:             foreach (string address in addresses.Split(';'))
  28:             {
  29:                 addressesFeature.Addresses.Add(address);
  30:             }
  31:         }
  32:  
  33:         //啟動伺服器
  34:         RequestDelegate application = appBuilder.Build();
  35:         ILogger logger = this.Services.GetRequiredService <ILogger<MyWebHost>>();
  36:         DiagnosticSource diagnosticSource = this.Services.GetRequiredService<DiagnosticSource>();
  37:         IHttpContextFactory httpContextFactory = this.Services.GetRequiredService<IHttpContextFactory>();
  38:         server.Start(new HostingApplication(application, logger, diagnosticSource, httpContextFactory));
  39:  
  40:         //對外發送通知

            
           

相關推薦

學習ASP.NET Core, 瞭解請求處理管道[3]: 自定義一個伺服器感受一下管道是如何監聽、接收和響應請求

我們在《伺服器在管道中的“龍頭”地位》中對ASP.NET Core預設提供的具有跨平臺能力的KestrelServer進行了介紹,為了讓讀者朋友們對管道中的伺服器具有更加深刻的認識,接下來我們採用例項演示的形式建立一個自定義的伺服器。這個自定義的伺服器直接利用HttpListener來完成針對請求的監聽、接收

學習ASP.NET Core, 瞭解請求處理管道[6]: 管道是如何隨著WebHost開啟構建出來的?

註冊的伺服器和中介軟體共同構成了ASP.NET Core用於處理請求的管道, 這樣一個管道是在我們啟動作為應用宿主的WebHost時構建出來的。要深刻了解這個管道是如何被構建出來的,我們就必須對WebHost和它的建立者WebHostBuilder這個重要的物件具有深刻的理解。[本文已經同步到《ASP.NET

學習ASP.NET Core,瞭解請求處理管道[2]: 伺服器在管道中的“龍頭”地位

ASP.NET Core管道由註冊的伺服器和一系列中介軟體構成。我們在上一篇中深入剖析了中介軟體,現在我們來了解一下伺服器。伺服器是ASP .NET Core管道的第一個節點,它負責完整請求的監聽和接收,最終對請求的響應同樣也由它完成。[本文已經同步到《ASP.NET Core框架揭祕》之中] 伺服器是我們對

學習ASP.NET Core, 瞭解請求處理管道[5]: 中介軟體註冊可以除了可以使用Startup之外,還可以選擇StartupFilter

中介軟體的註冊除了可以藉助Startup物件(DelegateStartup或者ConventionBasedStartup)來完成之外,也可以利用另一個叫做StartupFilter的物件來實現。所謂的StartupFilter是對所有實現了IStartupFilter介面的型別及其物件的統稱。IStart

學習ASP.NET Core,瞭解請求處理管道[1]: 中介軟體究竟是個什麼東西?

ASP.NET Core管道雖然在結構組成上顯得非常簡單,但是在具體實現上卻涉及到太多的物件,所以我們在 “通過重建Hosting系統理解HTTP請求在ASP.NET Core管道中的處理流程”(上篇、中篇、下篇) 中圍繞著一個經過極度簡化的模擬管道講述了真實管道構建的方式以及處理HTTP請求的流程。在本系列

學習ASP.NET Core, 瞭解請求處理管道[4]: 應用的入口——Startup

一個ASP.NET Core應用被啟動之後就具有了針對請求的處理能力,而這個能力是由管道賦予的,所以應用的啟動同時意味著管道的成功構建。由於管道是由註冊的伺服器和若干中介軟體構成的,所以應用啟動過程中一個核心的工作就是完成中間節的註冊。由於依賴注入在ASP.NET Core應用這得到非常廣泛的應用,框架絕大部

學習asp.net core + Ef+mysql建立連接

rtu clu pps 學習 ati conf work 數據 字符 註意事項:1、使用前用nuget導入Microsoft.EntityFrameworkCore.Tools和MySql.Data.EntityFrameworkCore2、DataContext必須聲明一

學習ASP.NET Core Razor 編程系列五——Asp.Net Core Razor新建模板頁面

post方法 頁面布局 解決 文件 捕獲 create 內容 接下來 添加 學習ASP.NET Core Razor 編程系列目錄 學習ASP.NET Core Razor 編程系列一 學習ASP.NET Core Razor 編程系列二——添加

學習ASP.NET Core Razor 編程系列九——增加查詢功能

方法的參數 將在 代碼 教程 ali 表示 得到 asp.net context 學習ASP.NET Core Razor 編程系列目錄 學習ASP.NET Core Razor 編程系列一 學習ASP.NET Core Razor 編程系列二—&mdash

學習ASP.NET Core Razor 編程系列十一——把新字段更新到數據庫

data 工具 itl tle 16px sed 目錄 mep 分享圖片 學習ASP.NET Core Razor 編程系列目錄 學習ASP.NET Core Razor 編程系列一 學習ASP.NET Core Razor 編程系列二——添加一

學習 ASP.NET Core 2.1:集成測試中使用 WebApplicationFactory

UNC enc sta 測試 修改 構造 creat -a msdn WebApplicationFactory 是 ASP.NET Core 2.1 新特性 MVC functional test infrastructure 中帶來的新東東,它封裝了 TestServe

學習ASP.NET Core Razor 編程系列十六——排序

esc 因此 detail href 替換 瀏覽器中 lec 創建項目 html 學習ASP.NET Core Razor 編程系列目錄 學習ASP.NET Core Razor 編程系列一 學習ASP.NET Core Razor 編程系列二—&mdash

從零開始學習 asp.net core 2.1 web api 後端api基礎框架(七)-新增一個查詢單筆資料的方法

再寫一個查詢單筆資料的方法: [Route("{id}")] public JsonResult GetProduct(int id) { return new JsonResult(ProductService.Curre

從零開始學習 asp.net core 2.1 web api 後端api基礎框架(六)-把獲取資料的程式碼整理成一個服務

建立一個Services目錄, 然後建立一個 ProductService.cs類 我們把獲取資料的程式碼整理成一個ProductService, 然後保證程式執行的時候, 操作的是同一批資料: namespace CoreBackend.Api.Services { public

從零開始學習 asp.net core 2.1 web api 後端api基礎框架(五)-Routing 路由

路由有兩種方式: Convention-based (按約定), attribute-based(基於路由屬性配置的).  其中convention-based (基於約定的) 主要用於MVC (返回View或者Razor Page那種的). Web api 推薦使用attribute

學習ASP.NET Core Razor 程式設計系列十八——併發解決方案

public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page();

從零開始學習 asp.net core 2.1 web api 後端api基礎框架(四)-建立Controller

建立一個Controllers目錄, 然後建立一個“控制器類” ProductController.cs, 它需要繼承Microsoft.AspNetCore.Mvc.Controller 在Controller裡面寫這個Get方法: namespace CoreBack

asp.net core 發布 打包cshtml 文件

folder .html 需要 compile razor cor shtml 9.png 技術分享 原文:asp.net core 發布 不打包cshtml 文件需要在 FolderProfile.pubxml 文件中添加 FolderProfile.pubxml

學習ASP.NET Core(06)-Restful與WebAPI

上一篇我們使用Swagger添加了介面文件,使用Jwt完成了授權,本章我們簡答介紹一下RESTful風格的WebAPI開發過程中涉及到的一些知識點,並完善一下尚未完成的功能 --- .NET下的WebAPI是一種無限接近RESTful風格的框架,RESTful風格它有著自己的一套理論,它的大概意思就是說使

換個角度學習ASP.NET Core中介軟體

## 中介軟體真面目 關於ASP.NET Core中介軟體是啥,簡單一句話描述就是:用來處理HTTP請求和響應的一段邏輯,並且可以決定是否把請求傳遞到`管道`中的下一個中介軟體! 上面只是概念上的一種文字描述,那問題來了,中介軟體在程式中到底是個啥:question: 一切還是從`IApplicatio