1. 请求管道

请求管道是什么?请求管道描述的是一个请求进到我们的后端应用,后端应用如何处理的过程,从接收到请求,之后请求怎么流转,经过哪些处理,最后怎么返回响应。请求管道就是一次请求在后端应用的生命周期。了解请求管道,有助于我们明白后端应用是怎么工作的,我们的代码是怎么工作的,在我们的业务代码执行前后经过哪些步骤,有助于我们之后更好的实现一些AOP操作。

请求管道是 .net 应用的一个最基本的概念。在 .net core 中,微软对框架底层进行了全新的设计,相对于原本的ASP.NET中的全家桶模式的管道模型,.net core的管道模型更加灵活便捷,可做到热插拔,通过管道可以随意注册自己想要的服务或者第三方服务插件,这也是.net core性能更好的原因。

以上是微软官方文档中的管道模型图。从图中可以看到 服务器接收到请求之后,将接收到的请求向后传递,依次经过一个个 Middleware 进行处理,然后由最后一个 MiddleWare 生成响应内容并回传,再反向依次经过每一个 Middleware,直到由服务器发送出去。整个过程就像一条流水线一样,管道这个词是很形象的,而 Middleware 就像一层一层的“滤网”,过滤所有的请求和响应。

2. 中间件

管道之中,对请求、响应进行加工处理的模块是 Middleware,也就是中间件。中间件本质上是一个委托。

2.1 工作模式

从上面的图可以看出,每一个中间件都会被执行两次,在下一个中间件执行之前和之后各执行一次,分别是在处理请求和处理响应,只有一个中间件是例外的,那就是最后一个中间件,它后面没有下一个中间件,所以执行到它管道就会回转。

这代表了中间件的两种工作模式,也是中间件的两种基本注册方式。中间件本质上是一个委托,在代码实现上就体现在委托的入参有所不同以及注册调用的方法不同。

中间件两种最基本的注册方式:

  • Use 方法注册

    • use 注册的中间件会传入next参数,在处理完本身的逻辑之后可以调用 next() 去执行下一个中间件
    • 如果不执行,就等于Run
  • Run 方法注册
    • Run 只是执行,没有去调用Next ,一般作为终结点。
    • Run 方法注册,只是一个扩展方法,最终还是调用Use方法

在代码中分别是以下方式:

app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello Middlerware !");
if(context.Request.Query.TryGetValue("query", out var query))
{
await context.Response.WriteAsync(query);
}
await next();
await context.Response.WriteAsync("End Middleware !");
}); app.Run(async context =>
{
await context.Response.WriteAsync("Hello last Middleware");
});

最后的执行结果如下,也可以从代码执行的先后顺序看出管道流动的顺序。当前中间件手动调用 next() 之后,就进入下一个中间件,下一个中间件处理完成之后,按照管道的顺序再一个一个回传。在这个过程中一直不变,被管道传递的就是HttpContext,而我们拿到 HttpContext,即可以通过 Request 和 Response 对当前这一次的请求做任何处理了。

通过分析asp.net core的源码,可以看到在我们调用 Run() 的时候,实际上还是调用了 Use() 方法。

而 Use() 方法中,主要的逻辑仅仅只是将相应的委托存放到集合中

之后在 build 方法调用的时候才一个一个地调用中间件委托。

除了上面的 Use() 、Run() 两个最基本的方法注册中间件之外,还有另外一些方法,如通过 Map() 方法注册中间件,这种方式会创建一个新的管道分支,在路由满足Map的规则时,请求则转型新的管道分支,最后沿着管道分支返回响应,而不走原有的管道。

app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello Middlerware1 ! ");
if(context.Request.Query.TryGetValue("query", out var query))
{
await context.Response.WriteAsync(query);
}
await next();
await context.Response.WriteAsync("End Middleware1 ! ");
}); app.Map("/map", app =>
{
// map方法中的委托,传入的时IApplicationBuilder, 在这里相当于一个新的管道,也可以和主管道一样进行任意操作
app.Run(async context =>
{
await context.Response.WriteAsync("Hello map Middleware pipeline ! ");
});
}); app.Run(async context =>
{
await context.Response.WriteAsync("Hello last Middleware ! ");
});

