本文作为学习过程中的一个记录。

学习文章地址:

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

一. 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的构建者;

而 在WebHostBuilder在调用 Build方法之前,调用的 两个方法:

UseKestrel :旨在注册一个名为Kestrel的服务器

Configure:为了注册一个用来处理请求的中间件

在上面的代码中,中间件在响应的主体内容中写入了一个 Hello World 的文本。

当我们在调用Run方法启动作为应用宿主的 WebHost的时候,WebHost会利用WebHostBuilder提供的服务器和中间件构建一个请求处理管道。

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

二. 在我们自己的ASP.NET Core Mini上面开发的 Hello World

本文作为学习过程中的一个记录。

学习文章地址:

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

一. 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的构建者;

而 在WebHostBuilder在调用 Build方法之前,调用的 两个方法:

UseKestrel :旨在注册一个名为Kestrel的服务器

Configure:为了注册一个用来处理请求的中间件

在上面的代码中,中间件在响应的主体内容中写入了一个 Hello World 的文本。

当我们在调用Run方法启动作为应用宿主的 WebHost的时候,WebHost会利用WebHostBuilder提供的服务器和中间件构建一个请求处理管道。

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

二. 在我们自己的ASP.NET Core Mini上面开发的 Hello World

代码说明:

在创建出 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的核心要素:请求和响应

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对象。

在调用第一个中间件(最后注册)的时候,我们创建了一个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对象。

如下,用来提供请求的IHttpRequestFeature和提供响应IHttpResponseFeature接口的定义,可以看出它们具有和HttpRequest和HttpResponse完全一致的成员定义。

接下来,我们来看看HttpContext的具体实现。

ASP.NET Core Mini的HttpContext只包含Request和Response两个属性成员,对应的类型分别为HttpRequest和HttpResponse,下面是这两个类型的具体实现。

其中,HttpRequest和HttpResponse都是通过一个IFeatureCollection对象构建而成的,它们对应的属性成员均由包含在这个Feature集合中的IHttpRequestFeature和IHttpResponseFeature对象来提供。

HttpContext的实现就更加简单了。我们在创建一个HttpContext对象时同样会提供一个IFeatureCollection对象,

我们利用该对象创建对应的HttpRequest和HttpResponse对象,并作为其对应的属性值。

总结:在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对象。

当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方法将服务器启

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对象。

至此,本篇结束。

补充:

这里补充的是按照这里的学习,实现这个程序的过程。

1. 首先 创建 Feature.cs 文件

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

2. 创建 FeatureCollection.cs 文件

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

注:1,2两个文件中的接口或者类,不依赖其他自定义类

3. 创建 HttpContext.cs 文件

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

4.创建 RequestDelegate.cs 文件

这个用来承载 共享上下文 HttpContext 的。

5. 创建 ApplicationBuilder.cs 文件

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

6. 创建 IServer.cs 文件

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

7.创建 HttpListenerFeature.cs 文件

我们的共享上下文信息最初来源于这个类中的 HttpListenerContext。它是在服务器中,用来接收 HttpListener 中的上下文对象的(即HttpListenerContext)。

这里把 它按 请求和响应的接口定义进行拆分。

8. 创建 HttpListenerServer.cs 文件

这个是服务器文件,其中定义了监听器,监听的url集合,及启动监听,创建共享上下文,及使用RequestDelegate类型的处理器承载 上下文等操作。

9. 创建 WebHost.cs 文件

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

10. 创建 WebHostBuilder.cs 文件

这个是宿主构建者,用来设置服务器,配置中间件,以及 Build 出宿主WebHost的。

11. 最后,创建 Program.cs 文件

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

代码结束!!

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

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

怎么修改呢?

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

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

如下

13. 重新编译,成功

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

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

这里,把我自己写的也上传到github了,方便自己查阅,有疑问的小伙伴可以自己去原文学习

我的github地址:https://github.com/Vincent-yuan/asp.net_core_mini

原文链接:https://www.cnblogs.com/Vincent-yuan/p/11318718.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

