ASP.NET Core 中间件和过滤器的区别

核心概念先明确

在看流程前,需先区分两者的核心差异,避免混淆:

维度 中间件(Middleware) 过滤器(Filter)
作用层级 全局请求管道(HTTP 生命周期) 控制器/动作方法层级(MVC 框架内部)
处理时机 早于过滤器(请求进入管道即触发) 晚于中间件(路由匹配后、控制器执行前后触发)
核心职责 处理 HTTP 上下文(如日志、认证、静态文件) 处理 MVC 上下文(如权限校验、模型验证、异常)
依赖框架 依赖 ASP.NET Core 基础管道,与 MVC 无关 依赖 MVC/Razor Pages 框架

image

关键节点拆解(避免踩坑)

  1. 中间件的“双向性”
    中间件按“正向顺序执行(请求阶段)→ 反向顺序执行(响应阶段)”,例如:
    日志中间件(正向)→ 认证中间件(正向)→ 路由中间件(正向)→ 控制器执行 → 路由中间件(反向)→ 认证中间件(反向)→ 日志中间件(反向)
    若某中间件未调用 await _next(context)(未传递请求给下一个中间件),则管道会“短路”,直接进入反向流程。

  2. 过滤器的“层级优先级”
    过滤器支持 全局、控制器、动作方法 三个层级,执行顺序为:
    全局过滤器 → 控制器过滤器 → 动作方法过滤器(如全局授权过滤器先于控制器授权过滤器执行)。

  3. 异常处理的“覆盖关系”

    • 过滤器异常(如授权失败、模型验证失败):优先由 异常过滤器 处理,处理后直接返回响应,不进入后续中间件的“正向流程”。
    • 中间件异常(如认证失败、静态文件不存在):由 异常处理中间件(如 UseExceptionHandler)处理,早于过滤器触发。

下面给你一个能直接跑起来的 ASP.NET Core 6 最小 API 案例,把“中间件”和“过滤器”两种扩展点串在一起。
代码结构 = 两段自定义中间件 + 一段 MVC Endpoint + 四类常用过滤器。
运行时观察控制台,就能直观看到请求在中间件与过滤器之间的完整穿梭顺序。

────────────────────
1️⃣ 新建空白项目(.NET 6)

dotnet new web -n MiddlewareFilterDemo
cd MiddlewareFilterDemo

────────────────────
2️⃣ 把 Program.cs 整个替换为下面内容即可运行

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

var builder = WebApplication.CreateBuilder(args);

// 为了演示过滤器,我们显式启用 MVC
builder.Services.AddControllers();

var app = builder.Build();

// ┌──────────────  自定义中间件 1  ──────────────┐
app.Use(async (ctx, next) =>
{
    Console.WriteLine("[Middleware-1] 进入 —— 在 Routing 之前");
    await next();
    Console.WriteLine("[Middleware-1] 离开 —— 响应已发出");
});

// ┌──────────────  自定义中间件 2  ──────────────┐
app.Use(async (ctx, next) =>
{
    Console.WriteLine("[Middleware-2] 进入 —— 在 UseRouting 之后");
    await next();
    Console.WriteLine("[Middleware-2] 离开 —— 响应即将回到 Middleware-1");
});

// 标准中间件
app.UseRouting();
app.UseAuthorization();   // 为了演示授权过滤器
app.MapControllers();     // 把请求交给 MVC

app.Run();

────────────────────
3️⃣ 新建 Controllers/DemoController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace MiddlewareFilterDemo.Controllers;

[ApiController]
[Route("api/[controller]")]
public class DemoController : ControllerBase
{
    [HttpGet]
    [CustomResource]
    [CustomAction]
    [CustomResult]
    [CustomAuth]
    public string Get()
    {
        Console.WriteLine("   → 正在执行 Controller Action");
        return "Hello ASP.NET Core!";
    }
}

// 以下 4 个过滤器仅打日志,方便观察顺序
public class CustomAuthAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context) =>
        Console.WriteLine("   [Filter] Authorization —— 授权检查");
}

public class CustomResourceAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext _) =>
        Console.WriteLine("   [Filter] Resource —— 执行前");
    public void OnResourceExecuted(ResourceExecutedContext _) =>
        Console.WriteLine("   [Filter] Resource —— 执行后");
}

public class CustomActionAttribute : Attribute, IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext _) =>
        Console.WriteLine("   [Filter] Action —— 执行前");
    public void OnActionExecuted(ActionExecutedContext _) =>
        Console.WriteLine("   [Filter] Action —— 执行后");
}

public class CustomResultAttribute : Attribute, IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext _) =>
        Console.WriteLine("   [Filter] Result —— 执行前");
    public void OnResultExecuted(ResultExecutedContext _) =>
        Console.WriteLine("   [Filter] Result —— 执行后");
}

────────────────────
4️⃣ 运行 & 调用

dotnet run
# 浏览器或 Postman 访问 https://localhost:5001/api/demo

────────────────────
5️⃣ 控制台输出(顺序即管线)

[Middleware-1] 进入 —— 在 Routing 之前
[Middleware-2] 进入 —— 在 UseRouting 之后
   [Filter] Authorization —— 授权检查
   [Filter] Resource —— 执行前
   [Filter] Action —— 执行前
   → 正在执行 Controller Action
   [Filter] Action —— 执行后
   [Filter] Result —— 执行前
   [Filter] Result —— 执行后
   [Filter] Resource —— 执行后
[Middleware-2] 离开 —— 响应即将回到 Middleware-1
[Middleware-1] 离开 —— 响应已发出

────────────────────
6️⃣ 总结(流程图文字版)

Client


[Middleware-1] ——> [Middleware-2] ——> Routing/Endpoint Selection


┌──────────────────────────┐
│ MVC Filter Pipeline │
│ Authorization │
│ Resource (before) │
│ Action (before) │
│ Controller.Action() │
│ Action (after) │
│ Result (before) │
│ Result (after) │
│ Resource (after) │
└──────────────────────────┘


[Middleware-2] ——> [Middleware-1] ——> Client

通过这份“最小但完整”的代码,你可以:

  • 把中间件想象成“洋葱”外层,所有请求/响应都要经过。
  • 把过滤器想象成 MVC 内部的“切面”,只对命中 Endpoint 的请求生效。

改两行日志、下个断点,就能亲手验证官方文档里说的顺序。

posted @ 2025-08-28 18:59  【唐】三三  阅读(37)  评论(0)    收藏  举报