首先特別說明下在startup中注冊完中間件的兩個注意事項,看到有人寫的東西有誤導人的作用。關於startup啟動發現類的內容,參照這里 http://www.asp.net/aspnet/overview/owin-and-katana/owin-startup-class-detection
1. 使用IApplicationBuilder.User注冊中間件是有先后順序關系的。
2. 注冊的中間件的執行過程是這樣的:輸入初始化是按照順序來的,輸出執行是反順序來的。
請求發生-->初始化中間件1--->初始化中間件n-->app忽略中間件方法,直接響應輸出--------->中間件n Invoke執行處理-->中間件1 Invoke執行處理--->響應輸出
進入正文
OWIN middleware 必須是具有以下代碼特征,要么是直接在startup類中直接注冊,要么就是寫的中間件類中方法返回。
Func<IDictionary<string, object>, Task> //這個function具有一個上下文的字典參數(OWIN environment dictionary),並返回Task
這段特征碼中的IDictionary<string, object>其實已經被katana的server層包裝成字典形式的請求上下文(HttpContext)IOwinContext,可以使用上下文屬性environment訪問字典值
我們會以這種形式注冊中間件
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); //middleware 可以是委托 or 類型 or 實例
中間件注冊進入管道,由於中間件返回一個Task,就能保證管道在處理的時候能找到下一個執行的Task(就是下邊代碼中的next參數,那么next就是已知的第一個RequestDelegate參數)來處理請求和響應。Task就是返回的具有中間件特征的中間件。
下面我們看下第一種寫法
app.Use(new Func<RequestDelegate, RequestDelegate>(next => (async context => { Console.WriteLine("初始化組件開始"); await next.Invoke(context); Console.WriteLine("管道下步執行完畢"); })));
以上代碼中會在請求時在控制台輸出“初始化組件開始”,當組件的下一個步驟執行完畢后,會再打印出“管道下步執行完畢”。
有時候組件里沒啥規則,但是也必須接受next作為參數,但是可以忽略它,並且仍然需要返回一個task,所以可以這樣寫
app.Use(new Func<RequestDelegate, RequestDelegate>(ignoreNext => (content=> { Console.WriteLine("The request ends with me!"); return Task.FromResult(0); })));
第二種寫法是將已有的方法傳遞給委托。如果你有一些邏輯需要抽象出來,但又不想單獨寫一個中間件類,這個寫法就比較合適
public class Startup { public void Configuration(IAppBuilder app) { app.Use(new Func<RequestDelegate, RequestDelegate>(next => content=> Invoke(next, content))); } private async Task Invoke(RequestDelegate next, IDictionary<string, object> environment) { Console.WriteLine("初始化組件開始"); await next.Invoke(environment); Console.WriteLine("管道下步執行完畢"); } }
如果使用一下代碼注冊組件,那么久必須寫一個實際的中間件類了
app.Use(typeof(LoggingMiddleware));
或者以中間件實例來注冊
app.Use(new LoggingMiddleware());
第三種寫法是實現一個實際的中間件類
public class LoggingMiddleware { private RequestDelegate next; public LoggingMiddleware(RequestDelegate next) { this.next = next; } public async Task Invoke(IDictionary<string, object> environment) { Console.WriteLine("初始化組件開始"); await next.Invoke(environment); Console.WriteLine("管道執行完畢"); } }
第四種寫法我們可以集成Microsoft.Owin庫中的OwinMiddleware基類來實現中間件類。它提供了強類型訪問IOwincontext。
public class LoggingMiddleware : OwinMiddleware { public LoggerMiddleware(OwinMiddleware next) : base(next) { } public async override Task Invoke(IOwinContext context) { Console.WriteLine("初始化組件開始"); await Next.Invoke(context); Console.WriteLine("管道執行完畢"); } }
以上幾種寫法實現都干了一樣的事情,其實更多復雜的中間件定義可以參考下Microsoft.AspNet.Diagnostics下的幾種中間件實現方式,比入WelcomePageMiddleware.cs,就是我們在app中使用UserWelcomPage()方法注冊的中間件。