【橙子老哥】.NetCore 管道模型源码深度解读

news2024/10/6 14:46:09

hello,大家好,今天又是橙子老哥的分享时间,希望大家一起学习,一起进步。

欢迎加入.net意社区,第一时间了解我们的动态,地址:ccnetcore.com

最近遇到很多小伙伴们问我,自己会.netframework想学.netcore,到底难不难,思考一下,其实基本大同小异,区别比较大的,主要是.netcore的管道模型

所以这篇文章,我们针对管道模型来玩一下,来看看它到底是个啥?

1、理论

说到管道模型,那我就不得不搬出下面这种经典图了:
在这里插入图片描述
.netcore的管道模型,类似一个俄罗斯套娃,从请求进来到请求出去,依次按照我们添加管道模型的顺序去执行

它的本质是属于一种特殊的 责任链 设计模式,不熟悉的人可以多去了解一下这个设计模式,真的很实用,主要是用于处理大量的if else等流程类型的业务,平替面向过程的不二之选

对于http请求,用管道模型,可谓是再合适不过了。

同时,每一个管道,又可以叫做中间件,对请求做了一个aop的操作,不就是中间件做的事情吗?很多中间件的功能都可以直接做成一个nuget包,引入到程序内部的管道模型中。微软考虑了这一点,也是用心了。

至于管道模型如何使用,网上资料很多,这里就不赘述了,本文主要以底层原理如何实现为主。

2、源码实现

入口:
在创建一个.netcore的web api程序,我们可以在program中build之后添加自己的管道

var app = builder.Build();
app.Use((context,next) =>
{
    Console.WriteLine("执行管道1");
    return next();
});
app.Use((context,next) =>
{
    Console.WriteLine("执行管道2");
    return next();
});

我们往use里面看看,我们自定义的管道模型是如何跑到进去的

	WebApplication:
	
    public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
    {
      this.ApplicationBuilder.Use(middleware);
      return (IApplicationBuilder) this;
    }

调用的WebApplication.use方法,走到this.ApplicationBuilder

如果熟悉.net5的伙伴们,肯定清楚WebApplication是后来新增的类,其实就是对ApplicationBuilder等元素包了一层,更方面我们使用,所以我们看看ApplicationBuilder内,use是做了什么

	ApplicationBuilder:

    public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
    {
        _components.Add(middleware);
        _descriptions?.Add(CreateMiddlewareDescription(middleware));

        return this;
    }

好像,没有做什么,只是把传入的Func<RequestDelegate, RequestDelegate> middleware一个委托丢到了_components,临时存储了起来而已

那我们刚好看看,存起来之后,如在哪里使用了,看到它的build方法,核心

    public RequestDelegate Build()
    {
        RequestDelegate app = context =>
        {
            // If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened.
            // This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware.
            var endpoint = context.GetEndpoint();
            var endpointRequestDelegate = endpoint?.RequestDelegate;
            if (endpointRequestDelegate != null)
            {
                var message =
                    $"The request reached the end of the pipeline without executing the endpoint: '{endpoint!.DisplayName}'. " +
                    $"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " +
                    $"routing.";
                throw new InvalidOperationException(message);
            }

            // Flushing the response and calling through to the next middleware in the pipeline is
            // a user error, but don't attempt to set the status code if this happens. It leads to a confusing
            // behavior where the client response looks fine, but the server side logic results in an exception.
            if (!context.Response.HasStarted)
            {
                context.Response.StatusCode = StatusCodes.Status404NotFound;
            }

            // Communicates to higher layers that the request wasn't handled by the app pipeline.
            context.Items[RequestUnhandledKey] = true;

            return Task.CompletedTask;
        };

        for (var c = _components.Count - 1; c >= 0; c--)
        {
            app = _components[c](app);
        }

        return app;
    }

