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

  相信很多读者已经看过老A写的这篇文章《200行代码,7个对象——让你了解ASP.NET Core框架的本质》,这是一篇模仿和重建的典范。重建说白了就是模仿,模仿有一个前置条件就是你对底层原理要烂熟于心。否则画虎难画骨,原本要画虎,最后出来的是只猫。

要理解原理就要去阅读源码,就像新人学开车,如何使用尚且磕磕碰碰,更何况让你去了解汽车的构造和引擎。

  所以老A是引路人,我像个门外汉一样对前辈的文章解读不下5遍。我有几个疑问,1.为什么是7个对象?2.这些对象如何分类,如何排序?3.这些对象发明的那个“无”是什么?

  在我深入学习和解读的时候,我越加感觉到老A的这篇文章很值得去深入解读,所谓知其然,知其所以然,这样在编码过程才会游刃有余,以下开始我个人的解读。

  • 委托

  • 构建模式

  • 适配器模式

public class Program

{

    public static void Main()

    => new WebHostBuilder()

        .UseKestrel()

        .Configure(app => app.Use(context => context.Response.WriteAsync("Hello World!")))

        .Build()

        .Run();

}

  以上是原文的代码,我们可以看到WebHostBuilder、Server(即Kestrel)、ApplicationBuilder(即app)三大重要的对象,如下图所示:

 WebHostBuilder这个父亲生出WebHost这个孩子,WebHost又生成整个ASP.NET Core最核心的内容,即由Server和中间件(Middleware)构成的管道Pipeline。我们看下Pipeline的放大图:

  继续把Pipeline拆开,有个很重要的ApplicationBuilder对象,里面包含MiddlewareRequestDelegate。至于HttpContext是独立共享的对象,贯穿在整个管道中间,至此7大对象全部出场完毕。

Configure是个什么玩意?看下代码:

public IWebHostBuilder Configure(Action<IApplicationBuilder> configure)

{

    _configures.Add(configure);

    return this;

}

  我们看到他是一个接受IApplicationBuilder的委托!继续刨根问底,IApplicationBuilder是什么玩意?看下源码:

public interface IApplicationBuilder

{

    IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);

    RequestDelegate Build();

}

  他是一个注册中间件和生成Application的容器,那么Application是什么呢?源码没有这个对象,但是看代码(如下所示)我们可以知道他是真正的委托执行者(Handler),执行是一个动作可以理解为app,我猜想这是取名为ApplicationBuilder的原因。

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);

    };

}

  更详细的过程可以参考下面这张图(图片来源),

  WebHostBuilder开始Build的那一刻开始,WebHost被构造,Server被指定,Middlewares被指定,等WebHost真正启动的时候,Server开始监听,收到请求后,Middleware开始执行。

到此,一个完整的ASP.NET Core的流程就简单的走完了。接下来,我们跟着老A一个一个对象的详细介绍。

1.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 { getset;}

}

  我们知道一个Http事务包括最核心的Request(输入)和Response(输出),所以HttpContext包含这两个核心的东西。

  老A建议大家从管道的角度来理解该对象的作用,管道和HTTP请求流程一脉相承。在Server接收到请求后,HttpContext被创建。

  在服务器和中间件,中间件之间通过什么来传递信息?就是共享上下文,这个上下文就是HttpContext。可以说HttpContext是根据HTTP请求原理包裹的在管道之间的共享的一个上下文对象。

  为什么这里要把HttpContext放在第一个来介绍,因为这是一个最基础的对象。这7大对象的讲解顺序,我感觉是从底层基础开始讲起,再层层往上,最后到WebHostBuilder。

2.RequestDelegate

  这个委托太重要了,和HttpContext一样,老A建议大家从管道的角度来理解这个委托。我们再复习一下管道的含义,如图所示:

  这里的管道:Pipeline = Server + Middlewares

  还能更简单一点吗?可以的:如下图所示

  这里的管道:Pipeline =Server + HttpHandler

  多个Middlewares构成一个HttpHandler对象,这是整个管道的核心,那么应该如何用代码来表示呢?

  老A讲到:“既然针对当前请求的所有输入和输出都通过HttpContext来表示,那么HttpHandler就可以表示成一个Action<HttpContext>对象”。

  但是由于ASP.NET Core推崇异步编程,所以你应该想得到Task对象,那么HttpHandler自然就可以表示为一个Func<HttpContext,Task>对象。由于这个委托对象实在太重要了,所以我们将它定义成一个独立的类型。下图展示的就是整个RequestDelegate的设计思路

1

