ASP.NET Core 框架本质学习

本文做为学习过程当中的一个记录。html

学习文章地址:git

https://www.cnblogs.com/artech/p/inside-asp-net-core-framework.htmlgithub

 

一. ASP.NET Core 框架上的 Hello World程序

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

而下面主要讲的就是 这个管道是如何被构建起来的,以及该管道采用怎样的请求处理流程

二. 在咱们本身的ASP.NET Core Mini上面开发的 Hello World

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框架讲解

下面主要是对 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     }
View Code

里面定义了请求和响应里面须要的一些内容。

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 }
View Code

这个应该就是 用来装 请求和响应 的。

注: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 }
View Code

这个里面定义了 请求实体类,响应实体类,共享上下文(HttpContext), 以及响应的扩展方法输出内容。

4.建立 RequestDelegate.cs 文件

1 namespace App
2 {
3     public delegate Task RequestDelegate(HttpContext context);
4 }
View Code

这个用来承载 共享上下文 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 }
View Code

这个是用来承载 RequestDelegate 的,而且存放中间件及使之串起来的。

6. 建立 IServer.cs 文件

1 namespace App
2 {
3     public interface IServer
4     {
5         Task StartAsync(RequestDelegate handler);
6     }
7 }
View Code

这个是用来定义服务器接口的。

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 }
View Code

咱们的共享上下文信息最初来源于这个类中的 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 }
View Code

这个是服务器文件,其中定义了监听器,监听的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 }
View Code

这里是用来建立宿主的,用来把服务器和中间件服务链接起来的。

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 }
View Code

这个是宿主构建者,用来设置服务器,配置中间件,以及 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 }
View Code

这其中包括 建立宿主构建者,设置服务器,配置中间件, Build成宿主,及启动宿主等。

代码结束!!

12. 上面的代码实际已经结束了,可是发现编译的时候报错。

C# 7.0 不支持Program.cs中的用法。

怎么修改呢?

右键项目---> 属性----> 生成 ----> 高级 ----> 

而后在 常规 下的语言版本中,选择 c#最新次要版本 

以下

13. 从新编译,成功

 

14. 运行,而后在 浏览器中 输入 http://localhost:5000

能够在代码中打上断点,观察执行过程。

 

这里,把我本身写的也上传到github了,方便本身查阅,有疑问的小伙伴能够本身去原文学习

个人github地址:https://github.com/Vincent-yuan/asp.net_core_mini

相关文章
相关标签/搜索