1. 程式人生 > >學習ASP.NET Core, 怎能不瞭解請求處理管道[5]: 中介軟體註冊可以除了可以使用Startup之外,還可以選擇StartupFilter

學習ASP.NET Core, 怎能不瞭解請求處理管道[5]: 中介軟體註冊可以除了可以使用Startup之外,還可以選擇StartupFilter

中介軟體的註冊除了可以藉助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