學習ASP.NET Core, 怎能不瞭解請求處理管道[5]: 中介軟體註冊可以除了可以使用Startup之外,還可以選擇StartupFilter
中介軟體的註冊除了可以藉助Startup物件(DelegateStartup或者ConventionBasedStartup)來完成之外,也可以利用另一個叫做StartupFilter的物件來實現。所謂的StartupFilter是對所有實現了IStartupFilter介面的型別及其物件的統稱。IStartupFilter介面定義瞭如下一個唯一的方法Configure,該方法的引數next返回的Action<IApplicationBuilder>物件體現了後續StartupFilter和Startup對中介軟體的註冊,而自身對中介軟體的註冊則實現在返回的Action<IApplicationBuilder>物件中。[本文已經同步到《
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: publicvoid 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註冊的中介軟體先執行。對於兩個採用相同方式註冊的中介軟體,先被註冊的中間會先執行。