当前位置: 首页 > news >正文

【ASP.NET Core】ASP.NET Core中间件解析

文章目录

  • 一、何为中间件
  • 二、请求管道中的中间件
    • 2.1 请求管道中的中间件执行顺序
    • 2.2 请求委托
      • 2.2.1 请求委托的本质
      • 2.2.2 请求委托与中间件
  • 三、中间件的定义方式
    • 3.1 内联中间件(in-line middleware)
    • 3.2 类中间件(class middleware)
    • 3.3 工厂模式中间件(factory-based middleware)
  • 四、中间件与过滤器


一、何为中间件

中间件是通过组装到应用程序管道中以处理请求和响应,它们按顺序组成一个请求管道。当客户端发送HTTP请求到应用程序时,请求会依次经过管道中的中间件进行处理;当服务器生成响应后,响应会按相反的顺序再次经过这些中间件,最终返回给客户端。

每个中间件都包含两大特性:

  • 选择是否将请求传递给管道中的下一个组件。
  • 在下一个组件之前和之后执行特定的逻辑。

每个中间件本身是一个处理当前请求的逻辑的请求委托(Request Delegate),它通过接收下一个中间件的请求委托,实现请求的传递。整个中间件管道,本质是由一系列嵌套的请求委托组成的。

也就是说中间件的本质是一个嵌套的请求委托容器,通过接收下一个中间件的请求委托,返回当前中间件的请求委托。其中这个返回的请求委托中包含了对当前逻辑的执行和对next的调用。多个中间件按注册顺序组成请求管道,请求按注册顺序流经每个节点,响应则按相反顺序回流

二、请求管道中的中间件

2.1 请求管道中的中间件执行顺序

请求管道采用链式处理流程。执行流程如下图所示
在这里插入图片描述
当客户端发送HTTP请求到服务器时,请求会按顺序流经管道中的每个中间件,中间件在管道中的注册顺序决定了执行顺序。先注册的中间件先处理请求,后处理响应;

中间件可以在请求进入时和响应返回时分别执行逻辑。也可以在在特定情况下不调用next(),短路请求。

每个中间件可以:

  • 对请求进行处理,执行逻辑;
  • 决定是否将请求传递给下一个中间件,调用.next()执行下一个中间件,否则就短路后续的中间件执行;
  • 在响应返回时执行额外逻辑;

管道的最后一个终端中间件会生成响应,响应再按相反顺序流经之前的中间件,最终返回给客户端。

2.2 请求委托

前面聊过中间件的本质是接收下一个中间件的请求委托,返回当前中间件的请求委托。其中这个返回的请求委托中包含了对当前逻辑的执行和对next的调用。

2.2.1 请求委托的本质

请求委托是一个预定义的委托类型,本质上是一个可执行的函数,接收HttpContext,返回Task。其签名如下。

/// <summary>
/// A function that can process an HTTP request.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> for the request.</param>
/// <returns>A task that represents the completion of request processing.</returns>
public delegate Task RequestDelegate(HttpContext context);

中间件正是通过请求委托的形式来定义自己的处理逻辑,来对HTTP请求进行各种处理。也就是说中间件的核心逻辑最终会被包装成一个请求委托,然后被加入请求处理管道中。

每个中间件在注册时,会接收下一个中间件的请求委托,并返回一个包含自身逻辑的请求委托。

整个管道本质是一系列嵌套的请求委托:前一个中间件的委托中包含对后一个中间件委托的调用。

下面用一个具体的内联中间件为例。

  1. app.Map用来定义一个管道可以处理哪些请求
  2. appbuilder.Use()的参数是一个 接收next并返回请求委托的函数,这正是中间件的核心定义方式。
  3. 中间件1返回的请求委托中,包含了自身逻辑和对中间件2的请求委托(next)的调用。直到最后的终端中间件,无next调用,到达管道终点。

Program.cs定义一个内联中间件管道

app.Map("/test", async appbuilder =>
{// 中间件1:返回一个请求委托,内部包含对next(中间件2的委托)的调用appbuilder.Use(async (context, next) =>{context.Response.ContentType = "text/html";await context.Response.WriteAsync("<h1>Middleware 1 : Hello World</h1>");await next.Invoke();await context.Response.WriteAsync("<h1>Middleware 1 : Goodbye World</h1>");});// 中间件2:返回一个请求委托,内部包含对next(中间件3的委托)的调用appbuilder.Use(async (context, next) =>{await context.Response.WriteAsync("<h1>Middleware 2 : Hello World</h1>");await next.Invoke();await context.Response.WriteAsync("<h1>Middleware 2 : Goodbye World</h1>");});// 中间件3:终端中间件,无next(管道终点)appbuilder.Run(async context =>{await context.Response.WriteAsync("Run<Br/>");});
});

