文章出處

中間件的注冊除了可以借助Startup對象(DelegateStartup或者ConventionBasedStartup)來完成之外,也可以利用另一個叫做StartupFilter的對象來實現。所謂的StartupFilter是對所有實現了IStartupFilter接口的類型及其對象的統稱。IStartupFilter接口定義了如下一個唯一的方法Configure,該方法的參數next返回的Action<IApplicationBuilder>對象體現了后續StartupFilter和Startup對中間件的注冊,而自身對中間件的注冊則實現在返回的Action<IApplicationBuilder>對象中。[本文已經同步到《ASP.NET Core框架揭秘》之中]

   1: public interface IStartupFilter
   2: {
   3:     Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);
   4: }

我們可以采用服務注冊的方式注冊多個StartupFilter。具體來說,StartupFilter具有如下兩種不同的注冊方式,一種是通過調用WebHostBuilder的ConfigureServices方法以服務的形式注冊所需的StartupFilter,另一種則是將針對StartupFilter的服務注冊實現在啟動類的ConfigureServices方法上。

   1: //注冊方式1
   2: new WebHostBuilder()
   3:     .ConfigureServices(svcs => svcs
   4:         .AddSingleton<IStartupFilter, Filter1>()
   5:         .AddSingleton<IStartupFilter, Filter2>())                    
   6:     …
   7:  
   8: //注冊方式2
   9: public class Startup
  10: {
  11:     public void ConfigureServices(IServiceCollection svcs)
  12:     {
  13:         svcs.AddSingleton<IStartupFilter,Filter1>()
  14:             .AddSingleton<IStartupFilter, Filter2>();
  15:     }    
  16: }

既然中間件可以同時通過Startup和StartupFilter進行注冊,那么通過這兩個種方式注冊的中間件有何不同嗎?其實它們唯一的區別在于StartupFilter注冊的中間件會先執行。話句話說,對于由注冊中間件構成的管道來說,通過Startup注冊的中間件位于通過StartupFilter注冊的中間件之后。我們不妨通過一個簡單的實例來證實這一點。我們在一個ASP.NET Core控制臺應用中定義如下四個中間件類型(Foo、Bar、Baz和Gux),它們針對請求的處理邏輯很簡單,就是將自身的類型名稱寫入請求的響應中。

   1: public abstract class MiddlewareBase
   2: {
   3:     private RequestDelegate _next;
   4:  
   5:     public MiddlewareBase(RequestDelegate next)
   6:     {
   7:         _next = next;
   8:     }
   9:     public async Task Invoke(HttpContext context)
  10:     {
  11:         await context.Response.WriteAsync($"{this.GetType().Name}=>");
  12:         await _next(context);
  13:     }
  14: }
  15:  
  16: public class Foo : MiddlewareBase
  17: {
  18:     public Foo(RequestDelegate next) : base(next){}
  19: }
  20: public class Bar : MiddlewareBase
  21: {
  22:     public Bar(RequestDelegate next) : base(next) {}
  23: }
  24: public class Baz : MiddlewareBase
  25: {
  26:     public Baz(RequestDelegate next) : base(next) {}
  27: }
  28: public class Gux : MiddlewareBase
  29: {
  30:     public Gux(RequestDelegate next) : base(next) {}
  31: }

接下來我們定義了如下一個泛型的 StartupFilter<TMiddleware>類,這是一個專門用于注冊指定類型中間件的StartupFilter,泛型參數代表注冊的中間件類型。在實現的Configure方法中,我們將中間件的注冊實現在返回的Action<IApplicationBuilder>對象中。

   1: public class StartupFilter<TMiddleware> : IStartupFilter
   2: {
   3:     public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
   4:     {
   5:         return app=> {
   6:             app.UseMiddleware<TMiddleware>();
   7:             next(app);
   8:         };
   9:     }
  10: }

我們最終編寫如下一段簡單的程序來啟動承載的應用程序。如下面的額代碼片段所示,在利用WebHostBuilder創建并啟動WebHost之前,我們調用其ConfigureServices方法注冊了兩個StartupFilter<TMiddleware>對象,它們對應的中間件類型分別為Foo和Bar。在隨后調用的Configure方法中,我們又完成了針對中間Baz和Gux的注冊。這段程序實際上注冊了五個中間件(調用ApplicationBuilder的Run方法可以視為中間件注冊)。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .ConfigureServices(svcs => svcs
   8:                 .AddSingleton<IStartupFilter>(new StartupFilter<Foo>())
   9:                 .AddSingleton<IStartupFilter>(new StartupFilter<Bar>()))
  10:             .Configure(app => app
  11:                 .UseMiddleware<Baz>()
  12:                 .UseMiddleware<Gux>()
  13:                 .Run(async context=> await context.Response.WriteAsync("End")))
  14:             .Build()
  15:             .Run();
  16:     }
  17: }

我們現在需要確定注冊的這五個在進行請求處理過程中的執行順序。為此我們直接啟動這個程序,然后開啟瀏覽器訪問默認的監聽地址(http://localhost:5000),瀏覽器會按照如下圖所示形式顯示出請求在這個五個中間件中的“路由”。瀏覽器顯示的結果清晰地表明通過StartupFilter注冊的中間件比通過Startup注冊的中間件先執行。對于兩個采用相同方式注冊的中間件,先被注冊的中間會先執行。

9


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()