自定义中间件进入管道中执行队列的流程
自定义中间件进入管道中执行队列的流程
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. 总结:转换流程的关键步骤
- 类定义:创建带有
RequestDelegate参数构造函数和Invoke/InvokeAsync方法的类 - 扩展方法注册:通过
UseMiddleware<T>注册到管道 - 反射分析:框架分析中间件类的构造函数和方法签名
- 委托生成:生成优化的
Func<RequestDelegate, RequestDelegate>委托 - 管道构建:在
Build()方法中被包装到管道中 - 请求执行:每次请求时创建中间件实例并调用相应方法
这种设计的好处:
- 类型安全:编译时检查中间件签名
- 依赖注入:自动处理服务生命周期
- 性能优化:使用代码生成而非纯反射
- 灵活性:支持多种方法签名和参数注入方式

浙公网安备 33010602011771号