中间件Middleware

参考:https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0

1、什么是中间件

中间件是一种装配到应用管道以处理请求(Request)和响应(Response)的组件。 每个组件:

  • 选择是否将请求传递到管道中的下一个组件。
  • 可在管道中的下一个组件前后执行工作。

请求委托(Request delegates)用于生成请求管道。 请求委托处理每个 HTTP 请求。

1.1、中间件的工作原理

多个中间件按照顺序关系形成请求管道,用来处理HTTP请求和响应。中间件本质上是一个用来处理HTTP请求与响应的类。

HTTP请求(Request)进入请求管道(Request Pipeline),将会创建一个对象,用于描述请求(Http Request)信息,也用于返回最终HTTP响应(Response)。

请求从一端进入,按照添加顺序通过各个中间件(Middleware),每个中间件都可以对传入的请求进行一些操作然后 传入下一个中间件 或 直接返回。而对于返回响应(HTTP Response)也会遍历进来时所经过的中间件,顺序与请求时的正好相反。

注意:

  • next()前的代码会在管道进入的阶段执行,next后的代码会在管道返回阶段进行执行。
  • 如果中间件最终没有返回HTTP Response, 将会提示HTTP 404 Not Found。

 

1.2、中间件顺序

下图显示了 ASP.NET Core MVC 和 Razor Pages 应用的完整请求处理管道。图中的“终结点”中间件为相应的应用类型(MVC 或 Razor Pages)执行筛选器管道。

MVC中间件:

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        //异常处理中间件
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    //静态文件中间件,为提供静态文件(如HTML、CSS、JavaScript、图像等)和目录浏览提供支持。
    //如果请求与文件匹配,则为终端。
    app.UseStaticFiles();
    //路由中间件
    app.UseRouting();
    //权限中间件
    app.UseAuthorization();
    //终端中间件,用于处理端点,如控制器和视图
    app.UseEndpoints(endpoints =>
    {
        //定义默认的路由规则,指定了控制器、操作和可选的ID参数。
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

 

1.3、常用中间件

(1)静态文件中间件

app.UseStaticFiles();

用于直接从文件系统提供静态文件,如 HTML、CSS、JavaScript 和图像。

用途:高效提供静态内容,避免不必要的进一步处理。

建议位置:放置在管道的早期,确保静态文件优先被处理。

(2) 路由中间件

app.UseRouting();

将传入的 HTTP 请求与定义的端点进行匹配。

用途:将请求定向到相应的控制器或端点。

建议位置:放在依赖路由信息的中间件(如授权)之前。

(3) 身份验证中间件

app.UseAuthentication();

用于验证用户身份,处理凭证,并建立用户上下文。

用途:确保请求中的用户经过身份验证。
建议位置:必须放在授权中间件之前。

(4)授权中间件

app.UseAuthorization();

用于检查用户是否具有访问资源的权限。

用途:根据访问控制策略进行权限检查。
建议位置:放在身份验证中间件之后。

(5)CORS 中间件

app.UseCors("AllowAllPolicy");

配置跨域资源共享策略,控制跨域请求的行为。

用途:实现安全的跨域请求,尤其对 API 应用至关重要。
建议位置:根据业务逻辑,通常放在需要跨域访问的中间件之前。

(6)会话中间件
app.UseSession();

管理用户会话,支持在多个请求间存储和检索数据。

用途:维护用户状态,例如购物车数据。
前提:需要在 services 中配置会话服务。

(7)响应压缩中间件

app.UseResponseCompression();

压缩 HTTP 响应以减少带宽占用和加载时间。

用途:优化性能,特别是对于大型响应。
建议位置:接近管道的末尾,但应在生成响应的中间件之前。

(8)异常处理中间件
集中处理异常并生成用户友好的错误响应。

// 堆代码 duidaima.com
// Web 应用
app.UseExceptionHandler("/Home/Error");
// API 应用
app.UseExceptionHandler();

用途:捕获管道中的未处理异常。
建议位置:放置在管道的早期,覆盖整个应用的异常处理。

(9)HTTPS 重定向中间件
app.UseHttpsRedirection();

将 HTTP 请求自动重定向到 HTTPS。

用途:确保通信安全。
建议位置:用于生产环境中,确保所有流量通过 HTTPS。

(10)HSTS 中间件

app.UseHsts();

通过 HTTP 严格传输安全 (HSTS) 标头强制浏览器仅通过 HTTPS 访问站点。

用途:进一步增强安全性,防止降级攻击。
建议位置:与 HTTPS 重定向中间件结合使用。

(11)终端中间件
定义一个终止管道的中间件,直接生成 HTTP 响应。

app.Run(async context =>
{
await context.Response.WriteAsync("Hello, World!");
});

用途:负责生成最终响应,不会将控制权传递给后续中间件。

建议位置:通常放在管道的末尾。

 

2、Map、Use、Run

Map:用来定义一个管道可以处理哪些请求。

Use和Run:用来定义管道,一个管道由若干个Use和一个Run组成,每个Use引入一个中间件,而Run用来执行最终的核心应用逻辑 。

 

.Net Core中多种方式注册中间件:

  • Use:以委托的方式注册中间件。
  • UseMiddleware:以类型的方式注册中间件,T表示中间件的类型。
  • Map:将特定的请求地址(path)与中间件绑定,即path匹配时执行该中间件。
  • MapWhen:定义一个逻辑判断委托,当判断为true时执行指定的中间件。
  • Run:表示一个断路的中间件,是执行并返回给上游的中间件,无后续中间件。

2.1、Map对中间件管道进行分支

Map 扩展用作约定来创建管道分支。 Map 基于给定请求路径的匹配项来创建请求管道分支。 如果请求路径以给定路径开头,则执行分支。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/map1", HandleMapTest1);

app.Map("/map2", HandleMapTest2);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from non-Map delegate.");
});

