惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Scott Helme
Scott Helme
N
Netflix TechBlog - Medium
AI
AI
Security Latest
Security Latest
GbyAI
GbyAI
P
Proofpoint News Feed
Y
Y Combinator Blog
A
Arctic Wolf
G
Google Developers Blog
U
Unit 42
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Vulnerabilities – Threatpost
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
T
Tor Project blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
C
Check Point Blog
B
Blog RSS Feed
The GitHub Blog
The GitHub Blog
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 【当耐特】
博客园 - Franky
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
Latest news
Latest news
L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
WordPress大学
WordPress大学
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
The Hacker News
The Hacker News
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
Project Zero
Project Zero
博客园_首页

博客园 - 邓磊Lei

为什么 Go 一个 HTTP 服务可以同时处理数万连接 gin怎么处理一次http请求的 我的经验:git提交信息分别用什么icon C# 也能像 Python 一样写脚本 | .NET 10 构建基于文件的应用 .NET 10 使用 Microsoft.AspNetCore.OpenApi 实现 API 版本管理 ASP.NET Core 内存缓存实战:一篇搞懂该怎么配、怎么避坑 Microsoft Agent Framework + Kimi API 实战:控制台应用跑通单次与多轮 Agent 对话 开发实战:asp.net core + ef core 实现动态可扩展的分页方案 Python 入门:从“其他语言”到 Pythonic 思维的完整迁移手册 .NET 进阶之路:异步、并发与内存管理的系统性认知 Redis:延迟双删的适用边界与落地细节 Serilog:从结构化日志认知到 .NET 工程落地 EF Core 原生 SQL 实战:FromSql、SqlQuery 与对象映射边界 EF Core 拦截器实战:SaveChangesInterceptor、CommandInterceptor 与审计落地 ASP.NET Core 外部依赖调用治理实战:HttpClientFactory、Polly 与幂等边界 .NET .Result 避坑指南:不同框架下的死锁与线程池饥饿 EF Core 慢查询排查实战:TagWith、OpenTelemetry、执行计划,30 分钟定位性能瓶颈 EF Core 并发冲突实战:乐观锁、RowVersion 与 DbUpdateConcurrencyException 怎么处理 EF Core 写入链路深拆:从 ChangeTracker 到 SQL Batch 的性能诊断与优化 ASP.NET Core 认证鉴权实战:JWT、Policy 与权限边界怎么落地 C# 异步编程深水区:Task、ValueTask、线程池饥饿与背压设计 EF Core 查询性能黑洞:Include、投影与跟踪策略的边界 从 IApplicationBuilder 到 RequestDelegate:ASP.NET Core 请求管线的性能与可观测性实战 如何在 rider 中配置 SonarQube
聊聊 ASP.NET Core 中间件和过滤器的区别
邓磊Lei · 2026-04-03 · via 博客园 - 邓磊Lei

引言

不知道你有没有在面试中遇到过这样的问题:"中间件和过滤器的区别是什么?",或者在平时开发中思考过:"一个请求进来,ASP.NET Core 到底是怎么一步步处理它的?"

这篇文章就来聊聊,不会涉及太深的源码,主要面向初级开发者,帮你建立一个清晰的认知。


先说中间件

中间件是 ASP.NET Core 处理 HTTP 请求的基本部件,是框架本身的组成部分。

每一个请求进来后,按照 Use 的注册顺序,依次经过每一个中间件,到达终端后,再按照相反的方向依次返回。一系列中间件串联起来,就组成了 ASP.NET Core 的请求管道。这个模型有人叫"洋葱模型",也有人叫"俄罗斯套娃模型",都是一个意思。

请求 →  中间件A → 中间件B → 中间件C(终端)
响应 ←  中间件A ← 中间件B ←

中间件可以做什么呢?它收到请求后,可以在请求/响应对象上加点佐料(自己的逻辑),然后调用 next() 把请求传递给下一个中间件。如果不调用 next(),请求就在这里短路了,后面的中间件不会执行,这个中间件就成了终端中间件。

下面是一个简单示例,演示如何自定义一个记录请求耗时的中间件:

// 定义中间件类
public class TimingMiddleware
{
    private readonly RequestDelegate _next;

    public TimingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var sw = Stopwatch.StartNew();

        // 调用 next(),请求继续往下走
        await _next(context);

        // next() 返回后,响应正在往回走
        sw.Stop();
        Console.WriteLine($"[{context.Request.Path}] 耗时:{sw.ElapsedMilliseconds}ms");
    }
}

