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

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

我們在《伺服器在管道中的“龍頭”地位》中對ASP.NET Core預設提供的具有跨平臺能力的KestrelServer進行了介紹,為了讓讀者朋友們對管道中的伺服器具有更加深刻的認識,接下來我們採用例項演示的形式建立一個自定義的伺服器。這個自定義的伺服器直接利用HttpListener來完成針對請求的監聽、接收和響應,我們將其命名為HttpListenerServer。在正式介紹HttpListenerServer的設計和實現之前,我們先來顯示一下如何將它應用到 一個具體的Web應用中。我們依然採用最簡單的Hello World應用來演示針對HttpListenerServer的應用,所以我們在Startup類的Configure方法中編寫如下的程式直接響應一個“Hello World”字串。[本文已經同步到《

ASP.NET Core框架揭祕》之中]

   1: public class Startup
   2: {
   3:     public void Configure(IApplicationBuilder app)
   4:     {
   5:         app.Run(async context => await context.Response.WriteAsync("Hello World!"));
   6:     }
   7: }

在作為程式入口的Main方法中,我們直接建立一個WebHostBuilder物件並呼叫擴充套件方法UseHttpListener完成針對自定義HttpListenerServer的註冊。我們接下來呼叫UseStartup方法註冊上面定義的這個啟動型別,然後呼叫Build方法建立一個WebHost物件,最後呼叫Run方法執行這個作為宿主的WebHost。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         new WebHostBuilder()
   6:             .UseHttpListener()
   7:             .UseStartup<Startup>()
   8:             .Build()
   9:             .Run();
  10:     }
  11: }
  12:  
  13: public static class WebHostBuilderExtensions
  14: {
  15:     public static IWebHostBuilder UseHttpListener(this IWebHostBuilder builder)
  16:     {
  17:         builder.ConfigureServices(services => services.AddSingleton<IServer, HttpListenerServer>());
  18:         return builder;
  19:     }
  20: }

