中间件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?}");
    });
}

 

 

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 @ 2024-03-19 15:05  茜茜87  阅读(4)  评论(0编辑  收藏  举报