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

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

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


我們在《伺服器在管道中的“龍頭”地位》中對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)); }
	   

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

-Advertisement-
Play Games
更多相關文章
  • 本節內容: 顯示信息 確認 Message API給用戶顯示一個信息,或從用戶那裡獲取一個確認信息。 Message API預設使用sweetalert實現,為使sweetalert正常工作,你應該包含它的css和javascript文件,然後把abp.sweet-alert.js適配器包含到你的頁 ...
  • 首先創建一個C# 控制台應用程式, 直接伺服器端代碼丟進去,然後再到Unity 裡面建立一個工程,把客戶端代碼掛到相機上,運行服務端,再運行客戶端。 高手勿噴!~! 完全源碼已經奉上,大家開始研究吧!! 嘎嘎嘎! 服務端代碼:Program.cs using System; using System ...
  • 網路爬蟲在信息檢索與處理中有很大的作用,是收集網路信息的重要工具。 接下來就介紹一下爬蟲的簡單實現。 爬蟲的工作流程如下 爬蟲自指定的URL地址開始下載網路資源,直到該地址和所有子地址的指定資源都下載完畢為止。 下麵開始逐步分析爬蟲的實現。 1. 待下載集合與已下載集合 為了保存需要下載的URL,同 ...
  • 介紹C#如何對ini文件進行讀寫操作,C#可以通過調用【kernel32.dll】文件中的 WritePrivateProfileString()和GetPrivateProfileString()函數分別對ini文件進行讀和寫操作。包括:讀取key的值、保存key的值、讀取所有section、讀取... ...
  • 剛開始接觸Nop的盆友們,肯定會遇到很多問題,在這,介紹下會經常遇到的問題。 一:後臺無法打開 原因:只生成了web,要把整個項目都生成下。 ~資料還在整理中~ ...
  • Nop里自帶的只有國家、省份,在國內是不夠的,得加上城市、地區。 本來想把代碼放上來的,可是,得改一天,新增和修改的代碼量可想而知了,只能說,國家、省份有了,參考來做城市、地區,也不是很難,不過很是繁瑣。 來點福利壓壓驚,NopAddressData.xml ...
  • using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Net;using System.Text;namespace HttpDemo{ class Program { ...
  • 問題 通過 CSRF(Cross-Site Request Forgery)防護,保護從 MVC 頁面提交到ASP.NET Web API 的數據。 解決方案 ASP.NET 已經加入了 CSRF 防護功能,只要通過 System.web.Helpers.AntiForgery 類(System.W ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...