把握 ASP.NET Core 请求管道,从零实现属于自己的中间件。
目录
-
编写 IP 记录中间件
6.1 核心 Middleware 类
6.2 扩展方法让注册更优雅
写在前面
ASP.NET Core 把所有 HTTP 请求都交给了一条由多个 Middleware 组成的管道来处理。通过编写自定义中间件,我们可以轻松地实现请求拦截、日志记录、认证授权等功能。本文将以 “记录访问者 IP” 为例,演示完整的中间件编写、注册与测试流程,并在最后附带一个简单的“访问频率限制”进阶示例。
环境说明
-
操作系统:Windows 10
-
.NET Core SDK:
1.1.x
(同样适用于 2.x / 3.x / 5.x / 6.x / 7.x) -
IDE:Visual Studio / VS Code
-
依赖包:
Microsoft.Extensions.Logging.Console
如果你使用的是更高版本的 .NET Core,只需要把下面示例中的版本号替换为对应的最新稳定版本即可。
什么是 Middleware
Middleware(中间件)位于请求处理管道中,可对 HTTP 请求进行以下操作:
-
读写
HttpContext
; -
决定是否继续调用下一个中间件;
-
在调用后续中间件之前、之后执行自定义逻辑。
依赖注入、AOP、管道式处理模型等思想使得中间件可插拔、顺序可控,开发者只需专注实现业务逻辑,把组合交给框架。
创建空模板项目
新建 ASP.NET Core Web Application → 选择 Empty 模板,项目结构最简化,更便于观察中间件运行效果。
emptyTemplate
安装日志组件
在 csproj
中添加控制台日志依赖:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" />
</ItemGroup>
使用 dotnet restore
或 IDE 自动还原即可。
控制台日志组件便于调试,生产环境可以接入
Serilog
,NLog
,Elasticsearch
等更强大的日志系统。
编写 IP 记录中间件
核心 Middleware 类
RequestIPMiddleware.cs
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace XzyCore.Middlewares
{
/// <summary>
/// 记录客户端 IP 的中间件
/// </summary>
public class RequestIPMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestIPMiddleware> _logger;
public RequestIPMiddleware(RequestDelegate next,
ILogger<RequestIPMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
// 1. 前置逻辑:记录 IP
var ip = context.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
_logger.LogInformation($"[RequestIPMiddleware] User IP: {ip}");
// 2. 调用管道中的下一个中间件
await _next(context);
// 3. 后置逻辑(可选):例如记录响应状态码
_logger.LogDebug($"[RequestIPMiddleware] Response Status: {context.Response.StatusCode}");
}
}
}
扩展方法让注册更优雅
RequestIPExtensions.cs
using Microsoft.AspNetCore.Builder;
namespace XzyCore.Middlewares
{
public static class RequestIPExtensions
{
/// <summary>
/// IApplicationBuilder 扩展,简化 UseMiddleware 调用
/// </summary>
public static IApplicationBuilder UseRequestIP(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestIPMiddleware>();
}
}
}
通过扩展方法,后续在
Startup.cs
里只需要app.UseRequestIP();
,使得管道代码更加语义化。
在 Startup 中注册中间件
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using XzyCore.Middlewares;
public class Startup
{
// 注册必要服务
public void ConfigureServices(IServiceCollection services) { }
// 配置请求管道
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
// Console 日志
loggerFactory.AddConsole();
// 自定义 IP 记录中间件
app.UseRequestIP();
// 开发异常页
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// 终结点
app.Run(async context =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}
在 Program.cs
中维持默认模板即可。
运行与验证
-
dotnet run
启动项目; -
浏览器访问
http://localhost:5000
; -
控制台输出类似日志:
info: XzyCore.Middlewares.RequestIPMiddleware[0]
[RequestIPMiddleware] User IP: 127.0.0.1
若部署到服务器,通过访问日志可获取真实的公网 IP。
进阶:加上 IP 访问频率限制
在高并发场景中,我们可能需要限制同一 IP 的访问频率以防止刷接口。下面给出一个极简示例(生产环境请使用如 AspNetCoreRateLimit
等成熟库):
public class RateLimitMiddleware
{
private readonly RequestDelegate _next;
private static readonly MemoryCache Cache = new MemoryCache(new MemoryCacheOptions());
private readonly ILogger<RateLimitMiddleware> _logger;
private const int LIMIT = 30; // 30 秒
private const int MAX_COUNT = 10; // 最大 10 次
public RateLimitMiddleware(RequestDelegate next, ILogger<RateLimitMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
var ip = context.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
var cacheKey = $"Rate:{ip}";
var info = Cache.GetOrCreate(cacheKey, entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(LIMIT);
return new RequestCounter();
});
if (++info.Count > MAX_COUNT)
{
_logger.LogWarning($"IP {ip} exceeded rate limit.");
context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
await context.Response.WriteAsync("Too Many Requests");
return;
}
await _next(context);
}
private class RequestCounter
{
public int Count { get; set; }
}
}
注册方式同上:
app.UseMiddleware<RateLimitMiddleware>();
这样就实现了最简单的限流逻辑。
总结与思考
本文完整展示了:
-
.NET Core 请求管道与 Middleware 基本概念
-
从零到一编写、注册、调试自定义中间件
-
日志打印、扩展方法封装的实用技巧
-
一个可落地的 IP 限流进阶示例
掌握中间件的设计思想后,你可以轻松扩展更多功能,例如:
-
统一异常处理
-
JWT 认证
-
性能埋点 / APM
-
静态资源压缩
中间件 = 轻量 + 插拔 + 高扩展性,是 ASP.NET Core 架构的精髓之一。
互动投票
如果本文对你有所帮助,
-
请动动小手 点个赞 👍!
-
觉得文章不错,收藏 ⭐ + 关注 ➕,让更多同学看到!
-
欢迎在评论区留言讨论,你最想实现的自定义中间件是什么?
你的每一次反馈,都是我持续创作的最大动力!谢谢~ 🎉