当一个 HTTP 请求到达时,执行顺序如下:

  • 请求进入管道,先执行中间件 1 的 “处理请求前” 逻辑;
  • 中间件 1 通过 await next() 调用中间件 2 的 请求委托,请求传递给中间件 2;
  • 中间件 2 执行 “处理请求前” 逻辑,再通过 await next() 调用终端中间件的 请求委托;
  • 终端中间件处理请求,输出 “Run< Br/>”;
  • 响应开始反向返回:终端中间件执行完毕后,回到中间件 2,执行 “处理响应后” 逻辑;
  • 继续回到中间件 1,执行 “处理响应后” 逻辑;
  • 整个管道执行完毕,响应返回给客户端

2.2.2 请求委托与中间件

当一个HTTP请求到达时请求管道时,从第一个中间件的请求委托开始执行,执行到await next()时,触发下一个中间件的请求委托。依次重复,直到执行到终端中间件后,按相反顺序执行每个中间件中await next()之后的逻辑,生成响应返回。

在这个执行流程中,中间件的执行逻辑通过请求委托得以执行,而请求委托通过中间件定义好的请求委托的嵌套关系,按照指定的顺序执行。

中间件是处理HTTP请求和响应的一系列功能组件,它们通过请求委托构成执行链,每个中间件的逻辑通过请求委托的形式嵌入管道,实现请求的依次处理。

三、中间件的定义方式

中间件使用Run,Map和Use这三个扩展方法来配置请求委托。

  1. Map的核心作用是根据请求路径创建分支管道,一个请求管道可能有多个分支管道。当请求路径匹配 Map定义的路径(比如接下里示例里的 /test)时,会进入该分支管道处理;不匹配则走主管道。
app.Map("/test", async appbuilder =>
{appbuilder.Use(async (context, next) =>{context.Response.ContentType = "text/html";await context.Response.WriteAsync("<h1>Middleware 1 : Hello World</h1>");await next.Invoke();await context.Response.WriteAsync("<h1>Middleware 1 : Goodbye World</h1>");});
});app.Use(async (context, next) =>
{await context.Response.WriteAsync("<h1>Middleware 2 : Hello World</h1>");await next.Invoke();await context.Response.WriteAsync("<h1>Middleware 2 : Goodbye World</h1>");
});
  1. Use是注册中间件的核心方法,其作用是将中间件加入管道,并通过next.Invoke()调用下一个中间件的连接整个管道。
  2. Run的作用是注册终端中间件,本质是Use的简化版,内部不包含next 参数会终止此管道,放在管道或分支管道的最末端。

ASP.NET Core中,主要通过内联中间件(in-line middleware),类中间件(class middleware)和工厂模式中间件(factory-based middleware)这三种方式定义中间件。

3.1 内联中间件(in-line middleware)

内联中间件写法如下,通过Map指定匹配路径,通过Use将中间件加入管道,最后通过Run定义终端中间件。
Program.cs

app.Map("/test", async appbuilder =>
{// 中间件1:返回一个请求委托,内部包含对next(中间件2的委托)的调用appbuilder.Use(async (context, next) =>{context.Response.ContentType = "text/html";await context.Response.WriteAsync("<h1>Middleware 1 : Hello World</h1>");await next.Invoke();await context.Response.WriteAsync("<h1>Middleware 1 : Goodbye World</h1>");});// 中间件2:返回一个请求委托,内部包含对next(中间件3的委托)的调用appbuilder.Use(async (context, next) =>{await context.Response.WriteAsync("<h1>Middleware 2 : Hello World</h1>");await next.Invoke();await context.Response.WriteAsync("<h1>Middleware 2 : Goodbye World</h1>");});// 中间件3:终端中间件,无next(管道终点)appbuilder.Run(async context =>{await context.Response.WriteAsync("Run<Br/>");});
});

3.2 类中间件(class middleware)

将中间件逻辑封装到单独的类中实现复用。
类中间件的写法固定,包含一个公共构造函数,参数为RequestDelegate,这个请求委托便是下一个中间件的委托;
另外它包含一个公共的InvokeAsync方法,参数为HttpContext,返回Task。这个便是当前中间件的核心执行逻辑。
InvokeAsync方法中通过await _next.Invoke(context)区分中间件前逻辑和后逻辑。
类中间件

public class CustomMiddleware
{private readonly RequestDelegate _next;public CustomMiddleware(RequestDelegate next){_next = next;}public async Task InvokeAsync(HttpContext context){await context.Response.WriteAsync("<h1>CustomMiddleware : Hello World</h1>");await _next.Invoke(context);await context.Response.WriteAsync("<h1>CustomMiddleware : Goodbye World</h1>");}
}