执行结果如下:

其他的分支管道创建方式,如 MapWhen,和 Map 大同小异,只是对于匹配判断的方式有所不同。像微软内置中间件中的静态文件中间件,MVC 中间件,其实都是以分支管道的方式实现的,一旦匹配到请求就会走管道分支。

2.2 中间件的使用配置

使用一个中间件需要在 .net core 的入口文件中进行配置,如果是 .net 6版本,那只要在 program.cs 文件中进行配置即可,通过 WebApplication 对象,也就是 app 调用相关的方法。

如果是 .net 6 以下版本,可以在 startup.cs 文件中的 Configure 方法中配置。.net 6 与之前版本入口文件的不同上一篇文章也讲过,这里就不赘述了。

这里可以看得到,一些中间件的调用并没有直接使用 Use() 和 Run(),毕竟将各个中间件的处理逻辑全部放在入口文件很不好管理,而且也很不优雅。这里涉及到了中间件封装的约定规则,一般情况下封装一个中间件都会提供一个 Use[Middleware] 方法以供使用者进行中间件的调用,WebApplication 对象的 UseXXX 方法都是中间件调用的方法。

2.3 ASP.NET Core 框架内置中间件

ASP.NET Core 框架之中内置有很多中间件,并且我们通过 VS 创建某一个类型的项目时,如MVC、Razor Page,初始化的项目代码中会帮我们配置好一些中间件。

以上为官方文档中列出的内置中间件,可以看到在列表中对每个中间件的顺序进行了说明。

管道中的中间件排列是有先后之分的,请求和响应按照中间件的排列顺序进行传递,这也是我们代码逻辑执行的顺序,而且一些中间件需要依赖于其他中间件的处理结果,或者必须在某些中间件前先执行,否则就会出问题了。

而中间件插入到管道中的顺序,就是依据我们在入口文件中调用相应中间注册方法的顺序,所以代码的前后顺序非常重要,一旦写错了就会出现很多意想不到的的Bug。

向 Program.cs 文件中添加中间件组件的顺序定义了针对请求调用这些组件的顺序,以及响应的相反顺序。 此顺序对于安全性、性能和功能至关重要。这是官方文档中的原话。

官方文档给出了典型MVC应用的管道中间件顺序,这里其实不止MVC,Razor Page、Web Api 也是这样的管道模型,如下图。这里也明确了我们自定义的中间件应该插入到哪个位置。

更多的内置中间件的作用,以及相应的管道顺序要求,请详细阅读一下官方文档,这里就不细说了。

参考文章:

ASP.NET Core 中间件

ASP.NET Core 系列:

目录:ASP.NET Core 系列总结

上一篇: ASP.NET Core - .NET 6 的入口文件

