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

自定义中间件进入管道中执行队列的流程

自定义中间件进入管道中执行队列的流程

1. 自定义中间件类的标准写法

public class CustomLoggingMiddleware
{private readonly RequestDelegate _next;// 1. 构造函数必须包含 RequestDelegate next 参数public CustomLoggingMiddleware(RequestDelegate next){_next = next;}// 2. 必须包含名为 Invoke 或 InvokeAsync 的公共方法public async Task InvokeAsync(HttpContext context){//执行前动作...// 调用管道中的下一个中间件await _next(context);       //执行后动作...}
}

2. 使用扩展方法注册中间件

public static class CustomLoggingMiddlewareExtensions
{public static IApplicationBuilder UseCustomLogging(this IApplicationBuilder app){return app.UseMiddleware<CustomLoggingMiddleware>();}
}

3. 在管道中使用

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{app.UseCustomLogging();  // 我们的自定义中间件app.UseHttpsRedirection();app.UseStaticFiles();app.UseRouting();app.UseAuthorization();app.MapControllers();
}

4. 转换流程详解

现在来看 UseMiddleware<CustomLoggingMiddleware>() 是如何将类转换成管道方法的:

步骤 1: UseMiddleware<T> 扩展方法
// Microsoft.AspNetCore.Builder.UseMiddlewareExtensions
public static class UseMiddlewareExtensions
{public static IApplicationBuilder UseMiddleware<TMiddleware>(this IApplicationBuilder app){return app.UseMiddleware(typeof(TMiddleware));}public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middlewareType){// 创建中间件工厂return app.Use(next =>{// 这里就是转换的关键!// 创建一个委托,它会在每次请求时实例化中间件并调用Invoke方法var middlewareFactory = (IMiddlewareFactory)app.ApplicationServices.GetService(typeof(IMiddlewareFactory));return async context =>{// 每次请求都会执行这个委托var middleware = middlewareFactory.Create(middlewareType);try{// 调用中间件的InvokeAsync方法await middleware.InvokeAsync(context, next);}finally{middlewareFactory.Release(middleware);}};});}
}
步骤 2: 运行时实际生成的委托

实际上,ASP.NET Core 使用更高效的代码生成机制。简化后的等效代码:

