今天推薦的這篇文章,講述了如何實現和使用ASP.NET 5的中間件。
雖然在ASP.NET 5中,微軟沒有再強調OWIN(Open Web Interface for .NET)及其微軟官方的OWIN實現Katana,但是其中涉及到一些原則和設計思想依然被ASP.NET 5以自己的方式所承載下來。比如,解耦服務器和應用程序的關系,應用程序委托,環境狀態這些特性都能在ASP.NET 5中找到,且進行了更多加強。
那么什么是“中間件”呢?OWIN的規范中如此定義:“中間件即是在服務器和應用程序之間的管道傳入的一些組件,為了特定目的監測、路由或編輯請求和回應消息。”這樣的定義對於ASP.NET 5同樣適用,或者可以被認為就是傳統ASP.NET中的HTTP模塊和處理器。某些中間件會完成一些中間任務,比如處理請求的驗證、會話狀態獲取和持久保持、日志記錄諸如此類;有一些中間件會最終生成回應消息。
要編寫ASP.NET 5的中間件,有一種非常簡單的方式,一段Lambda表達式就可以搞定:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(async context =>
{
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Hello ASP.NET 5!");
});
}
}
在上述代碼中,傳遞給IApplicationBuilder.Run方法的是一個委托:RequestDelegate,其定義如下:
public delegate Task RequestDelegate(HttpContext context);
RequestDelegate等效於OWIN中的AppFunc。其接受狀態信息HttpContext作為輸入參數,返回一個Task。注意,此HttpContext非SystemWeb中的HttpContext,這是封裝請求處理狀態且對服務器透明(不特定於某種服務器)的上下文狀態對象。而返回Task可以讓調用者能夠等待你的中間件完成工作后才進行后續任務執行。Run方法還有多個重載,以便讓你注入相關依賴。
RequestDelegate同樣也可以用於把中間件串接到執行管道中:
public class Startup { public void Configure(IApplicationBuilder app) { app.Use(next => async context => { // do your stuff here before calling the next middleware // in the pipeline await next.Invoke(context); // call the next guy // do some more stuff here as the call is unwinding }); app.Run(async context => { context.Response.ContentType = "text/plain"; await context.Response.WriteAsync("Hello ASP.NET 5!"); }); } }
通過使用IApplicationBuilder.Use方法就可以把自己的中間件代碼串到其他中間件的前面。其中next這個參數,就是下一個中間件的實例。其方法定義如下:
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate
上面是用Lambda表達式來實現中間件,不過在實際開發當中涉及的代碼都比較龐雜,所以最好是放到一個單獨的類當中,並提供相應的測試代碼。這樣你可以單獨編譯打包分發這個中間件。文章作者Andrei Dzimchuk以實現HTTP Basic驗證的一個簡單中間件為例給出了如下代碼:
public class BasicAuthentication { private readonly RequestDelegate next; public BasicAuthentication(RequestDelegate next) { this.next = next; } public async Task Invoke(HttpContext context, IAuthenticationService authenticationService) { try { var parser = new BasicAuthenticationParser(context); var username = parser.GetUsername(); var password = parser.GetPassword(); await authenticationService.AuthenticateAsync(username, password); await next(context); } catch (InvalidCredentialsException) { context.Response.StatusCode = 401; context.Response.Headers.Add("WWW-Authenticate", new[] { "Basic" }); } } }
這個類非常有意思。首先讓我們非常奇怪的是,它沒有繼承任何基類或者實現任何接口。由此可知,微軟開始在ASP.NET 5中推崇“約定勝於接口”的思想。我們只要實現一個接受RequestDelegate為參數的構造器,和一個方法簽名同RequestDelegate一致的Invoke方法。當然本例中Invoke還接受了另外一個參數,這就是第二個奇怪的地方,我們能夠在中間件里直接使用依賴注入。本例中就是注入了一個IAuthenticationService。
要使用編寫好的中間件也是非常簡單。首先引用一個依賴包“Microsoft.AspNet.RequestContainer ”,然后就可以使用Microsoft.AspNet.Http.Extensions的擴展方法IApplicationBuilder.UseMiddleware來加載中間件,如下:
builder.UseMiddleware<BasicAuthentication>();
通常,我們會把單獨編寫一個擴展類,來提供一個語義根據明確的擴展方法。最終Startup文件就可以編寫為:
public class Startup { public void Configure(IApplicationBuilder app) { app.UseBasicAuthentication(); app.Run(async context => { context.Response.ContentType = "text/plain"; await context.Response.WriteAsync("Hello ASP.NET 5!"); }); } }
到此,我們就完成了中間件的編寫和使用。當然還需要完成注冊IAuthenticationService這樣的代碼,這個就涉及到ASP.NET 5的依賴注入特性,有機會下次介紹。
原文地址在:http://dzimchuk.net/post/Understanding-ASPNET-5-middleware