ASP.NET Core 中的管道机制

640?wx_fmt=png

首先,很感谢在上篇文章 C# 管道式编程 中给我有小额捐助和点赞的朋友们,感谢你们的支持与肯定。希望我的每一次分享都能让彼此获得一些收获,当然如果我有些地方叙述的不正确或不当,还请不客气的指出。好了,下面进入正文。

前言

在开始之前,我们需要明确的一个概念是,在 Web 程序中,用户的每次请求流程都是线性的,放在 ASP.NET Core 程序中,都会对应一个 请求管道(request pipeline),在这个请求管道中,我们可以动态配置各种业务逻辑对应的 中间件(middleware),从而达到服务端可以针对不同用户做出不同的请求响应。在 ASP.NET Core 中,管道式编程是一个核心且基础的概念,它的很多中间件都是通过 管道式 的方式来最终配置到请求管道中的,所以理解这里面的管道式编程对我们编写更加健壮的 DotNetCore 程序相当重要。

剖析管道机制

在上面的论述中,我们提到了两个很重要的概念:请求管道(request pipeline) 和 中间件(middleware)。对于它俩的关系,我个人的理解是,首先,请求管道服务于用户,其次,请求管道可以将多个相互独立的业务逻辑模块(即中间件)串联起来,然后服务于用户请求。这样做的好处是可以将业务逻辑层级化,因为在实际的业务场景中,有些业务的处理即相互独立,又依赖于其它的业务操作,各个业务模块之间的关系实际上是动态不固定的。

下面,我们尝试着来一步步解析 ASP.NET Core 中的管道机制。

理论解释

首先,我们来看一下官方的图例解释:

640?wx_fmt=png

从上图中,我们不难看出,当用户发出一起请求后,应用程序都会为其创建一个请求管道,在这个请求管道中,每一个中间件都会按顺序进行处理(可能会执行,也可能不会被执行,取决于具体的业务逻辑),等最后一个中间件处理完毕后请求又会以相反的方向返回给用户最终的处理结果。

代码阐释

为了验证上述我们的理论解释,我们开始创建一个 DotNetCore 的控制台项目,然后引用如下包:

  • Microsoft.AspNetCore.App

编写如下示例代码:

class Program{    static void Main(string[] args)    {        CreateHostBuilder(args).Build().Run();    }    private static IHostBuilder CreateHostBuilder(string[] args) =>        Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>        {            webBuilder.UseStartup<Startup>();        });}public class Startup{    public void Configure(IApplicationBuilder app)    {                app.Use(async (context, next) =>        {            Console.WriteLine("A (in)");            await next();            Console.WriteLine("A (out)");        });                app.Use(async (context, next) =>        {            Console.WriteLine("B (in)");            await next();            Console.WriteLine("B (out)");        });                app.Run(async context =>        {            Console.WriteLine("C");            await context.Response.WriteAsync("Hello World from the terminal middleware");        });    }}

上述代码段展示了一个最简单的 ASP.NET Core Web 程序,尝试 F5 运行我们的程序,然后打开浏览器访问 http://127.0.0.1:5000 会看到浏览器显示了 Hello World from the terminal middleware 的信息。对应的控制台信息如下图所示:

640?wx_fmt=png

上述示例程序成功验证了我们理论解释中的一些设想,这说明在 Configure 函数中成功构建了一个完成的请求管道,那既然这样,我们就可以将其修改为我们之前使用管道的方式,示例代码如下所示:

public class Startup{    public void Configure(IApplicationBuilder app)    {        app.Use(async (context, next) =>        {            Console.WriteLine("A (int)");            await next();            Console.WriteLine("A (out)");        }).Use(async (context, next) =>        {            Console.WriteLine("B (int)");            await next();            Console.WriteLine("B (out)");        }).Run(async context =>        {            Console.WriteLine("C");            await context.Response.WriteAsync("Hello World from the terminal middleware");        });    }}

这两个方式都能让我们的请求管道正常运行,只是写的方式不同。至于采用哪种方式完全看个人喜好。需要注意的是,最后一个控制台中间件需要最后注册,因为它的处理是单向的,不涉及将用户请求修改后返回。

同样的,我们也可以对我们的管道中间件进行条件式组装(分叉路由),组装条件可以依据具体的业务场景而定,这里我以路由为条件进行组装,不同的访问路由最终访问的中间件是不一样的,示例代码如下所示:

