日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

一个迷你ASP.NET Core框架的实现(下)

發(fā)布時間:2023/12/4 asp.net 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一个迷你ASP.NET Core框架的实现(下) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

【框架內幕|?作者?/ Edison Zhou

這是恰童鞋騷年的第196篇原創(chuàng)文章


上一篇我們了解了AspNetCore.Mini這個項目的背景及項目結構和流程,這一篇我們繼續(xù)解析幾個核心對象。本文整理自A大(蔣金楠)的主題分享,點擊本文底部“閱讀原文”可以觀看A大的該主題視頻分享。

1基于HttpListener的WebServer

剛剛在WebHost中注入了Server,并啟動了Server。那么,這個Server長啥樣呢?我們知道,在ASP.NET Core中封裝了Kestrel和IIS兩個Server供我們使用,那么它們肯定有一個抽象層(這里是接口),定義了他們共有的行為,這里我們也寫一個IServer:

public interface IServer {Task RunAsync(RequestDelegate handler); }

IServer接口行為很簡單,就是約定一個啟動的方法RunAsync,接受參數(shù)是中間件(本質就是一個請求處理的委托)。

有了IServer接口,就可以基于IServer封裝基于不同平臺的WebServer了,這里基于HttpListener實現(xiàn)了一個HttpListenerServer如下(HttpListener簡化了Http協(xié)議的監(jiān)聽,僅需通過字符串的方法提供監(jiān)聽的地址和端口號以及虛擬路徑,就可以開始監(jiān)聽請求):