Program.cs

app.UseMiddleware<CustomMiddleware>();

这种方式注册到容器里的中间件的生命周期是Singleton,也就是说整个应用生命周期内只创建一个实例。而中间件类本身的生命周期由其构造函数依赖的服务决定,中间件的生命周期与管道一致(应用启动时创建,直到应用停止)

3.3 工厂模式中间件(factory-based middleware)

通过工程模式构建的中间件可以指定其生命周期。需要实现IMiddleware接口,在Program.cs中往由DI容器注入的时候可以指定其生命周期。
工厂模式中间件

public class ScopedLoggingMiddleware : IMiddleware
{private readonly ILogger<ScopedLoggingMiddleware> _logger;private readonly Guid _instanceId = Guid.NewGuid(); // 每次实例化生成唯一ID// 构造函数:接收依赖注入的服务public ScopedLoggingMiddleware(ILogger<ScopedLoggingMiddleware> logger){_logger = logger;}// 实现接口方法:处理请求public async Task InvokeAsync(HttpContext context, RequestDelegate next){_logger.LogInformation($"中间件实例 {_instanceId} 处理请求:{context.Request.Path}");await next(context);}
}

Program.cs

builder.Services.AddScoped<ScopedLoggingMiddleware>();
var app = builder.Build();
appbuilder.UseMiddleware<ScopedLoggingMiddleware>();
app.Run();

四、中间件与过滤器

中间件与过滤器都是实现AOP的重要机制,两者的执行逻辑有些类似,但面对具体功能时使用二者,我们要考虑好当前逻辑执行的时机。

中间件是ASP.NET Core 的底层基础设施,ASP.NET Core 框架的核心组成部分,属于最基础的请求处理机制,不依赖于特定框架。无论是 MVC、Web API、Razor Pages,还是纯中间件应用,都依赖中间件管道处理请求。

而过滤器仅存在于 MVC/Web API 的生命周期中,是 MVC 框架为了处理 “控制器动作执行” 这一特定场景设计的扩展点。它的运行依赖于 MVC 框架的控制器激活、模型绑定等内部流程,无法脱离 MVC 单独在中间件管道中使用。

也就是说过滤器面向与action的执行,而中间件面向的是整个http请求。

http://www.dtcms.com/a/339496.html

相关文章:

  • sfc_os!SfcQueueValidationRequest函数分析之sfc_os!IsFileInQueue
  • 关闭VSCode Markdown插件在Jupyter Notebook中的自动预览
  • 在linux系统中下载Andconda
  • windows电脑对于dell(戴尔)台式的安装,与创建索引盘,系统迁移到新硬盘
  • Vim 编辑器使用指南
  • Java I/O 模型精讲:从传统BIO到高性能NIO与AIO
  • uv与conda的区别及选择指南
  • Linux软件编程:进程与线程(进程(消息队列、共享内存、信号灯))
  • 决策树二-泰坦尼克号幸存者
  • 微服务集训整理
  • AI赋能市场拓展:从智能潜力洞察到渠道精准深耕,解锁商业增长新蓝海
  • 【Proteus仿真】【51单片机】基于51单片机密码锁
  • 优秘企业智脑 AISEO 实战:如何通过多模态大模型提升搜索引擎排名?附 3 大行业案例
  • pytorch学习笔记-argparse的使用(加更版)
  • 基于SpringBoot+Vue的写真馆预约管理系统(邮箱通知、WebSocket及时通讯、协同过滤算法)
  • 哪些仪器适合对接电子实验记录本,哪些不适合?
  • Java 11中的Collections类详解
  • Web安全攻防基础
  • 什么是IP隔离?一文讲清跨境电商/海外社媒的IP隔离逻辑
  • JVM对象创建和内存分配
  • 2025年12大AI测试自动化工具
  • 基礎複分析習題6.級數與乘積展開
  • 广东省省考备考(第八十一天8.19)——资料分析、数量(强化训练)
  • MVC、MVP、MVCC 和 MVI 架构的介绍及区别对比
  • 面试题储备-MQ篇 2-说说你对RocketMQ的理解
  • 基于WebSocket和SpringBoot聊天项目ChatterBox测试报告
  • 怎样平衡NLP技术发展中数据质量和隐私保护的关系?
  • 中科米堆CASAIM自动化三维测量设备测量汽车壳体直径尺寸
  • 多模态大模型应用落地:从图文生成到音视频交互的技术选型与实践
  • 5.1Pina介绍