public delegate Task RequestDelegate(HttpContext context);

  这就是委托的由来!

  • 为什么是委托,而不是别的函数?

  委托是架构设计的底层技术,非常常见。因为委托可以承载约定的函数,遵循开闭原则,能很好的把扩展对外进行开放,保证了底层架构的稳定性。

3.Middleware

  这个对象比较费解。根据源码我们知道Middleware也是一个委托对象(代码如下所示),中间件其实就是一个Func<RequestDelegateRequestDelegate>对象:

1

private readonly List<Func<RequestDelegate, RequestDelegate>> _middlewares = new List<Func<RequestDelegate, RequestDelegate>>();

  该对象的输入和输入都是RequestDelegate,为什么要这么设计呢?我们想一下,当前中间件处理完成后需要将请求分发给后续中间件进行处理,他如何让后续的中间件参与当前的请求呢?所以他必须要拿到代表后续中间件管道构成的那个Handler。

如下图所示,也就是说,后续三个中间件构成的管道就是一个输入,执行完毕后,当前中间件也将被“融入”这个管道(此时该新管道就会由四个中间件构成的一个委托链),然后再输出给你由所有的中间件构成的新管道。如下图所示:

4.ApplicationBuilder

  这又是一个builder,可见builder模式在ASP.NET Core有非常广泛的应用。但是该Builder构建的不是Application,到构建什么内容呢?从下面代码声明我们可以看到他有两个功能。

  从Use的使用来看,第一个功能是注册器,他把一个个中间件串联成一个管道。

public interface  IApplicationBuilder

{

    IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);

    RequestDelegate Build();

}

  第二个功能是Build,如下所示:

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;

    }

}

  Build真正做的事情是循环组装中间件,最后把组装好的委托链进行返回。从_middlewares.Reverse();我们又可以知道,对于委托链来说,中间件的注册顺序和执行顺序是相反的,这里需要进行反转,然后才能保证先注册的中间件先执行。

5.Server

Server对象相对比较简单,我们看下他的接口定义:

public interface IServer

{

    Task StartAsync(RequestDelegate handler);

}

  我们可以看到Server有个启动函数StartAsync,StartAsync内部封装了RequestDelegate中间件,同时内部也会new一个HttpContext(features),这样Server、RequestDelegate、HttpContext三者就全部聚齐了。

public class HttpListenerServer : IServer

{

    private readonly HttpListener _httpListener;

    private readonly string[] _urls;

    public HttpListenerServer(params string[] urls)

    {

        _httpListener = new HttpListener();

        _urls = urls.Any()?urls: new string[] { "http://localhost:5000/"};

    }

    public async Task StartAsync(RequestDelegate handler)

    {

        Array.ForEach(_urls, url => _httpListener.Prefixes.Add(url));   

        _httpListener.Start();

        Console.WriteLine("Server started and is listening on: {0}"string.Join(';', _urls));

        while (true)

        {

            var listenerContext = await _httpListener.GetContextAsync();

         Console.WriteLine("{0} {1} HTTP/{2}",

                    listenerContext.Request.HttpMethod,

                    listenerContext.Request.RawUrl,

                    listenerContext.Request.ProtocolVersion);

                var feature = new HttpListenerFeature(listenerContext);

                var features = new FeatureCollection()

                    .Set<IHttpRequestFeature>(feature)

                    .Set<IHttpResponseFeature>(feature);

                var httpContext = new HttpContext(features);

                Console.WriteLine("[Info]: Server process one HTTP request start.");

                await handler(httpContext);

                Console.WriteLine("[Info]: Server process one HTTP request end.");

                listenerContext.Response.Close();

        }

    }

}

public static partial class Extensions

{

    public static IWebHostBuilder UseHttpListener(this IWebHostBuilder builder, params string[] urls)

    => builder.UseServer(new HttpListenerServer(urls));

  通过以上代码分析,我们可以画个图做总结:

  • 适配器模式

由于ASP.NET Core可以支持不同的WebServer,比如Kestrel和IIS,不同的WebServer返回的HttpContext各不相同,所以这里又增加了一个中间层进行适配。这个中间层是什么呢?如下图所示,就是IRequestFeature和IResponseFeature。这一层是典型的适配器模式。

  这里重点讲解的7大对象,这个适配器模式的实现细节暂且略过。

6.WebHost

public interface IWebHost

{

    Task StartAsync();

}

  根据这段定义,我们只能知道简单知道WebHost只要是用来启动什么对象用的,具体什么对象似乎都可以。直到我们看了实现,如下代码所示:

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);

}

  通过StartAsync,我们知道WebHost是用来启动管道的中间件的,管道是在作为应用宿主的WebHost对象启动的时候被构建出来的。

  而WebHost是如何被创建的呢?接下来就要讲他的父亲WebHostBuilder

