⭐️ Furion v4 版本支持【所有历史版本】无缝升级,一套代码兼容 .NET 5+ ⭐️
Skip to main content

5.5 中间件 (Middleware)

5.5.1 关于中间件

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

  • 选择是否将请求传递到管道中的下一个组件。
  • 可在管道中的下一个组件前后执行工作。
  • 请求委托用于生成请求管道。 请求委托处理每个 HTTP 请求。

一句话总结:中间件是比筛选器更底层,更上游的面向切面技术,其性能最高,可处理的应用范围远比过滤器广,如实现网关,URL 转发,限流等等。

中间件更多内容

本章节暂不考虑将中间件展开讲,想了解更多知识可阅读官方文档 【ASP.NET Core - 中间件

5.5.2 常见中间件

5.5.2.1 所有请求返回同一个结果

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

5.5.2.2 拦截所有请求(可多个)

app.Use(async (context, next) =>{    // 比如设置统一头    context.Response.Headers["framework"] = "Furion";    // 执行下一个中间件    await next.Invoke();});// 多个app.Use(...);

5.5.2.3 特定路由中间件(可多个)

app.Map("/hello", app => {    app.Run(async context =>    {        await context.Response.WriteAsync("Map Test 1");    });});app.Map("/hello/say", app => {    // ....});

5.5.2.4 嵌套路由中间件(可多个)

app.Map("/level1", level1App => {    level1App.Map("/level2a", level2AApp => {        // "/level1/level2a" processing    });    level1App.Map("/level2b", level2BApp => {        // "/level1/level2b" processing    });});

更多例子查看官方文档 https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-6.0

5.5.3 自定义中间件

自定义中间件有多种方式,最简单的是通过 app.Use 方式,另外还支持独立类定义方式。

5.5.3.1 app.Use 方式 (不推荐)

Starup.cs
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;    }    // 调用下一个中间件    await next(context);});

5.5.3.2 独立类 方式(推荐)

独立类的方式是目前最为推荐的方式,拓展性强,维护性高,如:

  • 定义中间件,建议以 Middleware 结尾:
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;        }        // 调用下一个中间件        await _next(context);    }}
  • 添加中间件拓展类

定义了中间件之后,需要创建这个中间件的拓展类,中间件拓展方法建议以 Use 开头,如:

public static class RequestCultureMiddlewareExtensions{    public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)    {        return builder.UseMiddleware<RequestCultureMiddleware>();    }}
  • Startup.cs 中使用
public void Configure(IApplicationBuilder app, IWebHostEnvironment env){    // ... 其他中间件    app.UseRequestCulture();    // ... 其他中间件}

5.5.3.3 配置更多参数

默认情况下,自定义独立类中间件构造函数只有一个 RequestDelegate 参数,除此之后,还可以注入服务接口/类(建议是单例服务),另外还支持传入任何其他类型。

  • 服务类型参数
using System.Globalization;namespace Middleware.Example;public class RequestCultureMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger<RequestCultureMiddleware> _logger;    public RequestCultureMiddleware(RequestDelegate next        , ILogger<RequestCultureMiddleware> logger)    {        _next = next;        _logger = logger;    }    public async Task InvokeAsync(HttpContext context)    {        // 其他代码        _logger.LogInformation("...");        // 调用下一个中间件        await _next(context);    }}
  • 非服务类型参数

除此之外,还可以添加 非服务参数 参数,但必须是最后一个参数!!!

using System.Globalization;namespace Middleware.Example;public class RequestCultureMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger<RequestCultureMiddleware> _logger;    public RequestCultureMiddleware(RequestDelegate next        , ILogger<RequestCultureMiddleware> logger        , int age        , string name)    {        _next = next;        _logger = logger;    }    public async Task InvokeAsync(HttpContext context)    {        // 其他代码        _logger.LogInformation("...");        // 调用下一个中间件        await _next(context);    }}

之后还需要修改中间件拓展类:

public static class RequestCultureMiddlewareExtensions{    public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder, int age, string name)    {        return builder.UseMiddleware<RequestCultureMiddleware>(new object[] {age, name });    }}

使用:

app.UseRequestCulture(30, "百小僧");

5.5.4 中间件顺序

中间件是有执行顺序的,而且是先注册的先执行,无法通过其他方式更改,参考下图:

5.5.5 依赖注入/解析服务

中间件有两种方式注入服务,一种是通过构造函数注入,一种是通过 httpContext.RequestServices 方式解析。

5.5.5.1 构造函数方式

using System.Globalization;namespace Middleware.Example;public class RequestCultureMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger<RequestCultureMiddleware> _logger;    public RequestCultureMiddleware(RequestDelegate next        , ILogger<RequestCultureMiddleware> logger        , IHostEnvironment hostEnvironment)    {        _next = next;        _logger = logger;    }    public async Task InvokeAsync(HttpContext context)    {        // 其他代码        // 调用下一个中间件        await _next(context);    }}
特别说明

通过构造函数的方式建议注入 单例模式 的服务,否则可能存在服务不能释放问题,如需使用瞬时或作用域的服务,可使用下列 httpContext.RequestServices 方式

5.5.5.2 httpContext.RequestServices 方式

通过这种方式可以使用非单例服务解析:

using System.Globalization;namespace Middleware.Example;public class RequestCultureMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger<RequestCultureMiddleware> _logger;    // 构造函数注册单例    public RequestCultureMiddleware(RequestDelegate next        , ILogger<RequestCultureMiddleware> logger        , IHostEnvironment hostEnvironment)    {        _next = next;        _logger = logger;    }    public async Task InvokeAsync(HttpContext context)    {        // 非单例模式        using var serviceProvider = context.RequestServices.CreateScope();        var repository = serviceProvider.ServiceProvider.GetService<IRepository>();        // 调用下一个中间件        await _next(context);    }}

5.5.6 常见问题

由于中间件是比较原始的切面方式,有时候我们需要获取终点路由的特性或者其他信息,则需要一点技巧:

// 获取终点路由特性var endpointFeature = context.Features.Get<IEndpointFeature>();// 获取是否定义了特性var attribute = endpointFeature?.Endpoint?.Metadata?.GetMetadata<YourAttribute>()

5.5.7 了解更多

想了解更多中间件知识可阅读官方文档 【ASP.NET Core - 中间件

5.5.8 反馈与建议

与我们交流

给 Furion 提 Issue

演练场