这篇文章是 ASP.NET 6 中间件系列文章的第 4 部分。
到目前为止,我们已经介绍了 ASP.NET 6 中间件的基础知识,展示了如何创建自定义中间件类,并讨论了中间件执行顺序的重要性。
在本系列的最后一部分中,我们将展示在管道中有条件地执行中间件的两种方法:
采用 AppSettings.json 文件中的设置,来确定是否要添加中间件到管道中;
通过使用传入请求的数据,有条件地执行已经在管道中的中间件。
基于 AppSettings 的条件中间件
让我们回顾一下,上一篇文章中的TimeLoggingMiddleware
类:
using MiddlewareNET6Demo.Logging;
using System.Diagnostics;namespace MiddlewareNET6Demo.Middleware
{public class TimeLoggingMiddleware{private readonly RequestDelegate _next;private readonly ILoggingService _logger;public TimeLoggingMiddleware(RequestDelegate next, ILoggingService logger){_next = next;_logger = logger;}public async Task InvokeAsync(HttpContext context){Stopwatch watch = new Stopwatch();watch.Start();await _next(context);watch.Stop();_logger.Log(LogLevel.Information, "Time to execute: " + watch.ElapsedMilliseconds + " milliseconds.");}}
}
现在,我们只希望在特定的条件(比如,当我们在追踪一个 BUG,或者应用程序运行缓慢等)下将TimeLoggingMiddleware
添加到应用程序管道中。
为了有条件地向管道中添加中间件,我们可以在 AppSettings.json 文件中设置一个可以在Program.cs
中读取的字段。
在 AppSettings.json 文件中添加一个名为MiddlewareSettings
的配置字段,以及一个名为UseTimeLoggingMiddleware
的配置项:
{"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AllowedHosts": "*","MiddlewareSettings": {"UseTimeLoggingMiddleware": "true",}
}
我们还需要一个类来保存这些设置的值。按照约定,类名应该与配置字段名MiddlewareSettings
匹配,而属性名应该与配置项名UseTimeLoggingMiddleware
匹配:
namespace MiddlewareNET6Demo
{public class MiddlewareSettings{public bool UseTimeLoggingMiddleware { get; set; }}
}
然后,在 Program.cs 文件中,我们可以读取 AppSettings.json 的那一部分,并将其映射到MiddlewareSettings
类的的一个实例上:
var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();
builder.Services.AddTransient<ILoggingService, LoggingService>();var app = builder.Build();var middlewareSettings = builder.Configuration.GetSection("MiddlewareSettings").Get<MiddlewareSettings>();
只有当middlewareSettings
实例的UseTimeLoggingMiddleware
属性值为true
时,我们才能将TimeLoggingMiddleware
添加到管道中:
//...if(middlewareSettings.UseTimeLoggingMiddleware)app.UseTimeLoggingMiddleware();//...
通过这种方式,我们可以根据应用程序的设置,来控制哪个中间件在管道中处于活动状态。
基于请求 URL 的条件中间件
另一种方法可能带有欺骗性质,因为中间件总是会被添加到管道中,但是它除了将执行传递给下一个中间件之外,不会做任何其它事情。
假设我们有一个新的中间件类叫做CultureMiddleware
:
using System.Globalization;namespace MiddlewareNET6Demo.Middleware
{public class CultureMiddleware{private readonly RequestDelegate _next;public CultureMiddleware(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);}}
}
请注意,该中间件仅在传入请求中存在culture
请求参数时,才会执行一些实际工作。
如果该参数存在,中间件将应用程序的当前区域设置为传入参数的值。
例如,如果我们提交了以下请求:
http://codeman.tech/post/123?culture=zh-CN
CultureMiddleware
会将应用的区域设置为zh-CN
,然后再进行其它正常的处理。
如果传入的请求是:
http://codeman.tech/post/123
那么,中间件将会什么都不做,应用程序的区域仍然是默认的。
这种有条件地执行中间件的方法,比 AppSettings.json 解决方案提供了更细粒度的执行控制,但潜在的代价是始终需要将中间件添加到管道中。
以TimeLoggingMiddleware
为例:
如果传入的请求包含特定的值,才执行TimeLoggingMiddleware
的代码;
在 MVC 应用程序,也许我们只想记录单个控制器的执行时;
在 Razor 应用中,可能只想记录有问题的页面;
在 Web API 中,只是其中一个端点有些慢。
在这些情况下,我们都可以使用这种方法将TimeLoggingMiddleware
定位到我们想要记录的内容。