// 运行时生成的委托大致如下:
Func<RequestDelegate, RequestDelegate> generatedMiddleware = next =>
{return async context =>{// 创建中间件实例var middleware = new CustomLoggingMiddleware(next, logger);// 调用InvokeAsync方法await middleware.InvokeAsync(context);};
};
步骤 3: 管道构建过程
// 初始管道终点
RequestDelegate pipelineEnd = async context => 
{context.Response.StatusCode = 404; // 默认404
};// 应用我们的自定义中间件
var customMiddleware = generatedMiddleware(pipelineEnd);// 最终生成的委托
RequestDelegate finalPipeline = async context =>
{var logger = // 获取logger实例;var middlewareInstance = new CustomLoggingMiddleware(pipelineEnd, logger);await middlewareInstance.InvokeAsync(context);
};
完整的转换流程图示
自定义中间件类↓
UseMiddleware<T>() 扩展方法↓
反射分析中间件类的构造函数和方法↓
生成优化的委托代码 (Func<RequestDelegate, RequestDelegate>)↓
注册到 IApplicationBuilder 的中间件列表↓
管道构建时被包装成最终的 RequestDelegate↓
请求到达时执行生成的委托代码

支持多种方法签名的中间件

ASP.NET Core 支持多种方法签名,转换器会智能处理:

方式 1: 标准的 InvokeAsync 方法

csharp

public async Task InvokeAsync(HttpContext context)
{await _next(context);
}

方式 2: 带服务的 InvokeAsync 方法

csharp

public async Task InvokeAsync(HttpContext context, ISomeService service)
{// 依赖注入会自动注入服务await _next(context);
}

方式 3: 同步的 Invoke 方法

csharp

public void Invoke(HttpContext context)
{// 同步处理_next(context).Wait();
}

实际的复杂中间件示例

csharp

public class RateLimitingMiddleware
{private readonly RequestDelegate _next;private readonly IMemoryCache _cache;private readonly RateLimitOptions _options;// 构造函数注入public RateLimitingMiddleware(RequestDelegate next, IMemoryCache cache, IOptions<RateLimitOptions> options){_next = next;_cache = cache;_options = options.Value;}// 方法参数注入public async Task InvokeAsync(HttpContext context, ILogger<RateLimitingMiddleware> logger){var clientIp = context.Connection.RemoteIpAddress?.ToString();var cacheKey = $"rate_limit_{clientIp}";// 速率限制逻辑if (_cache.TryGetValue(cacheKey, out int requestCount) && requestCount >= _options.MaxRequests){logger.LogWarning("IP {IP} 触发速率限制", clientIp);context.Response.StatusCode = 429;await context.Response.WriteAsync("Too Many Requests");return;}// 更新计数器_cache.Set(cacheKey, requestCount + 1, TimeSpan.FromMinutes(1));await _next(context);}
}public class RateLimitOptions
{public int MaxRequests { get; set; } = 100;
}

依赖注入的自动处理

转换过程中,依赖注入容器会自动处理:

csharp

// 转换器会分析构造函数和方法参数
public async Task InvokeAsync(HttpContext context, ILogger<RateLimitingMiddleware> logger,        // 方法参数注入IMemoryCache cache,                            // 方法参数注入  IOptions<RateLimitOptions> options)            // 方法参数注入
{// 所有这些参数都会从DI容器自动解析
}

10. 总结:转换流程的关键步骤

  1. 类定义:创建带有 RequestDelegate 参数构造函数和 Invoke/InvokeAsync 方法的类
  2. 扩展方法注册:通过 UseMiddleware<T> 注册到管道
  3. 反射分析:框架分析中间件类的构造函数和方法签名
  4. 委托生成:生成优化的 Func<RequestDelegate, RequestDelegate> 委托
  5. 管道构建:在 Build() 方法中被包装到管道中
  6. 请求执行:每次请求时创建中间件实例并调用相应方法

这种设计的好处:

  • 类型安全:编译时检查中间件签名
  • 依赖注入:自动处理服务生命周期
  • 性能优化:使用代码生成而非纯反射
  • 灵活性:支持多种方法签名和参数注入方式
http://www.jsqmd.com/news/4276/

相关文章:

  • 翻斗幼儿园历险记-CTF-WP
  • .net8+winform+Antdui 制作 LOL 小助手
  • 深入解析:【Git】Git 简介及基本操作
  • hutool主要内容list
  • Kurt-Blender零基础教程:第2章:建模篇——第3节:陈列/父子级/蒙皮/置换修改器与小狐狸角色建模 - 教程
  • 20250916_QQ_Powershell
  • 完整教程:HTTP安全响应头--CSP(Content-Security-Policy)
  • 学习:uniapp全栈微信小程序vue3后台(26) - 指南
  • HTML5介绍(HTML5特性、HTML5功能) - 指南
  • Experiment1
  • 读书笔记:Oracle 自动索引:让数据库自己管索引?
  • 1_2025.9.26_1
  • 故障处理:Oracle RAC集群CTSS时钟同步故障案例分析与解决
  • Linux系统提权-web/普通用户-docker逃逸提权shell交互
  • PostgreSQL技术大讲堂 - 第106讲:分区表索引优化
  • AI智能体:从认知到实践
  • Kinect屏幕边缘检测不灵敏的解决方案
  • 暴力拓客游戏小程序:助力商家高效引流与裂变的智能解决方案
  • vue3小坑之-为什么把ref定义的数组赋值给数组对象后取值为空数组?
  • 第二类斯特林数
  • 扫码签到赢大奖小程序:助力多场景获客的智能营销工具
  • docker 镜像/容器
  • jmeter命令行参数详细解释
  • RK3399:性能与能效的嵌入式先锋,解锁多场景应用潜力
  • 【C++STL详解】带头双向循环结构 + 双向迭代器,核心接口 + 排序效率 + 避坑指南 - 教程
  • Node.js后端学习笔记:Express+MySQL - 指南
  • TorchV知识库安全解决方案:基于智能环境感知的动态权限控制
  • 详细介绍:Java HTTP协议(二)--- HTTPS,Tomcat
  • VBA ETH功能应用 | “0”代码构建SOME/IP节点
  • ISUP协议视频平台EasyCVR在智慧灯杆综合管理中的应用