0

0

ASP.NET Core中的中间件是什么?如何使用?

星降

星降

发布时间:2025-09-03 09:14:01

|

825人浏览过

|

来源于php中文网

原创

ASP.NET Core中间件是请求处理管道的核心,通过IApplicationBuilder按顺序注册,形成处理链条。每个中间件可选择是否传递请求,实现模块化、解耦和可复用的横切关注点,如认证、日志等。常见注册方式包括Use、Run、Map和扩展方法,执行顺序直接影响应用行为,如错误处理需前置,静态文件处理可短路后续流程。自定义中间件可通过约定类、Lambda表达式或Run实现,提升灵活性和维护性。

asp.net core中的中间件是什么?如何使用?

说起ASP.NET Core的中间件,这玩意儿简直是整个框架的灵魂所在。简单来说,它就是一系列按特定顺序排列的组件,它们串联起来处理HTTP请求和响应。每个中间件都能选择是否处理请求、修改请求或响应,然后决定是否将请求传递给管道中的下一个中间件。它让我们的应用程序能够以一种高度模块化和可插拔的方式来构建,极大地提升了灵活性和可维护性。至于怎么用,核心就是在应用程序的启动配置里,通过

IApplicationBuilder
接口来定义这个处理链条。

解决方案

在ASP.NET Core中,中间件的运用是贯穿整个请求生命周期的核心机制。它将HTTP请求处理过程拆解成一个个独立的、可复用的单元,每个单元(即中间件)只负责完成一项特定任务。

首先,中间件的注册和配置通常发生在

Startup.cs
文件的
Configure
方法中(或者在.NET 6+的Minimal API中,直接在
Program.cs
里)。
IApplicationBuilder
接口提供了多种方法来添加中间件:

  • Use
    方法
    :这是最常用的一种,它允许你将一个中间件添加到管道中,并且该中间件可以选择是否将请求传递给管道中的下一个中间件。它接受一个
    RequestDelegate
    作为参数,通常我们用lambda表达式来定义。例如,
    app.Use(async (context, next) => { /* do something before */ await next(); /* do something after */ });
    。这里的
    next
    委托就是管道中的下一个中间件。
  • Run
    方法
    :这个方法用于添加一个终端中间件。一旦请求到达
    Run
    方法定义的中间件,它就不会再传递给管道中的下一个中间件了,请求处理流程在此结束。所以,
    Run
    通常放在管道的末端,例如
    app.Run(async context => { await context.Response.WriteAsync("Hello from Run!"); });
  • Map
    方法
    Map
    允许你根据请求路径来分支管道。如果请求路径匹配,就会执行
    Map
    内部定义的中间件管道,否则跳过。这对于为特定路径提供不同的处理逻辑非常有用。比如,
    app.Map("/api", apiApp => { apiApp.UseMiddleware(); });
  • MapWhen
    方法
    :与
    Map
    类似,但它根据一个谓词函数来决定是否分支管道,而不是简单的路径匹配。这提供了更大的灵活性,可以基于任何请求属性进行条件判断。
  • 扩展方法:ASP.NET Core自带了许多常用的中间件,它们通常以
    UseXxx
    的形式提供,例如
    app.UseRouting()
    app.UseAuthentication()
    app.UseAuthorization()
    app.UseStaticFiles()
    等。这些都是预构建的中间件,可以直接拿来用。

当我们把这些中间件按序添加到

IApplicationBuilder
实例中时,实际上就构建了一个请求处理管道。HTTP请求进入管道的入口,然后依次流经每个中间件。每个中间件都有机会在请求到达下一个中间件之前或之后执行逻辑,甚至可以完全“短路”请求,直接返回响应。这种模式让整个应用的处理流程变得清晰且可控。

为什么我们需要ASP.NET Core中间件?它的核心价值在哪里?

在我看来,ASP.NET Core中间件的出现,彻底改变了我们构建Web应用的方式,它的核心价值在于解耦模块化可组合性。想想看,以前在ASP.NET Web Forms或MVC 5时代,很多跨领域的关注点,比如认证、授权、日志、错误处理,可能需要通过

HttpModule
HttpHandler
来实现,或者直接散落在控制器或业务逻辑中。这导致代码耦合度高,复用性差,维护起来简直是噩梦。