这里其实就是管道模型的核心,看起来很多,其实大部分都是在做校验,我们简化一下

    public RequestDelegate Build()
    {
        RequestDelegate app = context =>return Task.CompletedTask;
        //倒叙遍历
        for (var c = _components.Count - 1; c >= 0; c--)
        {
        	//_components是我们之前新增的中间件存储的地方
            app = _components[c](app);
        }
        return app;
    }

哈哈,是不是一目了然,先创建一个初始化的节点,将我们之前传入的_components中间件委托进行一个倒叙的遍历

不过,到这里,我们的中间件并不会执行,这里很巧妙,由于我们传入的是Func<RequestDelegate, RequestDelegate> middleware,委托入参、委托出参,传入初始化的委托RequestDelegate app,返回的还是一个委托,刚好通过倒叙遍历的方式,将执行顺序调转回来

所以这个方法返回的是public RequestDelegate Build()RequestDelegate,只有在真正去执行RequestDelegate的地方才会去真正执行

这里刚好和我们的管道模型执行流程对应上了,我们给管道模型新增中间件的时候,并不会执行,而只有到有请求进来的时候,才会执行这个委托

然后呢?没了。。管道模型看起来复杂,其实真的很简单,实现起来就是一个集合一个倒叙遍历

扮猪吃老虎?并不是,我们只是站在巨人的肩膀上。
不是因为它看起内容少,所以简单;而是因为它设计的巧妙,所以看起来内容少简单

3、扩展

学以致用,管道模型不光只是在启动的时候使用,可以看到.netcore源码内,有着非常非常多的管道,每个管道的职责不一样,我们当然可以通过自己创建管道,来解决我们的业务,下面自定义管道模型使用:

1:中间件接口
首先,我们定义一个中间件接口 IMiddleware。

public interface IMiddleware
{
    Task InvokeAsync(HttpContext context, Func<HttpContext, Task> next);
}

HttpContext 类
接下来,我们定义一个简单的 HttpContext,用于存储请求上下文信息。

public class HttpContext
{
    public string Request { get; set; }
    public string Response { get; set; }
}

示例中间件
我们创建几个示例中间件来处理请求。

public class Middleware1 : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, Func<HttpContext, Task> next)
    {
        Console.WriteLine("Middleware1 before");
        await next(context);
        Console.WriteLine("Middleware1 after");
    }
}

public class Middleware2 : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, Func<HttpContext, Task> next)
    {
        Console.WriteLine("Middleware2 before");
        await next(context);
        Console.WriteLine("Middleware2 after");
    }
}

中间件管道
接下来,我们要创建一个管道类,负责执行中间件。

public class MiddlewarePipeline
{
    private readonly List<IMiddleware> _middlewares = new List<IMiddleware>();

    public void Use(IMiddleware middleware)
    {
        _middlewares.Add(middleware);
    }

    public async Task ExecuteAsync(HttpContext context)
    {
        Func<HttpContext, Task> next = null;

        // 构建next委托
        next = async (ctx) =>
        {
            // 如果还有中间件未执行,则继续下一个中间件
            var index = 0;
            await ExecuteMiddleware(ctx, index);
        };

        await next(context);
    }

    private async Task ExecuteMiddleware(HttpContext context, int index)
    {
        if (index < _middlewares.Count)
        {
            var middleware = _middlewares[index];
            await middleware.InvokeAsync(context, async (ctx) =>
            {
                // 调用下一个中间件
                await ExecuteMiddleware(ctx, index + 1);
            });
        }
    }
}

使用示例
最后,我们使用以上创建的管道和中间件。

class Program
{
    static async Task Main(string[] args)
    {
        var pipeline = new MiddlewarePipeline();

        pipeline.Use(new Middleware1());
        pipeline.Use(new Middleware2());

        var context = new HttpContext { Request = "Request Data" };

        await pipeline.ExecuteAsync(context);
    }
}

运行结果

Middleware1 before
Middleware2 before
Middleware2 after
Middleware1 after

