隐藏

ASP.NET 6 中间件系列之条件中间件

发布:2023/1/12 15:48:38作者:管理员 来源:本站 浏览次数:475

在本系列的最后一部分中,我们将展示在管道中有条件地执行中间件的两种方法:


   采用 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.com/post/123?culture=zh-CN


CultureMiddleware会将应用的区域设置为zh-CN,然后再进行其它正常的处理。


如果传入的请求是:


http://codeman.com/post/123


那么,中间件将会什么都不做,应用程序的区域仍然是默认的。


这种有条件地执行中间件的方法,比 AppSettings.json 解决方案提供了更细粒度的执行控制,但潜在的代价是始终需要将中间件添加到管道中。


以TimeLoggingMiddleware为例:


如果传入的请求包含特定的值,才执行TimeLoggingMiddleware的代码;


在 MVC 应用程序,也许我们只想记录单个控制器的执行时;


在 Razor 应用中,可能只想记录有问题的页面;


在 Web API 中,只是其中一个端点有些慢。


在这些情况下,我们都可以使用这种方法将TimeLoggingMiddleware定位到我们想要记录的内容。