中间件机制则提供了一个优雅的解决方案。它把这些横切关注点从核心业务逻辑中剥离出来,封装成一个个独立的、可插拔的组件。比如,你不需要在每个Action里都写认证逻辑,只需在管道前面加上一个认证中间件,所有后续请求都会先经过它。这种设计哲学,让应用程序的各个部分各司其职,互不干扰。当我们需要添加新功能或者修改现有功能时,往往只需要添加、移除或调整中间件的顺序,而不需要大动干戈地修改核心代码。这不仅仅是提升了开发效率,更重要的是,它极大地增强了系统的可扩展性和健壮性。对我来说,这就像是搭积木,每一个中间件都是一块积木,我们可以根据需要自由组合,搭建出各种功能的房子,而不是每次都从零开始雕刻一整块石头。

如何自定义一个ASP.NET Core中间件?有哪些常见的实现方式?

自定义中间件是ASP.NET Core开发中非常常见的需求,毕竟框架提供的那些通用中间件不可能满足所有业务场景。实现自定义中间件主要有以下几种方式,每种都有其适用场景:

1. 基于约定(Convention-based)的中间件: 这是最常见、也相对简单的一种方式。你只需要创建一个类,满足以下两个条件:

  • 构造函数接受一个
    RequestDelegate
    类型的参数,用于注入管道中的下一个中间件。
  • 包含一个名为
    Invoke
    InvokeAsync
    的公共方法,该方法接受一个
    HttpContext
    参数,并且返回
    Task
public class MyCustomMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        // 在请求到达下一个中间件之前执行的逻辑
        Console.WriteLine($"请求进入 MyCustomMiddleware: {context.Request.Path}");

        await _next(context); // 调用管道中的下一个中间件

        // 在请求从下一个中间件返回之后执行的逻辑
        Console.WriteLine($"请求离开 MyCustomMiddleware: {context.Request.Path}");
    }
}

然后在

Configure
方法中,你可以这样使用它:

app.UseMiddleware();

或者,为了更方便,通常我们会写一个扩展方法:

通吃客零食网整站 for Shopex
通吃客零食网整站 for Shopex

第一步】:将安装包中所有的文件夹和文件用ftp工具以二进制方式上传至服务器空间;(如果您不知如何设置ftp工具的二进制方式,可以查看:(http://www.shopex.cn/support/qa/setup.help.717.html)【第二步】:在浏览器中输入 http://您的商店域名/install 进行安装界面进行安装即可。【第二步】:登录后台,工具箱里恢复数据管理后台是url/sho

下载
public static class MyCustomMiddlewareExtensions
{
    public static IApplicationBuilder UseMyCustomMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware();
    }
}
// 使用时:app.UseMyCustomMiddleware();

这种方式清晰直观,适用于需要访问服务(通过构造函数注入)或在请求前后执行逻辑的场景。

2. 直接使用Lambda表达式(Inline Middleware): 对于一些简单、不涉及复杂逻辑或无需从DI容器中获取服务的中间件,可以直接使用

app.Use()
方法传入一个lambda表达式。

app.Use(async (context, next) =>
{
    Console.WriteLine($"Inline Middleware - Before: {context.Request.Path}");
    await next();
    Console.WriteLine($"Inline Middleware - After: {context.Request.Path}");
});

这种方式非常简洁,特别适合快速原型开发或处理一些局部性的逻辑。但如果逻辑复杂,或者需要在多个地方复用,建议还是封装成独立的类。

3. 使用

app.Run()
作为终端中间件: 当你的中间件是管道的终点,即它会直接生成响应而不再将请求传递给下一个中间件时,可以使用
app.Run()

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the end of the pipeline!");
});

这通常用于处理404 Not Found、健康检查或简单的根路径响应等场景。

选择哪种方式取决于你的具体需求。如果需要高度封装、可复用且可能需要依赖注入,那么基于约定的类是首选。如果只是一个临时的、简单的逻辑,lambda表达式会更方便。而

app.Run()
则明确地表示了请求处理的终结。在我日常开发中,我倾向于将稍微复杂一点的逻辑都封装成独立的中间件类,这样代码结构更清晰,也方便测试和维护。

ASP.NET Core中间件的执行顺序如何影响应用程序行为?

中间件的执行顺序,这是个极其关键但又常常让人犯错的地方。我见过不少因为中间件顺序不对导致应用行为异常的案例。在ASP.NET Core的请求处理管道中,中间件的注册顺序就是它的执行顺序。这个规则简单粗暴,却决定了请求和响应流经各个组件的路径。