public class HttpListenerServer : IServer {private readonly HttpListener _httpListener;private readonly string[] _urls;public HttpListenerServer(params string[] urls){_httpListener = new HttpListener();// 綁定默認監(jiān)聽地址(默認端口為5000)_urls = urls.Any() ? urls : new string[] { "http://localhost:5000/" };}public async Task RunAsync(RequestDelegate handler){Array.ForEach(_urls, url => _httpListener.Prefixes.Add(url));if (!_httpListener.IsListening){// 啟動HttpListener_httpListener.Start();}Console.WriteLine("[Info]: Server started and is listening on: {0}", string.Join(";", _urls));while (true){// 等待傳入的請求,該方法將阻塞進程(這里使用了await),直到收到請求var listenerContext = await _httpListener.GetContextAsync();// 打印狀態(tài)行: 請求方法, URL, 協(xié)議版本Console.WriteLine("{0} {1} HTTP/{2}", listenerContext.Request.HttpMethod, listenerContext.Request.RawUrl, listenerContext.Request.ProtocolVersion);// 獲取抽象封裝后的HttpListenerFeaturevar feature = new HttpListenerFeature(listenerContext);// 獲取封裝后的Feature集合var features = new FeatureCollection().Set<IHttpRequestFeature>(feature).Set<IHttpResponseFeature>(feature);// 創(chuàng)建HttpContextvar httpContext = new HttpContext(features);Console.WriteLine("[Info]: Server process one HTTP request start.");// 開始依次執(zhí)行中間件await handler(httpContext);Console.WriteLine("[Info]: Server process one HTTP request end.");// 關閉響應listenerContext.Response.Close();}} }/// <summary> /// IWebHostBuilder擴展:使用基于HttpListener的Server /// </summary> public static partial class Extensions {public static IWebHostBuilder UseHttpListener(this IWebHostBuilder builder, params string[] urls)=> builder.UseServer(new HttpListenerServer(urls)); }

有了Server,也有了中間件,我們要進行處理的上下文在哪里?熟悉ASP.NET請求處理的童鞋都知道,我們會操作一個叫做HttpContext的東西,它包裹了一個HttpRequest和一個HttpResponse,我們要進行的處理操作就是拿到HttpRequest里面的各種參數(shù)進行處理,然后將返回的結果包裹或調用HttpResponse的某些方法進行響應返回。在ASP.NET Core Mini中,也不例外,我們會創(chuàng)建一個HttpContext,然后將這個HttpContext傳遞給注冊的中間件,各個中間件也可以拿到這個HttpContext去做具體的處理了。但是,不同的Server和單一的HttpContext之間需要如何適配呢?因為我們可以注冊多樣的Server,可以是IIS也可以是Kestrel還可以是這里的HttpListenerServer。

這時候,我們又可以提取一個抽象層了,如上圖所示,底層是具體的基于不同平臺技術的Server,上層是HttpContext共享上下文,中間層是一個抽象層,它是基于不同Server抽象出來的接口,本質是不同Server的適配器,下面就是這個IFeature的定義:

public interface IHttpRequestFeature {Uri Url { get; }NameValueCollection Headers { get; }Stream Body { get; } }public interface IHttpResponseFeature {int StatusCode { get; set; }NameValueCollection Headers { get; }Stream Body { get; } }

這里不再解釋,下面來看看HttpListener的適配的實現(xiàn):

public class HttpListenerFeature : IHttpRequestFeature, IHttpResponseFeature {private readonly HttpListenerContext _context;public HttpListenerFeature(HttpListenerContext context) => _context = context;Uri IHttpRequestFeature.Url => _context.Request.Url;NameValueCollection IHttpRequestFeature.Headers => _context.Request.Headers;NameValueCollection IHttpResponseFeature.Headers => _context.Response.Headers;Stream IHttpRequestFeature.Body => _context.Request.InputStream;Stream IHttpResponseFeature.Body => _context.Response.OutputStream;int IHttpResponseFeature.StatusCode{get { return _context.Response.StatusCode; }set { _context.Response.StatusCode = value; }} }

可以看出,這是一個典型的適配器模式的應用,通過一個抽象層接口,為不同Server提供HttpRequest和HttpResponse對象的核心屬性。

2Middleware與ApplicationBuilder

在啟動項目中,定義了三個中間件如下所示:

public static RequestDelegate FooMiddleware(RequestDelegate next) => async context => {await context.Response.WriteAsync("Foo=>");await next(context); };public static RequestDelegate BarMiddleware(RequestDelegate next) => async context => {await context.Response.WriteAsync("Bar=>");await next(context); };public static RequestDelegate BazMiddleware(RequestDelegate next) => context => context.Response.WriteAsync("Baz");

可以看到,每個中間件的作用都很簡單,就是向響應流中輸出一個字符串。其中Foo和Bar兩個中間件在輸出之后,還會調用下一個中間件進行處理,而Baz不會調用下一個中間件進行處理,因此Baz在注冊順序上排在了最后,這也解釋了我們?yōu)楹卧贏SP.NET Core中進行中間件的注冊時,注冊的順序比較講究,因為這會影響到后面的執(zhí)行順序。

剛剛在進行WebHost的創(chuàng)建時,調用了WebHostBuilder的Configure方法進行中間件的注冊,而這個Configure方法的輸入參數(shù)是一個IApplicationBuilder的委托:

public interface IApplicationBuilder {IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);RequestDelegate Build(); }

可能直接看這個接口定義不是太明白,下面來看看ApplicationBuilder的實現(xiàn):