学会这个,速速将屎山重构吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2192145.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【电力系统】Matlab|含风电-光伏-光热电站电力系统N-k安全优化调度模型

摘要 本文提出了一种结合风电、光伏与光热电站的电力系统N-k安全优化调度模型。通过在电力系统中集成多种可再生能源发电技术&#xff0c;优化不同类型电源的调度策略&#xff0c;确保在N-k故障情景下系统的稳定运行。基于Matlab仿真&#xff0c;本文分析了可再生能源发电的功…

路由:ReactRouter

概述 一个路径path对应一个组件component 当我们在浏览器中访问一个path的时候&#xff0c;path对应的组件会在页面中进行渲染。 使用 快速开始 安装依赖 npm i react-router-dom基本使用 import { createBrowserRouter, RouterProvider } from react-router-domconst ro…

【JavaEE初阶】多线程案列之定时器的使用和内部原码模拟

前言&#xff1a; &#x1f308;上期博客&#xff1a;【JavaEE初阶】深入理解多线程阻塞队列的原理&#xff0c;如何实现生产者-消费者模型&#xff0c;以及服务器崩掉原因&#xff01;&#xff01;&#xff01;-CSDN博客 &#x1f525;感兴趣的小伙伴看一看小编主页&#xff1…

房地产销售|基于springBoot的房地产销售管理系统设计与实现(附项目源码+论文+数据库)

私信或留言即免费送开题报告和任务书&#xff08;可指定任意题目&#xff09; 目录 一、摘要 二、相关技术 三、系统设计 四、数据库设计 五、核心代码 六、论文参考 七、源码获取 一、摘要 社会和科技的不断进步带来更便利的生活&#xff0c;计算机技术也越来…

fiddler抓包18-2_导出jmeter、postman脚本(带请求头)

课程大纲 1. Fiddler导出请求为curl脚本 选中请求&#xff0c;“文件” - “导出会话” - “选中的会话” - “cURL Script”。 2. 导入jmeter ① 复制curl脚本。 ② 打开jmeter&#xff0c;“工具” - “import from cURL”&#xff0c;粘贴脚本&#xff0c;勾选“Add cooki…

二分查找一>寻找峰值

1.题目&#xff1a; 2.解析&#xff1a; 暴力遍历代码&#xff1a;O(N),由于该题数据很少所以可以通过 暴力遍历&#xff1a;O(N),由于该题数据很少所以可以通过int index 0;for(int i 1; i < nums.length-1; i) {//某段区域内一直递增&#xff0c;更新就indexif(nums[i]…

codetop标签树刷题(三)!!暴打面试官!!!!

用于个人复习 1.子结构判断2.寻找重复的子树3.相同的树4.平衡二叉树5.二叉树展开为链表6.将二叉搜索树转化为排序的双向链表7.验证二叉搜索树8.二叉树的完全性检验9.完成二叉树的节点个数10.删除二叉搜索树中的节点11.寻找二叉树中的目标节点 1.子结构判断 给定两棵二叉树 tre…

Libtorch学习之Libtorch-VS2019-图像分割程序

文章目录 环境说明Pytorch 序列化Libtorch 下载VS配置主程序可能遇到的问题参考 环境说明 win10 VS2019 OPENCV4.7.0 Litorch1.13 Pytorch 1.12.1 Pytorch 序列化 import torch from torchvision.models import resnet50 net resnet50(pretrainedTrue) net net.cuda() net…

提升开机速度:有效管理Windows电脑自启动项,打开、关闭自启动项教程分享

日常使用Windows电脑时&#xff0c;总会需要下载各种各样的办公软件。部分软件会默认开机自启功能&#xff0c;开机启动项是指那些在电脑启动时自动运行的程序和服务。电脑开机自启太多的情况下会导致电脑卡顿&#xff0c;开机慢&#xff0c;运行不流畅的情况出现&#xff0c;而…