想象一下,中间件管道就像一个生产流水线。一个HTTP请求从一端进入,先经过第一个工位(第一个中间件),然后是第二个、第三个……直到最后一个工位。每个工位都有机会对产品(请求)进行加工。当所有工位都走完,或者某个工位直接把产品打包送出(短路),流水线才算结束。

1. 顺序的重要性:

  • 依赖关系: 很多中间件都有隐式的依赖关系。例如,认证中间件(
    UseAuthentication
    )通常需要放在授权中间件(
    UseAuthorization
    )之前,因为授权决策往往需要基于已认证的用户身份。同样,路由中间件(
    UseRouting
    )和终结点中间件(
    UseEndpoints
    )必须在身份验证和授权之后,以便这些安全特性能够作用于已匹配的路由。
  • 短路效应:
    Run
    方法以及一些特殊的
    Use
    方法(比如
    UseStaticFiles
    在找到文件后)会短路管道,这意味着它们之后的中间件将不会被执行。如果一个短路中间件被放在了不恰当的位置,它可能会阻止后续重要的中间件(如路由、授权)发挥作用,导致请求无法被正确处理。比如,如果你把
    UseStaticFiles
    放在了
    UseRouting
    之后,那静态文件请求就不会经过路由,这通常是没问题的。但如果你把一个自定义的、总是短路请求的中间件放在了管道最前面,那你的API控制器可能永远也接收不到请求了。
  • 日志和错误处理: 错误处理中间件(
    UseExceptionHandler
    UseDeveloperExceptionPage
    )通常应该放在管道的最前面。这样,它就能捕获管道中后续所有中间件抛出的异常。如果把它放在后面,它就无法捕获它前面中间件的异常了。日志中间件也类似,如果你想记录整个请求生命周期,它就应该尽可能靠前。

2. 典型的中间件顺序: 虽然没有一成不变的“圣经”顺序,但ASP.NET Core应用通常遵循一个推荐的模式:

  1. 错误处理 (
    UseDeveloperExceptionPage
    /
    UseExceptionHandler
    ):越靠前越好,以捕获所有后续异常。
  2. HSTS/HTTPS重定向 (
    UseHsts
    /
    UseHttpsRedirection
    ):安全相关的,尽早处理。
  3. 静态文件 (
    UseStaticFiles
    ):如果请求是静态文件,直接处理并短路。
  4. 路由 (
    UseRouting
    ):匹配请求URL到具体的终结点。
  5. CORS (
    UseCors
    ):处理跨域请求。
  6. 认证 (
    UseAuthentication
    ):验证用户身份。
  7. 授权 (
    UseAuthorization
    ):根据身份决定用户是否有权限访问资源。
  8. 会话 (
    UseSession
    ):如果需要会话状态。
  9. 终结点 (
    UseEndpoints
    ):执行匹配到的终结点(如MVC控制器Action、Razor Pages、Minimal API)。

这个顺序并非固定不变,但它提供了一个非常好的起点。理解每个中间件的作用以及它们之间的依赖关系,是正确配置管道的关键。我通常会在开发新功能或者排查问题时,特别留意中间件的注册顺序,这往往能帮助我快速定位问题所在。一旦顺序乱了,整个应用的表现就会变得难以预测,甚至直接崩溃。

相关专题

更多
什么是中间件
什么是中间件

中间件是一种软件组件,充当不兼容组件之间的桥梁,提供额外服务,例如集成异构系统、提供常用服务、提高应用程序性能,以及简化应用程序开发。想了解更多中间件的相关内容,可以阅读本专题下面的文章。

178

2024.05.11

Golang 中间件开发与微服务架构
Golang 中间件开发与微服务架构

本专题系统讲解 Golang 在微服务架构中的中间件开发,包括日志处理、限流与熔断、认证与授权、服务监控、API 网关设计等常见中间件功能的实现。通过实战项目,帮助开发者理解如何使用 Go 编写高效、可扩展的中间件组件,并在微服务环境中进行灵活部署与管理。

212

2025.12.18

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

204

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

190

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

49

2026.01.05

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1023

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

66

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

442

2025.12.29

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

3

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
【web前端】Node.js快速入门
【web前端】Node.js快速入门

共16课时 | 2万人学习

550W粉丝大佬手把手从零学JavaScript
550W粉丝大佬手把手从零学JavaScript

共1课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号