// 在 Program.cs 中注册
var app = builder.Build();

app.UseMiddleware<TimingMiddleware>();  // 注册自定义中间件

app.UseRouting();
app.UseAuthorization();
app.MapControllers();

app.Run();

如果你不想单独定义一个类,也可以用 app.Use 直接写一个内联中间件:

app.Use(async (context, next) =>
{
    Console.WriteLine($"请求进来了:{context.Request.Path}");

    await next();  // 传递给下一个中间件

    Console.WriteLine($"响应回来了:{context.Response.StatusCode}");
});

这里注册的是终端中间件,不接受 next,请求到这里就结束了,不会再往下传:

app.Run(async context =>
{
    await context.Response.WriteAsync("到终点了,不继续往下走");
});

再聊过滤器

过滤器是在路由中间件内部起作用的。

请求经过中间件管道,到达路由中间件后,路由中间件确定了要调用哪个 Controller / Action。在真正执行 Action 的前后,过滤器就夹在这里介入。

用一张简单的示意图来表示:

请求进来
  → 中间件们
    → 路由中间件(确定目标 Action)
      → 过滤器们(Action 执行前后插手)
        → Action 执行
      ← 过滤器们
    ← 路由中间件
  ← 中间件们
响应返回

正因为过滤器处于路由之后,所以它能拿到 MVC 的上下文信息,比如当前是哪个 Action、Action 上有哪些 Attribute、ModelState 是否合法等等。这是普通中间件做不到的,因为中间件执行的时候,路由还没解析呢。

// 定义过滤器类,继承 ActionFilterAttribute
public class ValidateModelAttribute : ActionFilterAttribute
{
    // 在 Action 执行之前触发
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            // ModelState 校验不通过,直接短路,返回 400
            var errors = context.ModelState
                .Where(e => e.Value?.Errors.Count > 0)
                .ToDictionary(
                    e => e.Key,
                    e => e.Value!.Errors.Select(x => x.ErrorMessage).ToArray()
                );

            context.Result = new BadRequestObjectResult(new
            {
                code = 400,
                message = "参数校验失败",
                errors
            });
        }
    }

    // 在 Action 执行之后触发
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // 可以在这里统一处理响应,比如包一层统一的返回格式
    }
}

使用方式很简单,直接作为 Attribute 标注在 Controller 或 Action 上:

// 标注在单个 Action 上
[HttpPost]
[ValidateModel]
public IActionResult CreateUser([FromBody] CreateUserRequest request)
{
    // 走到这里说明 ModelState 已经通过校验
    return Ok(new { message = "创建成功" });
}

// 也可以标注在 Controller 上,对所有 Action 生效
[ApiController]
[ValidateModel]
public class UserController : ControllerBase
{
    // ...
}

如果想全局生效,在 Program.cs 里注册就行:

builder.Services.AddControllers(options =>
{
    options.Filters.Add<ValidateModelAttribute>();  // 全局注册
});

它俩的区别

中间件 过滤器
作用范围 所有 HTTP 请求,包括静态文件 仅限 Controller / Action
典型使用场景 限流、压缩、跨域、日志、认证 权限校验、参数校验、统一响应封装、异常处理

一句话总结:中间件是 HTTP 管道的基础设施,过滤器是 MVC 系的切面机制,它们不是同一层次的东西。

给你一个小技巧,不确定用哪个的时候,问自己一个问题:我需要知道当前是哪个 Action 吗? 需要的话用过滤器,不需要的话中间件就够了。


内置清单

最后整理一份 ASP.NET Core 已经提供的常用中间件和过滤器,遇到需求的时候先查查有没有现成的,别重复造轮子。

常用内置中间件:

中间件 说明
UseRouting 路由解析
UseAuthentication 认证
UseAuthorization 授权
UseStaticFiles 静态文件服务
UseCors 跨域
UseExceptionHandler 全局异常处理
UseHttpsRedirection HTTPS 重定向
UseResponseCompression 响应压缩
UseRateLimiter 限流(.NET 7 新增)

内置过滤器:

过滤器 说明
[Authorize] 授权校验
[AllowAnonymous] 允许匿名访问
[RequireHttps] 强制 HTTPS
[ValidateAntiForgeryToken] 防止 CSRF 攻击
[ResponseCache] 响应缓存
[Produces] 指定响应内容类型
[Consumes] 指定请求内容类型