public class Startup{    public void Configure(IApplicationBuilder app)    {                app.Use(async (context, next) =>        {            Console.WriteLine("A (in)");            await next();            Console.WriteLine("A (out)");        });                app.Map(                new PathString("/foo"),                a => a.Use(async (context, next) =>                {                    Console.WriteLine("B (in)");                    await next();                    Console.WriteLine("B (out)");                }));                app.Run(async context =>        {            Console.WriteLine("C");            await context.Response.WriteAsync("Hello World from the terminal middleware");        });    }}

当我们直接访问 http://127.0.0.1:5000 时,对应的请求路由输出如下:

640?wx_fmt=png

对应的页面会回显 Hello World from the terminal middleware

当我们直接访问 httP://127.0.0.1:5000/foo 时,对应的请求路由输出如下:

640?wx_fmt=png

当我们尝试查看对应的请求页面,发现对应的页面却是 HTTP ERROR 404 ,通过上述输出我们可以找到原因,是由于最后一个注册的终端路由未能成功调用,导致不能返回对应的请求结果。针对这种情况有两种解决方法。

一种是在我们的 路由B 中直接返回请求结果,示例代码如下所示:

app.Map(    new PathString("/foo"),    a => a.Use(async (context, next) =>    {        Console.WriteLine("B (in)");        await next();        await context.Response.WriteAsync("Hello World from the middleware B");        Console.WriteLine("B (out)");    }));

这种方式不太推荐,因为它极易导致业务逻辑的不一致性,违反了 单一职责原则 的思想。

另一种解决办法是通过路由匹配的方式,示例代码如下所示:

app.UseWhen(    context => context.Request.Path.StartsWithSegments(new PathString("/foo")),    a => a.Use(async (context, next) =>    {        Console.WriteLine("B (in)");        await next();        Console.WriteLine("B (out)");    }));

通过使用 UseWhen 的方式,添加了一个业务中间件对应的业务条件,在该中间件执行完毕后会自动回归到主的请求管道中。最终对应的日志输出入下图所示:

640?wx_fmt=png

同样的,我们也可以自定义一个中间件,示例代码如下所示:

public class Startup{    public void Configure(IApplicationBuilder app)    {                        app.UseCustomMiddle();                app.Run(async context =>        {            Console.WriteLine("C");            await context.Response.WriteAsync("Hello World from the terminal middleware");        });    }}public class CustomMiddleware{    private readonly RequestDelegate _next;    public CustomMiddleware(RequestDelegate next)    {        _next = next;    }    public async Task InvokeAsync(HttpContext httpContext)    {        Console.WriteLine("CustomMiddleware (in)");        await _next.Invoke(httpContext);        Console.WriteLine("CustomMiddleware (out)");    }}public static class CustomMiddlewareExtension{    public static IApplicationBuilder UseCustomMiddle(this IApplicationBuilder builder)    {        return builder.UseMiddleware<CustomMiddleware>();    }}

日志输出如下图所示:

640?wx_fmt=png

由于 ASP.NET Core 中的自定义中间件都是通过 依赖注入(DI) 的的方式来进行实例化的。所以对应的构造函数,我们是可以注入我们想要的数据类型,不光是 RequestDelegate;其次,我们自定义的中间件还需要实现一个公有的 public void Invoke(HttpContext httpContext) 或 public async Task InvokeAsync(HttpContext httpContext) 的方法,该方法内部主要处理我们的自定义业务,并进行中间件的连接,扮演着 枢纽中心 的角色。

源码分析

由于 ASP.NET Core 是完全开源跨平台的,所以我们可以很容易的在 Github 上找到其对应的托管仓库。最后,我们可以看一下 ASP.NET Core 官方的一些实现代码。如下图所示:

640?wx_fmt=png

官方开源了内置中间件的全部实现代码,这里我以 健康检查(HeathChecks) 中间件为例,来验证一下我们上面说的自定义中间件的实现。

640?wx_fmt=png

通过查阅源码,我们可以看出,我们上述自定义的中间件是符合官方的实现标准的。同样的,当我们以后使用某个内置中间件时,如果对其具体实现感兴趣,可以通过这种方式来进行查看。

总结

当我们对 ASP.NET Core 的请求管道进行中间件配置的时候,有一个地方需要注意一下,就是中间件的配置一定要具体的业务逻辑顺序进行,比如网关配置一定要先于路由配置,结合到代码就是下述示例:

public void Configure(IApplicationBuilder app, IHostingEnvironment env){        app.UseAuthentication();        app.UseMvc();}

如果当我们的中间件顺序配置不当的话,极有可能导致相应的业务出现问题。