ASP.NET Core 框架本质学习相关推荐

  1. 大内老A:200行代码,7个对象——让你了解ASP.NET Core框架的本质

    来源 | https://www.cnblogs.com/artech/p/inside-asp-net-core-framework.html 2019年1月19日,微软技术(苏州)俱乐部成立,我受 ...

  2. asp服务器_200行代码,7个对象——让你了解ASP.NET Core框架的本质「3.x版」

    2019年1月19日,微软技术(苏州)俱乐部成立,我受邀在成立大会上作了一个名为<ASP.NET Core框架揭秘>的分享.在此次分享中,我按照ASP.NET Core自身的运行原理和设计 ...

  3. 200行代码,7个对象——让你了解ASP.NET Core框架的本质[3.x版]

    2019年1月19日,微软技术(苏州)俱乐部成立,我受邀在成立大会上作了一个名为<ASP.NET Core框架揭秘>的分享.在此次分享中,我按照ASP.NET Core自身的运行原理和设计 ...

  4. spring web请求statuscode = 200 无响应值_200行代码,7个对象——让你了解ASP.NET Core框架的本质[3.x版]...

    2019年1月19日,微软技术(苏州)俱乐部成立,我受邀在成立大会上作了一个名为<ASP.NET Core框架揭秘>的分享.在此次分享中,我按照ASP.NET Core自身的运行原理和设计 ...

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

    [框架内幕]| 作者 / Edison Zhou 这是恰童鞋骚年的第196篇原创文章 上一篇我们了解了AspNetCore.Mini这个项目的背景及项目结构和流程,这一篇我们继续解析几个核心对象.本文 ...

  6. 解读大内老A的《.NET Core框架本质》

    老A说的一句话让我很受启发,想要深入了解框架,你要把精力聚焦在架构设计的层面来思考问题.而透彻了解底层原理,最好的笨办法就是根据原理对框架核心进行重建或者说再造.看起来没有捷径,也是最快的捷径. 相信 ...

  7. 蒋金楠:200行代码7个对象《ASP.NET Core框架揭密》苏州.NET俱乐部课程分享

    [课程名称] <ASP.NET Core框架揭密> [老师介绍] 蒋金楠,同程艺龙机票事业部技术专家,微软最有价值专家(MVP,连续12),多本.NET专著作者.博客园Artech,公众号 ...

  8. ASP.NET Core 源码学习之Logging[1]:Introduction

    在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...

  9. 一步步完成“迷你版” 的ASP.NET Core框架

    一 前言 Artech 分享了 200行代码,7个对象--让你了解ASP.NET Core框架的本质 . 用一个极简的模拟框架阐述了ASP.NET Core框架最为核心的部分. 这里一步步来完成这个迷 ...

最新文章

  1. BZOJ 3483 SGU505 Prefixes and suffixes(字典树+可持久化线段树)
  2. 医学与人工智能交叉融合,打开眼科理疗新窗
  3. leetcode-79-单词搜索(用dfs解决)
  4. Oracle 11g Data Guard 使用duplicate from active database 创建 standby database
  5. 实战并发编程 - 08基于Guarded Suspension模式优化轮询while(true)
  6. c语言 错误 无效的控制谓词,PAT 1025反转链表的代码实现及错误分析(C语言)
  7. 如何在ABAP Development Tool里只显示能在SAP云平台上使用的ABAP资源
  8. shell for循环1到100_浅谈Linux下shell 编程的for循环常用的6种结构
  9. java连接打印机访问被拒绝_java – 尝试访问spring security中的登录页面时访问被拒绝的异常...
  10. LeetCode刷题(42)--Subsets
  11. Linux电驴客户端,ubuntu装电驴
  12. 聊聊flink JobManager的heap大小设置 1
  13. c语言上机作业题及答案,华为C语言上机试题及答案
  14. 大一计算机题库百度云,大一计算机考试题库.pdf
  15. MYSQL基础学习了解
  16. 2019百度之星初赛第五题
  17. Oracle 中的 unique index 和 non unique index的区别
  18. 计算机大神专业小说,5本高人气系统流小说,无敌满足你,一路爽到底,全部是大神出品...
  19. 论文推介:CaTT-KWS—基于级联Transducer-Transformer的多阶段自定义关键词识别框架
  20. 数据分析模型篇—安索夫矩阵

热门文章

  1. 将VC++6.0的代码迁移到VS2005常见问题总结(Window核心编程第五版以前代码在VS2005无法编译的解决方案)...
  2. Mac OS X必备APP推荐之一
  3. 使用Adaptive cards来构建Teams app的界面
  4. html走马观花效果,走马观花台湾行 用EF-S 10-18来记录风景
  5. 暴力打表之hdu 2089
  6. 远程访问CENTOS的MYSQL数据库设置
  7. 海尔联手软银机器人,进军服务机器人领域
  8. Java Socke 探究
  9. 大数据量分页查询方法(转)
  10. java的linux执行的shell