public class ApplicationBuilder : IApplicationBuilder {private readonly List<Func<RequestDelegate, RequestDelegate>> _middlewares = new List<Func<RequestDelegate, RequestDelegate>>();/// <summary>/// 構建請求處理管道/// </summary>/// <returns>RequestDelegate</returns>public RequestDelegate Build(){_middlewares.Reverse(); // 倒置注冊中間件集合的順序return httpContext =>{// 注冊默認中間件 => 返回404響應RequestDelegate next = _ => {_.Response.StatusCode = 404;return Task.CompletedTask;};// 構建中間件處理管道foreach (var middleware in _middlewares){next = middleware(next);}return next(httpContext);};}/// <summary>/// 注冊中間件/// </summary>/// <param name="middleware">中間件</param>/// <returns>ApplicationBuilder</returns>public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware){_middlewares.Add(middleware);return this;} }

其中,Use方法的作用就是接受中間件進行注冊,Build方法的作用就是構建由注冊中間件組成的請求處理管道,而Server加上這個由中間件組成的請求處理管道便是ASP.NET Core的核心內容。因此,我們可以說ASP.NET Core Pipeline = Server + Middlewares。此外,我們還可以將多個Middleware構建成一個單一的“HttpHandler”,那么整個ASP.NET Core框架將具有更加簡單的表達:Pipeline = Server + HttpHandler

因此,這里的Build方法中做了以下幾件事情:

(1)倒置注冊中間件集合的順序

_middlewares.Reverse();

為什么要倒置順序呢?不是說執(zhí)行順序要跟注冊順序保持一致么?別急,且看后面的代碼。

(2)注冊默認中間件

return httpContext => {// 注冊默認中間件 => 返回404響應RequestDelegate next = _ => {_.Response.StatusCode = 404;return Task.CompletedTask;};...... }

這里默認中間件是返回404,在如果沒有手動注冊任何中間件的情況下生效。

(3)構建一個中間件處理管道 => "HttpHandler"

public RequestDelegate Build() {......return httpContext =>{......// 構建中間件處理管道foreach (var middleware in _middlewares){next = middleware(next);}return next(httpContext);}; }

在通過Use方法注冊多個中間件到middlewares集合中后,會在這里通過一個遍歷組成一個單一的middleware(在這里表示為一個RequestDelegate對象),如下圖所示。

對于middleware,它在這里是一個Func<RequestDeletegate, RequestDelegate>對象,它的輸入和輸出都是RequestDelegate。

對于管道的中的某一個middleware來說,由后續(xù)middleware組成的管道體現(xiàn)為一個RequestDelegate對象,由于當前middleware在完成了自身的請求處理任務之后,往往需要將請求分發(fā)給后續(xù)middleware進行處理,所以它需要將由后續(xù)中間件構成的RequestDelegate作為輸入。當代表中間件的委托對象執(zhí)行之后,我們希望的是將當前中間件“納入”這個管道,那么新的管道體現(xiàn)的RequestDelegate自然成為了輸出結果。

因此,這里也就解釋了為什么要在第一步中進行middleware的順序的倒置,否則無法以注冊的順序構成一個單一的middleware,下圖是示例代碼中的所有middleware構成的一個單一的RequestDelegate,經過層層包裹,以達到依次執(zhí)行各個middleware的效果。需要注意的就是在BazMiddleware中,沒有調用下一個中間件,因此404中間件便不會得到觸發(fā)處理的機會。

下圖是最后的執(zhí)行結果:

3小結

經過蔣金楠老師的講解以及自己的學習,對這個Mini版的ASP.NET Core框架有了一個初步的理解,正如蔣老師所說,ASP.NET Core的核心就在于由一個服務器和若干中間件構成的管道,了解了這一點,就對ASP.NET Core的核心本質有了大概印象。當然,這個Mini版的ASP.NET Core只是模擬了ASP.NET Core的冰山一角,還有許多的特性都沒有,比如基于Starup來注冊中間件,依賴注入框架,配置系統(tǒng),預定義中間件等等等等,但是對于廣大ASP.NET Core學習者來說是個絕佳的入門,是進階學習ASP.NET Core源碼的基石讀物,最后感謝大內老A的分享!

最后讓我們一起期待A大的《ASP.NET Core框架揭秘》早日出版!

4參考資料

蔣金楠,《200行代碼,7個對象—讓你了解ASP.NET Core框架的本質》

蔣金楠,《Inside ASP.NET Core Framework》

Edison Zhou,https://github.com/EdisonChou/AspNetCore.Mini

????點擊閱讀原文,觀看A大分享

總結

以上是生活随笔為你收集整理的一个迷你ASP.NET Core框架的实现(下)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。