就 ASP.NET Core 的技术架构而言,管道式编程只是其中很小很基础的一部分,整个技术框架设计与实现,用到了很多优秀的技术和架构思想。但是这些高大上的实现都是基于基础技术衍化而来的,所以,基础很重要,只有把基础打扎实了,才不会被技术浪潮所淘汰。

上述所有内容就是我个人对 ASP.NET Core 中的管道式编程的一些理解和拙见,如果有不正确或不当的地方,还请斧正。

望共勉!

相关参考

  • ASP.NET Core Middleware

  • UNDERSTANDING THE ASP.NET CORE MIDDLEWARE PIPELINE

  • ASP.NET Web API标准的“管道式”设计

原文:https://www.cnblogs.com/hippieZhou/p/11205573.html


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

640?wx_fmt=jpeg

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

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

相关文章

微软宣布SQL Server 2019免费支持Java

在 2018 年 9 月的时候&#xff0c;微软就宣布与领先的 Java 开源贡献者和发行商 Azul Systems 建立新的合作伙伴关系。这一关系允许所有 Azure 客户在微软和 Azul 联合提供的支持下&#xff0c;免费使用 Azul 的 Zulu for Azure-Enterprise Java 发行版 。最近&#xff0c;微软…

P4231 三步必杀 二次差分

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 考虑给[2,6][2,6][2,6]加上s2,e10s2,e10s2,e10的等差数列&#xff0c;变成2,4,6,8,102,4,6,8,102,4,6,8,10&#xff0c;考虑差分数组2,2,2,2,2,−102,2,2,2,2,-102,2,2,2,2,−10&#xff0…

创新的迷思——2019 Microsoft Hackathon 苏州站回顾

每年&#xff0c;微软在7月份都会在内部举办一次黑客松&#xff08;Hachathon&#xff09;活动&#xff0c;所有的员工&#xff0c;包括实习生都可以自由组队参赛&#xff0c;在为期一周的时间内完成构想、设计和开发的一系列过程&#xff0c;并且在最后一天进行项目路演和评选…

