本文做爲學習過程當中的一個記錄。html
學習文章地址:git
https://www.cnblogs.com/artech/p/inside-asp-net-core-framework.htmlgithub
public class Program { public static void Main() => new WebHostBuilder() .UseKestrel() .Configure(app => app.Run(context => context.Response.WriteAsync("Hello World!"))) .Build() .Run(); }
WebHost : 承載Web應用的宿主;編程
WebHostBuilder :WebHost的構建者;c#
而 在WebHostBuilder在調用 Build方法以前,調用的 兩個方法:瀏覽器
UseKestrel :旨在註冊一個名爲Kestrel的服務器服務器
Configure:爲了註冊一個用來處理請求的中間件app
在上面的代碼中,中間件在響應的主體內容中寫入了一個 Hello World 的文本。框架
當咱們在調用Run方法啓動做爲應用宿主的 WebHost的時候,WebHost會利用WebHostBuilder提供的服務器和中間件構建一個請求處理管道。 asp.net
而下面主要講的就是 這個管道是如何被構建起來的,以及該管道採用怎樣的請求處理流程。
public class Program { public static async Task Main() { await new WebHostBuilder() .UseHttpListener() .Configure(app => app .Use(FooMiddleware) .Use(BarMiddleware) .Use(BazMiddleware)) .Build() .StartAsync(); } 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"); }
代碼說明:
在建立出 WebHostBuilder 以後,咱們調用了它的擴展方法 UseHttpListener 註冊了一個自定義的基於 HttpListener的服務器;
隨後針對 Configure 方法的調用中,咱們註冊了三個中間件。
因爲中間件最終是經過 Delegate對象來體現的,因此咱們能夠將中間件定義成與Delegate類型具備相同簽名的方法。
程序運行後,獲得的輸出結果:
下面主要是對 ASP.NET Core Mini框架的構建過程當中關鍵部分的講解。
主要涉及 HttpContext、RequestDelegate、Middleware、ApplicationBuilder、Server、WebHost、WebHostBuilder 等七個對象;
另外 會講到 HttpContext與Server之間的適配;HttpListenerServer等;
1. 第一個對象:HttpContext
關於 HttpContext的本質,還得從請求處理管道的層面來說。
對於由一個服務器和多箇中間件構建的管道來講,面向傳輸層的服務器負責請求的監聽、接收和最終的響應;
當它接收到客戶端發送的請求後,須要將它分發給後續中間件進行處理。
對於某個中間件來講,當咱們完成了自身的請求處理任務以後,在大部分狀況下,也須要將請求分發給後續的中間件。
請求在服務器與中間件之間,以及在中間件之間的分發是經過共享上下文的方式實現的。
( 如上圖,當服務器接收到請求以後,會建立一個經過HttpContext表示的上下文對象,全部中間件都是在這個上下文中處理請求的;
那麼一個HttpContext對象究竟攜帶了怎樣的上下文信息呢?
咱們知道一個HTTP事務具備很是清晰的界定,即接收請求、發送響應;
因此請求和響應是兩個基本的要素,也是HttpContext承載的最核心的 上下文信息。)
故,HttpContext的核心要素:請求和響應
public class HttpContext { public HttpRequest Request { get; } public HttpResponse Response { get; } } public class HttpRequest { public Uri Url { get; } //請求地址 public NameValueCollection Headers { get; } //首部集合 public Stream Body { get; } //主體內容 } public class HttpResponse { public NameValueCollection Headers { get; } //首部集合 public Stream Body { get; } //主體內容 public int StatusCode { get; set;} //響應狀態碼 }
2. 第二個對象:RequestDelegate
這是一個委託,也須要從管道的角度才能充分理解這個委託對象的本質。
2.1 管道的設計
能夠總結爲 Pipeline = Server + Middlewares ,再精簡寫的話,能夠寫爲 Pipeline = Server + HttpHandler .
2.2 那麼,咱們如何來表達HttpHandler呢?
既然針對當前請求的全部輸入和輸出都經過HttpContext來表示,那麼 HttpHandler就能夠表示成一個 Action<HttpContext>對象。
那麼HttpHandler在ASP.NET Core中時經過 Action<HttpContext>來表示的嗎?
其實不是的,緣由很簡單:Action<HttpContext>只能表示針對請求的 同步的處理操做,可是針對 HTTP 請求既能夠是同步的,也能夠是異步的,更多的實際上是異步的。
那麼在 .NET Core的世界中如何來表示一個同步或者異步操做呢?就是Task對象,那麼 HttpHandler天然能夠表示爲一個 Func<HttpContext,Task>對象。
因爲這個委託對象實在過重要了,因此咱們將它定義成一個獨立的類型: delegate Task RequestDelegate(HttpContext context) 。
3. 第三個對象:Middleware
中間件在ASP.NET Core 中被表示成一個 Func<RequestDelegate,RequestDelegate>對象,即它的輸入和輸出都是一個RequestDelegate。
爲何採用一個Func<RequestDelegate,RequestDelegate>對象來表示中間件。是由於這樣的考慮:
對於管道中的某一箇中間件來講,由後續中間件組成的管道體現爲一個RequestDelegate對象,因爲當前中間件在完成了自身的請求處理任務以後,每每須要將請求分發給後續中間件進行處理,因此它須要將由後續中間件構成的RequestDelegate做爲輸入。
即:上一個中間件的輸出須要能夠做爲下一個中間件的輸入,因此設計爲Func<RequestDelegate,RequestDelegate>對象
4. 第四個對象:ApplicationBuilder
ApplicationBuilder 是用來構建 Application的。
既然 Pipeline = Server + HttpHandler , 能夠看出HttpHandler承載了當前應用的全部職責,那麼 HttpHandler就等於 Application。
因爲 HttpHandler經過RequestDelegate表示,那麼由ApplicationBuilder構建的Application就是一個RequestDelegate對象。(職責1)
因爲表示HttpHandler的RequestDelegate是由註冊的中間件來構建的,因此ApplicationBuilder還具備註冊中間件的功能。(職責2)
基於ApplicationBuilder具備的這兩個基本職責,咱們能夠將對應的接口定義爲以下形式。
Use 方法用來註冊提供的中間件,Builder方法則將註冊的中間件構建成一個RequestDelegate對象。
public interface IApplicationBuilder { IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); RequestDelegate Build(); }
下面是針對這個接口的具體實現。
咱們用一個列表保存註冊的中間件,因此Use方法只須要將提供的中間件添加到這個列表中便可。
當Build方法被調用後,咱們只須要按照與註冊相反的順序依次執行表示中間件的Func<RequestDelegate,RequestDelegate>對象,就能最終構建出表明HttpHandler的RequestDelegate對象。
public class ApplicationBuilder : IApplicationBuilder { private readonly List<Func<RequestDelegate, RequestDelegate>> _middlewares = new List<Func<RequestDelegate, RequestDelegate>>(); public RequestDelegate Build() { _middlewares.Reverse(); return httpContext => { RequestDelegate next = _ => { _.Response.StatusCode = 404; return Task.CompletedTask; }; foreach (var middleware in _middlewares) { next = middleware(next); } return next(httpContext); }; } public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { _middlewares.Add(middleware); return this; } }
在調用第一個中間件(最後註冊)的時候,咱們建立了一個RequestDelegate做爲輸入,後者會將響應狀態碼設置爲404。
因此若是ASP.NET Core應用在沒有註冊任何中間件的狀況下,老是返回一個404響應。
若是全部中間件在完成了自身的請求處理任務以後都選擇將請求向後分發,一樣會返回一個404響應。
總結:對於上面的四個對象,從後向前依次對前一個進行包裝。
5. 第五個對象:Server
當咱們運行(Run)做爲應用宿主的WebHost的時候,服務器它被自動啓動。
啓動後的服務器會綁定到指定的端口進行請求監聽,一旦有請求抵達,服務器會根據該請求建立出表明上下文的HttpContext對象,
並將該上下文做爲輸入,調用由全部註冊中間件構建而成的RequestDelegate對象。
簡單起見,咱們使用以下簡寫的IServer接口來表示服務器。
咱們經過定義在IServer接口的惟一方法 StartAsync啓動服務器,
做爲參數的 handler 正是由全部中間件共同構建而成的RequestDelegate對象
public interface IServer { Task StartAsync(RequestDelegate handler); }
6. HttpContext和Server之間的適配
面向應用層的HttpContext對象是對請求和相應的封裝,可是請求最初來源於服務器,針對HttpContext的任何響應操做也必須做用於當前的服務器才能真正起做用。
如今問題來了,全部的ASP.NET Core應用使用的都是同一個HttpContext類型,可是卻能夠註冊不一樣類型的服務器,咱們必須解決二者之間的適配問題。
同一個HttpContext類型與不一樣服務器類型之間的適配能夠經過添加一個抽象層來解決,咱們定義該層的對象爲Feature。
如上圖,咱們能夠定義一系列的Feature接口來爲HttpContext提供上下文信息,其中最重要的就是提供請求的 IRequestFeature和完成響應的IResponseFeature接口。
那麼具體的服務器只須要實現這些Feature接口就能夠了。
下面是一些代碼片斷。咱們定義了一個IFeatureCollection接口來表示存放Feature對象的集合。
爲了編程上的便利,咱們定義了兩個擴展方法 Set<T>和Get<T>來設置和獲取Feature對象。
public interface IFeatureCollection : IDictionary<Type, object> { } public class FeatureCollection : Dictionary<Type, object>, IFeatureCollection { } public static partial class Extensions { public static T Get<T>(this IFeatureCollection features) => features.TryGetValue(typeof(T), out var value) ? (T)value : default(T); public static IFeatureCollection Set<T>(this IFeatureCollection features, T feature) { features[typeof(T)] = feature; return features; } }
以下,用來提供請求的IHttpRequestFeature和提供響應IHttpResponseFeature接口的定義,能夠看出它們具備和HttpRequest和HttpResponse徹底一致的成員定義。
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; } }
接下來,咱們來看看HttpContext的具體實現。
ASP.NET Core Mini的HttpContext只包含Request和Response兩個屬性成員,對應的類型分別爲HttpRequest和HttpResponse,下面是這兩個類型的具體實現。
其中,HttpRequest和HttpResponse都是經過一個IFeatureCollection對象構建而成的,它們對應的屬性成員均由包含在這個Feature集合中的IHttpRequestFeature和IHttpResponseFeature對象來提供。
public class HttpRequest { private readonly IHttpRequestFeature _feature; public Uri Url => _feature.Url; public NameValueCollection Headers => _feature.Headers; public Stream Body => _feature.Body; public HttpRequest(IFeatureCollection features) => _feature = features.Get<IHttpRequestFeature>(); } public class HttpResponse { private readonly IHttpResponseFeature _feature; public NameValueCollection Headers => _feature.Headers; public Stream Body => _feature.Body; public int StatusCode { get => _feature.StatusCode; set => _feature.StatusCode = value; } public HttpResponse(IFeatureCollection features) => _feature = features.Get<IHttpResponseFeature>(); }
HttpContext的實現就更加簡單了。咱們在建立一個HttpContext對象時一樣會提供一個IFeatureCollection對象,
咱們利用該對象建立對應的HttpRequest和HttpResponse對象,並做爲其對應的屬性值。
public class HttpContext { public HttpRequest Request { get; } public HttpResponse Response { get; } public HttpContext(IFeatureCollection features) { Request = new HttpRequest(features); Response = new HttpResponse(features); } }
總結:在HttpContext中傳入 IFeatureCollection對象,HttpContext中的成員對象HttpRequest和HttpResponse會利用這個IFeatureCollection來被構建。從而完成HttpContext的構建。固然,其中少不了須要Server部分,下面會講。
7. HttpListenerServer 服務器
在對服務器和它與HttpContext的適配原理有清晰的認識以後,咱們嘗試着定義一個服務器。
在前面,咱們利用WebHostBuilder的擴展方法UseHttpListener註冊了一個HttpListenerServer,咱們如今來看看這個採用HttpListener做爲監聽器的服務器類型是如何實現的。
因爲全部的服務器都須要有本身的Feature實現來爲HttpContext提供對應的上下文信息,因此咱們得先來爲HttpListenerServer定義相應的接口。
對HttpListener監聽器稍微瞭解的朋友應該知道它在接收到請求以後同時會建立一個本身的上下文對象,對應的類型爲HttpListenerContext。
若是採用HttpListenerServer做爲應用的服務器,意味着HttpContext承載的上下文信息最初來源於這個HttpListenerContext,因此Feature的目的旨在解決這兩個上下文之間的適配問題。
總結:Feature實際上解決的就是HttpContext和HttpListenerContext之間的適配問題。
下面的HttpListenFeature就是咱們爲HttpListenerServer定義的Feature。HttpListenerFeature同時實現了IHttpRequestFeature和IHttpResponseFeature,實現的 6 個屬性最初都來源於建立該對象提供的HttpListenerContext對象。
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 => _context.Response.StatusCode; set => _context.Response.StatusCode = value; } }
當HttpListener監聽到抵達的請求後,咱們會獲得一個HttpListenerContext對象,此時咱們只須要據此建立一個HttpListenerFeature對象,
而且它分別以IHttpRequestFeature和IHttpResponseFeature接口類型註冊到建立FeatureCollection集合上。
咱們最終利用這個FeatureCollection對象建立出表明上下文的HttpContext,而後將它做爲參數調用由全部中間件公共構建的RequestDelegate對象便可。
8. 第六個對象:WebHost
到目前爲止,咱們已經知道了由一個服務器和多箇中間件構成的管道是如何完整針對請求的監聽、接收、處理和最終響應的,接下來討論這樣的管道是如何被構建出來的。
管道是在做爲應用宿主的WebHost對象啓動的時候被構建出來的,在ASP.NET Core Mini 中,
咱們將表示應用宿主的IWebHost接口簡寫成以下形式:
只包含一個StartAsync方法來啓動應用程序。
public interface IWebHost { Task StartAsync(); }
因爲由WebHost構建的管道由Server和HttpHandler構成,咱們在默認實現的WebHost類型中,咱們直接提供這兩個對象。
在實現的StartAsync方法中,咱們只須要將後者做爲參數調用前者的StartAsync方法將服務器啓動就能夠了
public class WebHost : IWebHost { private readonly IServer _server; private readonly RequestDelegate _handler; public WebHost(IServer server, RequestDelegate handler) { _server = server; _handler = handler; } public Task StartAsync() => _server.StartAsync(_handler); }
9. 第七個對象:WebHostBuilder
WebHost的做用:就是建立做爲應用宿主的WebHost。
因爲在建立WebHost的時候,須要提供註冊的服務器和由全部註冊中間件構建而成的RequestDelegate,
因此在對應接口IWebHostBuilder中,咱們爲它定義了三個核心方法。
public interface IWebHostBuilder { IWebHostBuilder UseServer(IServer server); IWebHostBuilder Configure(Action<IApplicationBuilder> configure); IWebHost Build(); }
除了用來建立WebHost的Build方法以外,咱們提供了用來註冊服務器的UseServer方法和用來註冊中間件的Configure方法。
Configure方法提供了一個類型爲 Action<IApplicationBuilder>的參數,
意味着咱們針對中間件的註冊時利用上面介紹的IApplicationBuilder對象來完成的。
以下代碼,WebHostBuilder是針對IWebHostBuilder接口的默認實現,
它具備兩個字段分別用來保存註冊的中間件和調用Configure方法提供的Action<IApplicationBuilder>對象。
當Build方法被調用以後,咱們建立一個ApplicationBuilder對象,並將它做爲參數調用這些Action<IApplicationBuilder>委託,
進而將全部中間件所有註冊到這個ApplicationBuilder對象上。
咱們最終調用它的Build方法獲得全部中間件共同構建的RequestDelegate對象,並利用它和註冊的服務器構建做爲應用宿主的WebHost對象。
public class WebHostBuilder : IWebHostBuilder { private IServer _server; private readonly List<Action<IApplicationBuilder>> _configures = new List<Action<IApplicationBuilder>>(); public IWebHostBuilder Configure(Action<IApplicationBuilder> configure) { _configures.Add(configure); return this; } public IWebHostBuilder UseServer(IServer server) { _server = server; return this; } public IWebHost Build() { var builder = new ApplicationBuilder(); foreach (var configure in _configures) { configure(builder); } return new WebHost(_server, builder.Build()); } }
至此,本篇結束。
這裏補充的是按照這裏的學習,實現這個程序的過程。
1. 首先 建立 Feature.cs 文件
1 namespace App 2 { 3 public interface IHttpRequestFeature 4 { 5 Uri Url { get; } 6 7 NameValueCollection Headers { get; } 8 9 Stream Body { get; } 10 } 11 12 13 public interface IHttpResponseFeature 14 { 15 int StatusCode { get; set; } 16 17 NameValueCollection Headers { get; } 18 19 Stream Body { get; } 20 21 }
裏面定義了請求和響應裏面須要的一些內容。
2. 建立 FeatureCollection.cs 文件
1 namespace App 2 { 3 public interface IFeatureCollection : IDictionary<Type, object> { } 4 5 public class FeatureCollection : Dictionary<Type, object>, IFeatureCollection { } 6 7 public static partial class Extensions 8 { 9 public static T Get<T>(this IFeatureCollection features) => features.TryGetValue(typeof(T), out var value) ? (T)value : default(T); 10 11 public static IFeatureCollection Set<T>(this IFeatureCollection features,T feature) 12 { 13 features[typeof(T)] = feature; 14 return features; 15 } 16 } 17 }
這個應該就是 用來裝 請求和響應 的。
注:1,2兩個文件中的接口或者類,不依賴其餘自定義類
3. 建立 HttpContext.cs 文件
1 namespace App 2 { 3 /// <summary> 4 /// 共享上下文 5 /// </summary> 6 public class HttpContext 7 { 8 public HttpRequest Request { get; } 9 10 public HttpResponse Response { get; } 11 12 public HttpContext(IFeatureCollection features) 13 { 14 Request = new HttpRequest(features); 15 Response = new HttpResponse(features); 16 } 17 } 18 19 /// <summary> 20 /// 表示請求實體 21 /// 會使用到IFeatureCollection接口 22 /// </summary> 23 public class HttpRequest 24 { 25 private readonly IHttpRequestFeature _feature; 26 27 public HttpRequest(IFeatureCollection features) => _feature = features.Get<IHttpRequestFeature>(); 28 29 public Uri URl => _feature.Url; 30 public NameValueCollection Headers => _feature.Headers; 31 32 public Stream Body => _feature.Body; 33 34 35 } 36 37 /// <summary> 38 /// 響應實體 39 /// 須要用到IFeatureCollection接口 40 /// </summary> 41 public class HttpResponse 42 { 43 private readonly IHttpResponseFeature _feature; 44 45 public HttpResponse(IFeatureCollection features) => _feature = features.Get<IHttpResponseFeature>(); 46 47 public int StatusCode 48 { 49 get => _feature.StatusCode; 50 set => _feature.StatusCode = value; 51 } 52 53 public NameValueCollection Headers => _feature.Headers; 54 55 public Stream Body => _feature.Body; 56 } 57 58 /// <summary> 59 /// 響應的輸出 60 /// </summary> 61 public static partial class Extensions 62 { 63 public static Task WriteAsync(this HttpResponse response,string contents) 64 { 65 var buffer = Encoding.UTF8.GetBytes(contents); 66 return response.Body.WriteAsync(buffer, 0, buffer.Length); 67 } 68 } 69 70 }
這個裏面定義了 請求實體類,響應實體類,共享上下文(HttpContext), 以及響應的擴展方法輸出內容。
4.建立 RequestDelegate.cs 文件
1 namespace App 2 { 3 public delegate Task RequestDelegate(HttpContext context); 4 }
這個用來承載 共享上下文 HttpContext 的。
5. 建立 ApplicationBuilder.cs 文件
1 namespace App 2 { 3 /// <summary> 4 /// Application構建者 5 /// </summary> 6 public interface IApplicationBuilder 7 { 8 IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); 9 10 RequestDelegate Build(); 11 } 12 13 public class ApplicationBuilder : IApplicationBuilder 14 { 15 /// <summary> 16 /// 用來存放註冊的中間件 17 /// </summary> 18 private readonly List<Func<RequestDelegate, RequestDelegate>> _middlewares = new List<Func<RequestDelegate, RequestDelegate>>(); 19 20 public RequestDelegate Build() 21 { 22 _middlewares.Reverse(); 23 return httpContext => 24 { 25 RequestDelegate next = _ => { _.Response.StatusCode = 404; return Task.CompletedTask; }; 26 27 foreach(var middleware in _middlewares) 28 { 29 next = middleware(next); 30 } 31 return next(httpContext); 32 }; 33 } 34 35 public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) 36 { 37 _middlewares.Add(middleware); 38 return this; 39 } 40 } 41 }
這個是用來承載 RequestDelegate 的,而且存放中間件及使之串起來的。
6. 建立 IServer.cs 文件
1 namespace App 2 { 3 public interface IServer 4 { 5 Task StartAsync(RequestDelegate handler); 6 } 7 }
這個是用來定義服務器接口的。
7.建立 HttpListenerFeature.cs 文件
1 namespace App 2 { 3 public class HttpListenerFeature : IHttpRequestFeature, IHttpResponseFeature 4 { 5 private readonly HttpListenerContext _context; 6 7 public HttpListenerFeature(HttpListenerContext context) => _context = context; 8 9 Uri IHttpRequestFeature.Url => _context.Request.Url; 10 11 NameValueCollection IHttpRequestFeature.Headers => _context.Request.Headers; 12 13 NameValueCollection IHttpResponseFeature.Headers => _context.Response.Headers; 14 15 Stream IHttpRequestFeature.Body => _context.Request.InputStream; 16 17 Stream IHttpResponseFeature.Body => _context.Response.OutputStream; 18 19 int IHttpResponseFeature.StatusCode 20 { 21 get { return _context.Response.StatusCode; } 22 set { _context.Response.StatusCode = value; } 23 } 24 } 25 }
咱們的共享上下文信息最初來源於這個類中的 HttpListenerContext。它是在服務器中,用來接收 HttpListener 中的上下文對象的(即HttpListenerContext)。
這裏把 它按 請求和響應的接口定義進行拆分。
8. 建立 HttpListenerServer.cs 文件
1 namespace App 2 { 3 public class HttpListenerServer : IServer 4 { 5 //監聽器 6 private readonly HttpListener _httpListener; 7 //監聽的url集合 8 private readonly string[] _urls; 9 10 public HttpListenerServer(params string[] urls) 11 { 12 _httpListener = new HttpListener(); 13 _urls = urls.Any() ? urls : new string[] { "http://localhost:5000/" }; 14 } 15 16 /// <summary> 17 /// 啓動服務器 18 /// </summary> 19 /// <param name="handler"></param> 20 /// <returns></returns> 21 public async Task StartAsync(RequestDelegate handler) 22 { 23 #region 啓動監聽 24 Array.ForEach(_urls, url => _httpListener.Prefixes.Add(url)); 25 _httpListener.Start(); 26 Console.WriteLine("Server started and is listening on:{0}", string.Join(';', _urls)); 27 #endregion 28 29 while (true) 30 { 31 //獲取監聽的上下文 32 var listenerContext = await _httpListener.GetContextAsync(); 33 34 35 var feature = new HttpListenerFeature(listenerContext); 36 var features = new FeatureCollection() 37 .Set<IHttpRequestFeature>(feature) 38 .Set<IHttpResponseFeature>(feature); 39 var httpContext = new HttpContext(features); 40 41 await handler(httpContext); 42 listenerContext.Response.Close(); 43 } 44 45 46 } 47 } 48 49 public static partial class Extensions 50 { 51 /// <summary> 52 /// IWebHostBuilder使用服務器的擴展方法 53 /// </summary> 54 /// <param name="builder"></param> 55 /// <param name="urls"></param> 56 /// <returns></returns> 57 public static IWebHostBuilder UseHttpListener(this IWebHostBuilder builder, params string[] urls) 58 => builder.UseServer(new HttpListenerServer(urls)); 59 } 60 }
這個是服務器文件,其中定義了監聽器,監聽的url集合,及啓動監聽,建立共享上下文,及使用RequestDelegate類型的處理器承載 上下文等操做。
9. 建立 WebHost.cs 文件
1 namespace App 2 { 3 public interface IWebHost 4 { 5 Task StartAsync(); 6 } 7 8 /// <summary> 9 /// 宿主 10 /// </summary> 11 public class WebHost : IWebHost 12 { 13 private readonly IServer _server; 14 private readonly RequestDelegate _handler; 15 16 public WebHost(IServer server,RequestDelegate handler) 17 { 18 _server = server; 19 _handler = handler; 20 } 21 22 public Task StartAsync() => _server.StartAsync(_handler); 23 } 24 }
這裏是用來建立宿主的,用來把服務器和中間件服務鏈接起來的。
10. 建立 WebHostBuilder.cs 文件
1 namespace App 2 { 3 public interface IWebHostBuilder 4 { 5 IWebHostBuilder UseServer(IServer server); 6 IWebHostBuilder Configure(Action<IApplicationBuilder> configure); 7 8 IWebHost Build(); 9 10 } 11 12 public class WebHostBuilder : IWebHostBuilder 13 { 14 private IServer _server; 15 private readonly List<Action<IApplicationBuilder>> _configures = new List<Action<IApplicationBuilder>>(); 16 17 18 public IWebHostBuilder Configure(Action<IApplicationBuilder> configure) 19 { 20 _configures.Add(configure); 21 return this; 22 } 23 24 public IWebHostBuilder UseServer(IServer server) 25 { 26 _server = server; 27 return this; 28 } 29 30 public IWebHost Build() 31 { 32 var builder = new ApplicationBuilder(); 33 foreach(var configure in _configures) 34 { 35 configure(builder); 36 } 37 return new WebHost(_server, builder.Build()); 38 } 39 40 41 } 42 }
這個是宿主構建者,用來設置服務器,配置中間件,以及 Build 出宿主WebHost的。
11. 最後,建立 Program.cs 文件
1 namespace App 2 { 3 public class Program 4 { 5 6 public static async Task Main() 7 { 8 await new WebHostBuilder() 9 .UseHttpListener() 10 .Configure(app => app 11 .Use(FooMiddleware) 12 .Use(BarMiddleware) 13 .Use(BazMiddleware)) 14 .Build() 15 .StartAsync(); 16 } 17 18 public static RequestDelegate FooMiddleware(RequestDelegate next) 19 => async context => 20 { 21 await context.Response.WriteAsync("Foo=>"); 22 await next(context); 23 }; 24 25 public static RequestDelegate BarMiddleware(RequestDelegate next) 26 => async context => 27 { 28 await context.Response.WriteAsync("Bar=>"); 29 await next(context); 30 }; 31 32 public static RequestDelegate BazMiddleware(RequestDelegate next) 33 => async context => 34 { 35 await context.Response.WriteAsync("Baz=>"); 36 }; 37 } 38 }
這其中包括 建立宿主構建者,設置服務器,配置中間件, Build成宿主,及啓動宿主等。
代碼結束!!
12. 上面的代碼實際已經結束了,可是發現編譯的時候報錯。
C# 7.0 不支持Program.cs中的用法。
怎麼修改呢?
右鍵項目---> 屬性----> 生成 ----> 高級 ---->
而後在 常規 下的語言版本中,選擇 c#最新次要版本
以下
13. 從新編譯,成功
14. 運行,而後在 瀏覽器中 輸入 http://localhost:5000
能夠在代碼中打上斷點,觀察執行過程。
這裏,把我本身寫的也上傳到github了,方便本身查閱,有疑問的小夥伴能夠本身去原文學習
個人github地址:https://github.com/Vincent-yuan/asp.net_core_mini