ASP.NET Core - 请求管道与中间件的更多相关文章

  1. asp.net core mvc 管道之中间件

    asp.net core mvc 管道之中间件 http请求处理管道通过注册中间件来实现各种功能,松耦合并且很灵活 此文简单介绍asp.net core mvc中间件的注册以及运行过程 通过理解中间件 ...

  2. 配置 ASP.NET Core 请求(Request)处理管道

    配置 ASP.NET Core 请求(Request)处理管道 在本节中,我们将讨论使用中间件组件为 asp.net core 应用程序配置请求处理管道. 作为应用程序启动的一部分,我们要在Confi ...

  3. ASP.NET Core HTTP 管道中的那些事儿

    前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...

  4. Asp.Net Core入门之自定义中间件

    什么是中间件? 这里引用官方解释: 中间件是用于组成应用程序管道来处理请求和响应的组件.管道内的每一个组件都可以选择是否将请求交给下一个组件.并在管道中调用下一个组件之前和之后执行某些操作.请求委托被 ...

  5. 在ASP.NET Core 中使用Cookie中间件

    在ASP.NET Core 中使用Cookie中间件 ASP.NET Core 提供了Cookie中间件来序列化用户主题到一个加密的Cookie中并且在后来的请求中校验这个Cookie,再现用户并且分 ...

  6. 用.Net Core控制台模拟一个ASP.Net Core的管道模型

    在我的上几篇文章中降到了asp.net core的管道模型,为了更清楚地理解asp.net core的管道,再网上学习了.Net Core控制台应用程序对其的模拟,以加深映像,同时,供大家学习参考. ...

  7. 在ASP.NET Core 中使用Cookie中间件 (.net core 1.x适用)

    在ASP.NET Core 中使用Cookie中间件 ASP.NET Core 提供了Cookie中间件来序列化用户主题到一个加密的Cookie中并且在后来的请求中校验这个Cookie,再现用户并且分 ...

  8. asp.net core 3.1 自定义中间件实现jwt token认证

    asp.net core 3.1 自定义中间件实现jwt token认证 话不多讲,也不知道咋讲!直接上代码 认证信息承载对象[user] /// <summary> /// 认证用户信息 ...

  9. Asp.Net HttpApplication请求管道与Session(二)

    Asp.Net 回话的创建与结束 LogHelper.LogHelper _log = new LogHelper.LogHelper(); /// <summary> /// 程序开始- ...

  10. Asp.Net HttpApplication请求管道与Session(一)

    1.请求处理顺序执行事件 /********************请求处理顺序执行事件**********************/ /// <summary> /// 请求入站 /// ...

随机推荐

  1. laravel 腾讯云短信发送

    public function tecentSms(Request $request) { $phone = $request->input("hiphone"); $app ...

  2. 我的Python基础(二)

    python包含6种内奸的序列:列表.元组.字符串.Unicode字符串.buffer对象和xrange对象 列表和元组的主要区别在于,列表可以修改,元组则不能. 索引: 使用负数索引时,最后一个元素 ...

  3. gin领域层:用户实体编写和值对象(初步)

    1.用户实体和值对象 2. 要做的事 3.常见的四层模型 4.Domin层 5.值对象

  4. Go语言核心36讲13

    我们已经讨论过了通道的基本操作以及背后的规则.今天,我再来讲讲通道的高级玩法. 首先来说说单向通道.我们在说"通道"的时候指的都是双向通道,即:既可以发也可以收的通道. 所谓单向通 ...

  5. python面试题常用语句

    一.比较与交换1.比较并输出大的 print(a if a>b else b) 2.交换两个元素 a,b = b,alist1[i],list[j]=list1[j],list[i] 二.排序 ...

  6. x=x+=x-=x-x;

    int x=10; x=x+=x-=x-x; // x=x+(x-(x-x)) System.out.println(x); 输出结果20

  7. 【每日一题】【map、数组、二维数组排序、静态函数和库函数】2022年2月24日-NC97 字符串出现次数的TopK问题

    描述给定一个字符串数组,再给定整数 k ,请返回出现次数前k名的字符串和对应的次数.返回的答案应该按字符串出现频率由高到低排序.如果不同的字符串有相同出现频率,按字典序排序.对于两个字符串,大小关系取 ...

  8. python3中的常见知识点2

    python3中的常见知识点2 列表与栈和队列 map()函数 python列表遍历的4种方式 参考链接 列表栈和队列 1.列表作为栈使用 栈:先进后出,First In Last Out 使用 ap ...

  9. 论文解读(PCL)《Probabilistic Contrastive Learning for Domain Adaptation》

    论文信息 论文标题:Probabilistic Contrastive Learning for Domain Adaptation论文作者:Junjie Li, Yixin Zhang, Zilei ...

  10. MySQL进阶实战7,查询的执行过程

    @ 目录 一.拆分查询 二.分解关联查询 三.查询的执行过程 四.优化器的一些优化手段 1.重新定义关联表的顺序 2.将外连接转化为内连接 3.使用增加变换规则 4.优化count().max().m ...