P4062 [Code+#1]Yazid 的新生舞会 树状数组维护三阶差分

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 给你一个序列aaa&#xff0c;让你求有多少个子区间满足存在一个数是这个区间的绝对众数&#xff0c;绝对众数指该数在区间内出现的次数严格大于r−l12\frac{r-l1}{2}2r−l1​。 n≤5e5,0≤ai≤n−1n\le5e5,0…

一句话概括4本管理著作

之前618活动的时候买了不少书&#xff0c;最近一段时间集中看了四本管理相关的书籍&#xff0c;收获颇丰。在这里分享给大家。如果你不是管理者也没关系&#xff0c;可以换个视角来看看管理者眼中的世界&#xff0c;毕竟&#xff0c;“赤兔马”也需要让“伯乐”看到才行嘛。虽然…

[译].Net中的内存-什么分配在了哪里

原文链接&#xff1a;https://jonskeet.uk/csharp/memory.html人们在理解值类型和引用类型之间的差异时因为“值类型在栈上分配&#xff0c;引用类型在堆上分配”这句话造成了很多混乱。这完全是不对的&#xff0c;本文试图澄清这个问题。变量中有什么&#xff1f;理解.NET中内…

上车时机已到--.NETCore是适应时代发展的雄鹰利剑

要起飞了.NET Core 3.0-prevew7&#xff1a;https://dotnet.microsoft.com/download/dotnet-core/3.0随着 .NET Core 3.0-prevew7 的发布&#xff0c;开源社区的一支重要力量重要即将起飞&#xff1b;官方指出&#xff0c;该预览版为可能为最终版本&#xff0c;在很长一段时间内…

HDU - 7028 Decomposition 无向完全图构造欧拉回路

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 给你一张无向完全图&#xff0c;让你构造kkk个长度分别为lil_ili​的路径&#xff0c;这些路径不相交&#xff0c;且∑lin∗(n−1)2\sum l_i\frac{n*(n-1)}{2}∑li​2n∗(n−1)​。 n≤1000,nmod21n\le1000,…

「Azure」数据分析师有理由爱Azure之二-立即申请帐号开始学习之旅

目前关于Azure的学习资料不多&#xff0c;除了官方的文档和Microsoft Learn频道外&#xff0c;几乎没有什么中文性资料可学习&#xff0c;就算有&#xff0c;也是以IT的思维方式来展开介绍&#xff0c;对没有IT背景的数据分析师来说&#xff0c;非常难于适应。本篇给大家带来一…

矩阵快速幂的最简单用法

矩阵快速幂 链接&#xff1a;https://ac.nowcoder.com/acm/contest/1168/K 来源&#xff1a;牛客网 题目描述 这个勇者明明超强却过分慎重&#xff0c;勇者龙宫院圣哉与n名冒险者一起去讨伐神秘魔物&#xff0c;龙宫院圣哉十分谨慎&#xff0c;他只会在最后一刻出手&#xff…

DevOps案例研究|史上最能“拜客户教”的公司,是如何做到持续交付的?(第1趴)...

内容来源&#xff1a;DevOps案例深度研究 –Amazon持续交付之道战队&#xff08;本文只展示部分PPT及研究成果&#xff0c;更多细节请关注案例分享会&#xff0c;及本公众号。&#xff09;本案例内容贡献者&#xff1a;单冰 (Topic Leader)、 赵栋、梁兴龙、李杰、毛艳清、牛恒…

P2261 [CQOI2007]余数求和 整除分块

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; n,k≤1e9n,k\le1e9n,k≤1e9 思路&#xff1a; 考虑转换式子&#xff0c;∑i1nkmodi∑i1n(k−⌊ki⌋∗i)n∗k−∑i1n⌊ki⌋∗i\sum_{i1}^{n}k\bmod i\sum_{i1}^n(k-\left \lfloor \frac{k}{i} \right \rfloo…

背包问题——第一篇

一&#xff0c;01背包 最简单也是最经典的背包问题。 首先我们知道背包问题是一种d问题&#xff0c;最重要的就是要去找到他的状态转移方程。而在01背包中转移方程就比较简单了&#xff0c;这里用一个二维数组进行标表示。 ans[i][j]max(ans[i-1][j],ans[i-1][j-v[i]w[i]); 在…

小白开学Asp.Net Core 《十》

小白开学Asp.Net Core 《十》 — — Session、Cookie、Cache&#xff08;老生常谈&#xff09;一、背景在常谈Session和Cookie之前我们先来简单的了解下Http&#xff08;可以说这是必须的&#xff0c;默认大家都了解&#xff09;总结一句话&#xff1a;HTTP是一种无状态的协议&…

P3935 Calculating 整除分块

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 我们设s(x)∑i1nf(x)s(x)\sum_{i1}^nf(x)s(x)∑i1n​f(x)&#xff0c;那么答案就是s(r)−s(l−1)s(r)-s(l-1)s(r)−s(l−1)。 容易发现&#xff0c;我们要求的f(x)f(x)f(x)实际上就是xxx的…

Sticks-hdu-1455深度搜索dfs

Sticks 题目传送门。 题目大意是&#xff0c;给你一个数字n代表总共的棍子数量&#xff0c;要做的就是&#xff0c;把这几根棍子拼成长度相同的棍子&#xff0c;并且让所拼成的棍子的长度尽可能地小&#xff0c;也就是拼成的棍子的数量尽可能的多。 在这里很简单的想到要给棍…

gRPC in ASP.NET Core 3.0 -- Protocol Buffer(1)

开发环境&#xff1a;IDE: VSCodeVSCode的扩展插件&#xff1a;vscode-proto3和Clang-Format这两个扩展Windows还需要安装Clang&#xff0c;Windows 64位系统的地址如下&#xff1a;Clang for Windows (64-bit)(http://llvm.org/releases/3.7.0/LLVM-3.7.0-win64.exe)&#xff…

[蓝桥杯2016初赛]方格填数

正如题目&#xff0c;我们要保证相邻的数不相连就行了&#xff0c;一共要填十个数。 下面是我的思路&#xff0c;供大家参考一下&#xff0c; 我把上面的各子放进了这个5*6的矩形&#xff0c;其中标记1–10的分别是要填数的各格子&#xff0c;一开始我先初始化这个二维数组为全…

2021牛客暑期多校训练营7 xay loves monotonicity 线段树区间合并

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 题面挺绕口的&#xff0c;还是看原题比较好。 大概的意思就是让你从给定的区间中选择一个以左端点为起点的一个上升子序列&#xff0c;让后将这些下标存下来&#xff0c;在bbb中将这些位置拿出来后&#xff…

容器化单页面应用中Nginx反向代理与Kubernetes部署

在《容器化单页面应用中RESTful API的访问》一文中&#xff0c;我介绍了一个在容器化环境中单页面应用访问后端服务的完整案例。这里我将继续使用这个案例&#xff0c;介绍一下容器化单页面应用部署的另一个场景&#xff1a;将Nginx的职责独立出来。注&#xff1a;这里单页面应…