源碼參見Microsoft.Owin.Builder.AppBuilderhtml
推薦三篇文章,對理解本文內容有幫助。c#
Delegate.CreateDelegate Method (Type, Object, MethodInfo) 官方文檔app
https://msdn.microsoft.com/en-us/library/74x8f551(v=vs.110).aspx函數
http://www.cnblogs.com/50614090/archive/2011/11/14/2248408.htmlthis
http://blog.csdn.net/argpunk/article/details/42121099.net
前文講到的AppBuilder.Build方法開始pipeline的重建,其其實是對AppBuilder.BuildInternal的封裝,傳入參數爲typeof(Func<IDictionary<string, object>, Task>),Func的參數爲IDictionary<string, object>,返回一個Task,這就是middleware能串起來所要遵循的規範之一,微軟工程師將其稱爲middleware的簽名。設計
先看看NotFound的初始化調試
1 private static readonly AppFunc NotFound = new NotFound().Invoke; //將NotFound.Invoke綁定到AppBuilder.NotFound上 2 internal class NotFound 3 { 4 private static readonly Task Completed = CreateCompletedTask(); 5 6 private static Task CreateCompletedTask() 7 { 8 var tcs = new TaskCompletionSource<object>(); 9 tcs.SetResult(null); 10 return tcs.Task; 11 } 12 13 public Task Invoke(IDictionary<string, object> env) //這是一個知足AppBuilder中對於AppFunc定義的一個方法,以前在這裏總是被繞暈了 14 { 15 env["owin.ResponseStatusCode"] = 404; //設置StatusCode 16 return Completed; //返回一個Task 17 } 18 }
上面的代碼展現了AppBuilder.NotFound是如何初始化爲一個AppFunc的,這是對中間件的簽名,對於後面的Convert方法來講相當重要。
1 private object BuildInternal(Type signature) 2 { 3 object app; 4 if (!_properties.TryGetValue(Constants.BuilderDefaultApp, out app)) //嘗試尋找默認的最後一步處理方法,若是尋找失敗則將app指向NotFound 5 { 6 app = NotFound; 7 } 8 9 foreach (var middleware in _middleware.Reverse()) //對List進行反向遍歷,反向遍歷很重要,這樣上一節所說的UseStageMarker對stage.Name的處理方式才能理解 10 { 11 Type neededSignature = middleware.Item1; //解耦三元組 12 Delegate middlewareDelegate = middleware.Item2; 13 object[] middlewareArgs = middleware.Item3; 14 15 app = Convert(neededSignature, app); //嘗試將app的Invoke方法建立爲一個委託,委託爲needSignature所表示的Type,聽起來有點繞,不要緊,慢慢來 16 //這將涉及到pipeline中AppFunc與Middleware的轉換,這是OWIN的精華所在 17 object[] invokeParameters = new[] { app }.Concat(middlewareArgs).ToArray(); //將app做爲第一個參數與args合併 18 app = middlewareDelegate.DynamicInvoke(invokeParameters); 19 app = Convert(neededSignature, app); //這一步我也沒大懂,到後面懂了再說 20 } 21 22 return Convert(signature, app); //同理這一步我也沒大懂 23 }
從實際例子出發容易理解上面的流程一些,上一章講到UseCookieAuthentication方法中先調用app.Use(typeof(CookieAuthenticationMiddleware), app, options),再調用app.UseStageMarker(stage),這實際上會調用app.Use(decoupler)方法,而decoulper是一個Func<AppFunc,AppFunc>委託,因此當前進行_middleware.Reverse遍歷的時候,最早取到的就是app.Use(decoupler)壓進去的委託。
而參考上上一章對AppBuilder.Use方法的總結,實際上會調用第一種Use處理流程,因此上面源代碼中middleware中的三元組對應的類型以下
Item1 |
GetParameterType(an instance of (Func<AppFunc,AppFunc>)),結果爲typeof(AppFunc) = typeof(Func<Idictionary<string, object>, Task>) = a special Delegate,是一個委託 |
Item2 |
Func<AppFunc,AppFunc> 委託的一個實例,對應decoupler |
Item3 |
New object[0] 爲空 |
因此Convert(neededSignature, app)能夠替換成Convert(a special Delegate, an instance of Func<Idictionary<string, object>, Task>)
來看看Convert作了什麼。
1 private object Convert(Type signature, object app) 2 { 3 if (app == null) 4 { 5 return null; 6 } 7 8 object oneHop = ConvertOneHop(signature, app); 9 if (oneHop != null) 10 { 11 return oneHop; 12 } 13 14 object multiHop = ConvertMultiHop(signature, app); 15 if (multiHop != null) 16 { 17 return multiHop; 18 } 19 throw new ArgumentException( 20 string.Format(CultureInfo.CurrentCulture, Resources.Exception_NoConversionExists, app.GetType(), signature), 21 "signature"); 22 23 }
Covert其實是對ConvertOneHop和ConvertMultiHop的封裝。
先看看ConvertOneHop方法。
1 private object ConvertOneHop(Type signature, object app) 2 { 3 if (signature.IsInstanceOfType(app)) //針對上面的例子,app確實是signature的一個實例,都對應Func<Idictionary<string, object>, Task> 4 { 5 return app; //因此第一次調用會直接返回 6 } 7 if (typeof(Delegate).IsAssignableFrom(signature)) //若是signature是對Delegate的繼承 8 { 9 Delegate memberDelegate = ToMemberDelegate(signature, app); //嘗試將app的Invoke方法建立爲一個signature所表示的Type類型的委託 10 if (memberDelegate != null) 11 { 12 return memberDelegate; 13 } 14 } 15 foreach (var conversion in _conversions) //若是app的Invoke方法與signature的Invoke方法衝突,須要進行轉換 16 //這是Middleware與AppFunc之間的重要轉換,也是pipeline的重點,留到後文詳述 17 { 18 Type returnType = conversion.Key.Item1; 19 Type parameterType = conversion.Key.Item2; 20 if (parameterType.IsInstanceOfType(app) && 21 signature.IsAssignableFrom(returnType)) 22 { 23 return conversion.Value.DynamicInvoke(app); 24 } 25 } 26 return null; 27 }
再回頭看看_middleware.Rerverse遍歷的第一次中,Convert(needSignature,app)會很快返回,值就是app,也就是Func<Idictionary<string, object>, Task>的一個實例,再運行app = middlewareDelegate.DynamicInvoke(invokeParameters)的時候,由於app已經合併進invokeParameters中因此,等同於執行
1 app => 2 { 3 if (string.Equals(name, stage.Name, StringComparison.OrdinalIgnoreCase)) //name = "Authenticate", stage.Name = "PreHandlerExecute",返回false 4 { 5 // no decoupling needed when pipeline is already split at this name 6 return app ; 7 } 8 if (!IntegratedPipelineContext.VerifyStageOrder(name, stage.Name)) //name = "Authenticate", stage.Name = "PreHandlerExecute",name < stage.Name,返回false,注意前面有個'!' 9 { 10 // Stage markers added out of order will be ignored. 11 // Out of order stages/middleware may be run earlier than expected. 12 // TODO: LOG 13 return app ; 14 } 15 stage.EntryPoint = app ; //設置PreHandlerExecute這一Stage的EntryPoint爲app,此時的app就是NotFound.Invoke方法 16 stage = new IntegratedPipelineBlueprintStage //爲Authenticate新建一個IntegratedPipelineBlueprintStage,NextStage綁定到PreHandlerExcute這一Stage上 17 //因此兩個PipelineStage就連接起來了 18 { 19 Name = name, 20 NextStage = stage, 21 }; 22 onStageCreated(stage); //更新firstStage,使其指向Autenticate這一Stage 23 return (AppFunc)IntegratedPipelineContext.ExitPointInvoked; //返回ExitPointInvoked方法 24 };
上面的代碼演示了PreHandlerExcute和Authenticate兩個PipelineStage是如何串接在一塊兒的,再來看看IntegratedPipelineContext.ExitPointInvoked到底幹了什麼。
1 public static Task ExitPointInvoked(IDictionary<string, object> env) 2 { 3 object value; 4 if (env.TryGetValue(Constants.IntegratedPipelineContext, out value)) //嘗試從environment中獲取IntegratedPipelineContext實例, 5 { 6 var self = (IntegratedPipelineContext)value; 7 return self._state.ExecutingStage.ExitPointInvoked(env); //改變當前管道狀態,使其能夠流入下一管道 8 } 9 throw new InvalidOperationException(); 10 } 11 public Task ExitPointInvoked(IDictionary<string, object> env) 12 { 13 _context.PreventNextStage = false; //改變當前管道狀態 14 return Epilog(env); //進行最後的收尾工做 15 } 16 17 private Task Epilog(IDictionary<string, object> env) 18 { 19 var tcs = new TaskCompletionSource<object>(); 20 _responseShouldEnd = false; //開啓response,由於即將進行Stage的切換,與Stage剛開始執行的時候關閉response相對應 21 _context.PushLastObjects(env, tcs); //驗證當前pipeline所在Stage中的environment爲null,TaskCompletionSource<object>爲null,由於即將離開Stage,而Stage是公用的 22 //這與IntegratedPipelineContextStage.BeginEvent中的TakeLastEnvironment,TakeLastCompletionSource相對應,都是原子操做 23 StageAsyncResult result = Interlocked.Exchange(ref _result, null); 24 if (result != null) 25 { 26 result.TryComplete(); 27 } 28 return tcs.Task; 29 }
扯了好遠,在沒有進行調試的狀況下推斷這些運行流程還真是很累的一件事兒。這對於前面沒有搞懂的地方有很大幫助,看代碼。
app = middlewareDelegate.DynamicInvoke(invokeParameters)執行以後,app = (AppFunc)IntegratedPipelineContext.ExitPointInvoked了,這就是PreHandlerExecute的收尾工做。
以後再次執行了app = Convert(neededSignature, app),此時的參數app仍然是一個AppFunc,因此仍是會很快返回,進入下一循環。
此次_middleware.Rerverse遍歷獲取到的應該是app.Use(typeof(CookieAuthenticationMiddleware), app, options)壓進去的CookieAuthenticationMiddleware。
參考AppBuilder(一)那一節所分析的結果,由於傳入的參數是一個Type,args長度爲2,因此會採用第四種方法來處理,以下
private static Tuple<Type, Delegate, object[]> ToConstructorMiddlewareFactory(object middlewareObject, object[] args, ref Delegate middlewareDelegate)
這個方法嘗試尋找middlewareObject類中的參數個數爲args長度+1,便是3個的構造函數。如下是對應的構造函數
public CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options) : base(next, options)
因此能夠推斷出此時取到的middleware三元組爲
Item1 |
OwinMiddleware的Type |
Item2 |
CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options) 構造函數 |
Item3 |
[IAppBuilder app, CookieAuthenticationOptions options] 長度爲2的object[] |
再次執行
app = Convert(needSignature, app)…>object oneHop = ConvertOneHop(signature, app)
此時的參數app是一個AppFunc,而signature是一個OwinMiddleware,會用到_conversions,這將是OwinMiddleware與AppFunc之間互相轉換的實現,須要用到AppBuilder時候對_conversions初始化的知識,留待下一章再說。
總結AppBuilder.BuildeInternal對middleware的List遍歷是反向的,雖然如今還不明白爲何如此設計,並且如何在一個PipelineStage中執行多個middleware也還不明朗,曾經覺得是使用相似Invoke += Middleware.Invoke實現的,但既然是反向的,這不就順序反了嗎?
目前能肯定下來的時候每一個PipelineStage的EntryPoint已經顯式指定了,剛剛大概又想了一下,爲了保證PipelineStage的規範性,那麼每一個PipelineStage應該都是一個Func<AppFunc, AppFunc>形式的纔對,而Middleware應該是被封裝在這兩個AppFunc之間的,這麼說,應該是_conversions來完成了同一個PipelineStage中的Middleware的串聯工做了,理應如此。下一節再驗證這個問題。