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

来源:http://www.cnblogs.com/artech/archive/2016/11/23/asp-net-core-real-pipeline-06.html
-Advertisement-
Play Games

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


註冊的伺服器和中間件共同構成了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));
	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • make,make install都是典型的使用GNU的AUTOCONF和AUTOMAKE產生的程式的安裝步驟。 make是用來編譯的,在命令行輸入make命令之後,系統會在當前目錄下搜索Makefile或者makefile文件,它從Makefile中讀取指令,然後編譯。make install是用 ...
  • linux shell可以識別4種不同類型的引字元號: 單引號字元' 雙引號字元" 反斜杠字元\ 反引號字元` 1. 單引號 ( '' )# grep Susan phonebook Susan Goldberg 403-212-4921 Susan Topple 212-234-2343 如果我們 ...
  • 在Ubuntu中第一次使用VIM編輯器發現好強大,打算在Win7中安裝,其中遇到一些小問題,下邊介紹詳細的安裝過程和遇到的問題。 1-安裝 首先發現Github中有一款中意的作者,並且他開源的基本插件都集合了。 地址:https://github.com/DemonCloud/Aix-Vim(下載地 ...
  • web.config <configuration> <connectionStrings> <add name="constr" connectionString="server=.\sqlexpress;database=db2016;uid=sa;pwd=123;" /> <add name= ...
  • Public Class Profile Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As Stri ...
  • 現在流行的系統一般都採用依賴註入的實現方式,利用DI容器來直接獲取所用到的類/介面的實例。.net core也一樣採用DI的方式,提供了DI容器的介面IServiceCollection,並提供了基於該介面的預設實現ServiceCollection。 這樣我們就可以不再像以前一樣,需要引入第三方的 ...
  • 1.界面 <UserControl x:Class="HKDCMS.Client.Demo.UIViews.UIControls.AboutUsControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml ...
  • In Windows 8 & 10, you have to right-click devenv.exe and select "Troubleshoot compatibility". 轉自 http://stackoverflow.com/questions/12257110/can-you- ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...