7.WebHostBuilder

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());

    }

}

  我们看到该对象有个Build方法,内部返回一个WebHost对象,这就是父亲的职责,负责生娃,他的娃就是WebHost。生出来的时候,给孩子一个ApplicationBuilder作为食物。而这个食物其实是包裹起来的,展开来看就是一个个RequestDelegate委托链。

public interface IWebHostBuilder

{

    IWebHostBuilder UseServer(IServer server);

    IWebHostBuilder Configure(Action<IApplicationBuilder> configure);

    IWebHost Build();

}

  父亲除了创建WebHost之外,他还提供了注册服务器的UseServer方法和用来注册中间件的Configure方法。说到Configure方法,我们一定还记得ApplicationBuilder方法的Use也是一个注册器。这两个注册器有何不同呢?我们对比一下代码:

  • WebHostBuilder

public IWebHostBuilder Configure(Action<IApplicationBuilder> configure)

{

    _configures.Add(configure);

    return this;

}

  • ApplicationBuilder

public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)

{

    _middlewares.Add(middleware);

    return this;

}

  其中Use只是增加一个中间件,Configure输入的是中间件构成的委托链。我们看下入口函数的代码就知道了:

public static async Task Main()

{

    await new WebHostBuilder()

        .UseHttpListener()

        .Configure(app => app

            .Use(FooMiddleware)

            .Use(BarMiddleware)

            .Use(BazMiddleware))

        .Build()

        .StartAsync();

}

参加文章:

  • 深入研究 Mini ASP.NET Core

  • 一个Mini的ASP.NET Core框架的实现

  • 200行代码,7个对象——让你了解ASP.NET Core框架的本质

原文地址:https://www.cnblogs.com/jackyfei/p/10838586.html

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

解读大内老A的《.NET Core框架本质》相关推荐

  1. ASP.NET Core 框架本质学习

    本文作为学习过程中的一个记录. 学习文章地址: https://www.cnblogs.com/artech/p/inside-asp-net-core-framework.html 一. ASP.N ...

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

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

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

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

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

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

  5. 老张 .NetCore与Vue 框架学习

    缘起 作为一个.Net攻城狮已经4年有余了,一直不温不火,正好近来项目不是很忙,闲得无聊,搞一搞新技术,一方面是打发无聊的时间,一方面也是督促自己该学习辣!身边的大神都转行的转行,加薪的加薪,本人比较 ...

  6. ASP.NET Core 框架源码地址

    ASP.NET Core 框架源码地址 https://github.com/dotnet/corefx 这个是.net core的 开源项目地址 https://github.com/aspnet  ...

  7. ML之xgboost:解读用法之xgboost库的core.py文件中的get_score(importance_type=self.importance_type)方法

    ML之xgboost:解读用法之xgboost库的core.py文件中的get_score(importance_type=self.importance_type)方法 目录 xgboost之skl ...

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

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

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

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

最新文章

  1. mysql select语句详解_mysql学习笔记之完整的select语句用法实例详解
  2. 【tensorflow】重置/清除计算图
  3. lazada的产品搜索排名有哪些影响因素,怎样做排名优化提升?
  4. C语言煎饼排序Pancake sort算法(附完整源码)
  5. 数组赋值给vector和list,顺便说明int和size_t的区别
  6. 今年暑假不AC-贪心
  7. 多项式相关操作学习笔记
  8. 一个简单的调用动态库的实例
  9. K8S_Google工作笔记0006---通过kubeadm方式_部署master节点
  10. tushare 安装
  11. IT餐馆—第一回 前言
  12. Linux文件目录与路径
  13. aws lambda_AWS API Gateway和AWS Lambda示例
  14. 20191201每日一句
  15. 个股和股票池的beta系数的估算
  16. 大庆铁人精神与时俱进 石油石化行业如何利用ICT基础设施驱动价值创造?
  17. java中的空指针异常处理
  18. ORBSLAM2论文翻译
  19. TI官网注册账号一直卡人机识别问题
  20. 不得不说!这个让我受益良多的思维利器!

热门文章

  1. 无法将类中的构造器应用到给定类型
  2. 天气很热,用Python告诉你奶茶哪家最好喝性价比最高?
  3. 汉语计算机语言,从计算机编程语言说汉语的比较优势
  4. 让你心动的20句励志文案
  5. 京东模拟扫码登陆破解-爬虫
  6. 明年债券收益率有望延续下行的趋势
  7. # IE浏览器打不开网页,google浏览器能打开
  8. serenity+jbehave
  9. mobile safari下 overflow:auto无效的解决方法
  10. ribbonmq超时配置_使用RabbitMQ实现订单超时取消(延迟队列)