我們自定義的擴充套件方法UseHttpListener的邏輯很簡單,它只是呼叫WebHostBuilder的ConfigureServices方法將我們自定義的HttpListenerServer型別以單例模式註冊到指定的ServiceCollection上而已。我們直接執行這個程式並利用瀏覽器訪問預設的監聽地址(http://localhost:5000),服務端響應的“Hello World”字串會按照如下圖所示的形式顯示在瀏覽器上。

image

接下來我們來介紹一下HttpListenerServer的大體涉及。除了HttpListenerServer這個實現了IServer的自定義Server型別之外,我們只定義了一個名為HttpListenerServerFeature的特性型別,圖7所示的UML基本上體現了HttpListenerServer的總體設計。

6

如果我們利用HttpListener來監聽請求,它會為接收到的每次請求建立一個屬於自己的上下文,具體來說這是一個型別為HttpListenerContext物件。我們可以利用這個HttpListenerContext物件獲取所有與請求相關的資訊,針對請求的任何響應也都是利用它完成的。上面這個HttpListenerServerFeature實際上就是對這個作為原始上下文的HttpListenerContext物件的封裝,或者說它是管道使用的DefaultHttpContext與這個原始上下文之間溝通的中介。

如下所示的程式碼片段展示了HttpListenerServerFeature型別的完整定義。簡單起見,我們並沒有實現上面提到過的所有特性介面,而只是選擇性地實現了IHttpRequestFeature和IHttpResponseFeature這兩個最為核心的特性介面。它的建構函式除了具有一個型別為HttpListenerContext的引數之外,還具有一個字串的引數pathBase用來指定請求URL的基地址(對應IHttpRequestFeature的PathBase屬性),我們利用它來計算請求URL的相對地址(對應IHttpRequestFeature的Path屬性)。IHttpRequestFeature和IHttpResponseFeature中定義的屬性都可以直接利用HttpListenerContext對應的成員來實現,這方面並沒有什麼特別之處。

   1: public class HttpListenerServerFeature : IHttpRequestFeature, IHttpResponseFeature
   2: {
   3:     private readonly HttpListenerContext     httpListenerContext;
   4:     private string                           queryString;
   5:     private IHeaderDictionary                requestHeaders;
   6:     private IHeaderDictionary                responseHeaders;
   7:     private string                           protocol;
   8:     private readonly string                  pathBase;
   9:  
  10:     public HttpListenerServerFeature(HttpListenerContext httpListenerContext, string pathBase)
  11:     {
  12:         this.httpListenerContext     = httpListenerContext;
  13:         this.pathBase                 = pathBase;
  14:     }
  15:  
  16:     #region IHttpRequestFeature
  17:  
  18:     Stream IHttpRequestFeature.Body
  19:     {
  20:         get { return httpListenerContext.Request.InputStream; }
  21:         set { throw new NotImplementedException(); }
  22:     }
  23:  
  24:     IHeaderDictionary IHttpRequestFeature.Headers
  25:     {
  26:         get { return requestHeaders ?? (requestHeaders = GetHttpHeaders(httpListenerContext.Request.Headers)); }
  27:         set { throw new NotImplementedException(); }
  28:     }
  29:  
  30:     string IHttpRequestFeature.Method
  31:     {
  32:         get { return httpListenerContext.Request.HttpMethod; }
  33:         set { throw new NotImplementedException(); }
  34:     }
  35:  
  36:     string IHttpRequestFeature.Path
  37:     {
  38:         get { return httpListenerContext.Request.RawUrl.Substring(pathBase.Length);}
  39:         set { throw new NotImplementedException(); }
  40:     }
  41:  
  42:     string IHttpRequestFeature.PathBase
  43:     {
  44:         get { return pathBase; }
  45:         set { throw new NotImplementedException(); }
  46:     }
  47:  
  48:     string IHttpRequestFeature.Protocol
  49:     {
  50:         get{ return protocol ?? (protocol = this.GetProtocol());}
  51:         set { throw new NotImplementedException(); }
  52:     }
  53:  
  54:     string IHttpRequestFeature.QueryString
  55:     {
  56:         Get { return queryString ?? (queryString = this.ResolveQueryString());}
  57:         set { throw new NotImplementedException(); }
  58:     }
  59:  
  60:     string IHttpRequestFeature.Scheme
  61:     {
  62:         get { return httpListenerContext.Request.IsWebSocketRequest ? "https" : "http"; }
  63:         set { throw new NotImplementedException(); }
  64:     }
  65:     #endregion
  66:  
  67:     #region IHttpResponseFeature
  68:     Stream IHttpResponseFeature.Body
  69:     {
  70:         get { return httpListenerContext.Response.OutputStream; }
  71:         set { throw new NotImplementedException(); }
  72:     }
  73:  
  74:     string IHttpResponseFeature.ReasonPhrase
  75:     {
  76:         get { return httpListenerContext.Response.StatusDescription; }
  77:         set { httpListenerContext.Response.StatusDescription = value; }
  78:     }
  79:  
  80:     bool IHttpResponseFeature.HasStarted
  81:     {
  82:         get { return httpListenerContext.Response.SendChunked; }
  83:     }
  84:  
  85:     IHeaderDictionary IHttpResponseFeature.Headers
  86:     {
  87:         get { return responseHeaders ?? (responseHeaders = GetHttpHeaders(httpListenerContext.Response.Headers)); }
  88:         set { throw new NotImplementedException(); }
  89:     }
            
           

相關推薦

學習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 2.2 : 二十一. 內容協商與定義IActionResult格式化類

上一章的結尾留下了一個問題:同樣是ObjectResult,在執行的時候又是如何被轉換成string和JSON兩種格式的呢? 本章來解答這個問題,這裡涉及到一個名詞:“內容協商”。除了這個,本章將通過兩個例子來介紹如何自定義IActionResult和格式化類。(ASP.NET Co

學習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風格它有著自己的一套理論,它的大概意思就是說使