app.Run();

static void HandleMapTest1(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map Test 1");
    });
}

static void HandleMapTest2(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map Test 2");
    });
}
请求 响应
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 2
localhost:1234/map3 Hello from non-Map delegate.

 

2.2、Use链接

用 Use 将多个请求委托链接在一起。 next 参数表示管道中的下一个委托。 可通过不调用 next 参数使管道短路。 通常可在 next 委托前后执行操作。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 向应用程序的请求管道中添加一个Func委托,这个委托其实就是所谓的中间件。
    // context参数是HttpContext,表示HTTP请求的上下文对象
    // next参数表示管道中的下一个中间件委托,如果不调用next,则会使管道短路
    // 用Use可以将多个中间件链接在一起
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("MiddleWare(1)-In\n");
        await next();
        await context.Response.WriteAsync("MiddleWare(1)-Out\n");
    });
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("MiddleWare(2)-In\n");
        await next();
        await context.Response.WriteAsync("MiddleWare(2)-Out\n");
    });
    
    // Run方法向应用程序的请求管道中添加一个RequestDelegate委托
    // 放在管道最后面,作为终端中间件
    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("MiddleWare(3)\n"); 
    });
}
////执行结果:
//MiddleWare(1)-In
//MiddleWare(2)-In
//MiddleWare(3)
//MiddleWare(2)-Out
//MiddleWare(1)-Out

 

3、自定义中间件

3.1、Use委托

app.Use(async (context, next) =>
{
    var cultureQuery = context.Request.Query["culture"];
    if (!string.IsNullOrWhiteSpace(cultureQuery))
    {
        var culture = new CultureInfo(cultureQuery);

        CultureInfo.CurrentCulture = culture;
        CultureInfo.CurrentUICulture = culture;
    }

    // Call the next delegate/middleware in the pipeline.
    await next(context);//参数可省略,ASP.NET自动给参数。
});

app.Run(async (context) =>
{
    await context.Response.WriteAsync(
        $"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});

app.Run();

 

3.2、自定义中间件类

  • 具有类型为 RequestDelegate 的参数的公共构造函数。
  • 名为 Invoke 或 InvokeAsync 的公共方法。 此方法必须:
    • 返回 Task
    • 接受类型 HttpContext 的第一个参数。

构造函数和 Invoke/InvokeAsync 的其他参数由依赖关系注入 (DI) 填充。

以下示例中间件通过查询字符串设置当前请求的区域性:

using System.Globalization;

namespace Middleware.Example;

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery))
        {
            var culture = new CultureInfo(cultureQuery);

            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }

        // Call the next delegate/middleware in the pipeline.
        await _next(context);
    }
}

 通常,创建扩展方法以通过 IApplicationBuilder 公开中间件:

public static class RequestCultureMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestCulture(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestCultureMiddleware>();
    }
}

调用中间件:

app.UseRequestCulture();

 

4、Middleware和Filter的区别

比较:

  • 中间件: 中间件是一种装配到应用程序管道以处理HTTP请求和响应的组件。每个组件可以选择是否将请求传递到管道中的下一个组件,并可以在管道中的任意点添加。
  • 过滤器: 过滤器是在ASP.NET Core MVC或Web API应用程序中用于拦截操作方法执行的组件。它们可以在执行前后执行逻辑,例如验证、日志记录、处理异常等。

中间件示例:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use(async (context, next) => {
        // 在调用下一个中间件之前可以做一些事情
        await context.Response.WriteAsync("Before next middleware \r\n");
        
        // 调用下一个中间件
        await next();
        
        // 在调用下一个中间件之后可以做一些事情
        await context.Response.WriteAsync("After next middleware \r\n");
    });
 
    // ... 其他中间件配置
}

过滤器示例:

public class MySampleFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // 在动作方法执行之前可以做一些事情
    }
 
    public void OnActionExecuted(ActionExecutedContext context)
    {
        // 在动作方法执行之后可以做一些事情
    }
}
 
[ServiceFilter(typeof(MySampleFilter))]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

总结:

  • 中间件和过滤器都是一种AOP的思想,都用于处理HTTP请求和响应。但是它们处理HTTP请求的方式不同:中间件是基于管道的方式,而过滤器是基于特定动作或控制器的方式。
  • 中间件可以用于那些不是特定于业务领域的逻辑,以及需要在每个请求或大多数请求中发生的操作,更加底层。而过滤器关注的是如何实现业务。
posted @   茜茜87  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示