如何从计算机的硬盘中恢复照片 - 成功

如何从计算机硬盘恢复图片&#xff1f; 与所有电子和机械设备一样&#xff0c;硬盘驱动器也可能由于任何原因而死机。如果您的系统硬盘驱动器已停止工作或在启动系统时听到振动声&#xff0c;则它有可能已死机。如果是这样的话&#xff0c;上面的数据呢&#xff1f; 不要惊慌…

十二、血条UI

一、制作血条UI 注&#xff1a;一般不用Slider制作血条&#xff1b;而是用两个Image制作&#xff0c;选择为填充 使用Slider滑动条制作UI 人物血条&#xff1a;背景深绿色&#xff1b;滑条浅绿色 在场景中的画布选择为OverLay 敌人血条&#xff1a; 在预制体里面制作&#x…

自动驾驶系列—自动驾驶背后的数据通道:通信总线技术详解与应用场景分析

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

最具有世界影响力的人颜廷利:全球著名哲学家思想家起名大师

颜廷利教授&#xff0c;这位源自济南唐王镇的杰出人物&#xff0c;不仅是中国当代最杰出的国学大师之一&#xff0c;更是将传统文化与现代科技巧妙结合的先锋。他积极推崇以人工智能技术为辅助的国学研究方法&#xff0c;为这一古老领域注入了新的活力和时代表达。 除了在学术…

【LeetCode】每日一题 2024_10_6 加油站(贪心)

前言 每天和你一起刷 LeetCode 每日一题~ 大家国庆节快乐呀~ LeetCode 启动&#xff01; 国庆第 6 天&#xff0c;在加油站 . . . 题目&#xff1a;加油站 代码与解题思路 今天这道题目是力扣上的经典贪心&#xff08;第 134 题&#xff09; func canCompleteCircuit(gas…

springboot中配置优先级

先来看在idea当中运行程序时&#xff0c;如何来指定Java系统属性和命令行参数。 系统属性 1、右键启动类&#xff0c;点击Edit Configuration 点击Modify options 选择Add VM options&#xff0c;就是系统属性 选择Program arguements&#xff0c;就是命令行参数 总结&#…

排查和解决JVM OOM实战

JVM OOM介绍 Java内存区域布局 下面的分析中都是基于JDK 8开始的。关于JMM不过多介绍每个区域的作用。OOM不单只会发生在堆内存&#xff0c;也可能是因为元空间或直接内存泄漏导致OOM&#xff0c;此时在OOM的详细信息中会有不同体现。 Java OOM的类别 java.lang.OutOfMemory…

CSS 布局——清除浮动 (二)

目录 1. 清除浮动 2. 清除浮动本质 3. 清除浮动 4. 清除浮动方法 4.1 额外标签法 4.1.1 总结 4.2 父级添加 overflow 4.3 after 伪元素法 4.4 双伪元素清除浮动 5 总结 1. 清除浮动 这是上面的源代码&#xff1a; <!DOCTYPE html> <html lang"en"&…

飞书消息转发

飞书是字节跳动开发的一个款即时通讯软件 不同与微信和钉钉&#xff0c;飞书是基于Electron的跨平台桌面客户端&#xff08;主要开发语言是JavaScript&#xff09;&#xff0c;程序运行在chrom内核中&#xff0c;所以HOOK方案不好使 针对Electron 框架&#xff0c;打包后的应用…

京东e卡滑块 分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 有相关问题请第一时间头像私信联系我删…

AI2.0时代,普通小白如何通过AI月入30万

最近这2年AI真的太火了&#xff0c;很多人都在讨论怎么用AI赚钱、提高效率。其实&#xff0c;我觉得AI并没有那么复杂&#xff0c;尤其是如果你不做AI底层研究&#xff0c;只是利用它来帮你省事、提效、赚钱&#xff0c;那就像当初学用电脑、用手机一样简单。你不需要懂AI的技术…