AppBuilder(三)BuildInternal

源碼參見Microsoft.Owin.Builder.AppBuilderhtml

推薦三篇文章,對理解本文內容有幫助。c#

Delegate.CreateDelegate Method (Type, Object, MethodInfo) 官方文檔app

https://msdn.microsoft.com/en-us/library/74x8f551(v=vs.110).aspx函數

c#委託(delegate)揭祕ui

http://www.cnblogs.com/50614090/archive/2011/11/14/2248408.htmlthis

C#中delegate的機制原理spa

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其實是對ConvertOneHopConvertMultiHop的封裝。

先看看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                 };

上面的代碼演示了PreHandlerExcuteAuthenticate兩個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(一)那一節所分析的結果,由於傳入的參數是一個Typeargs長度爲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

OwinMiddlewareType

Item2

CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options)  構造函數

Item3

[IAppBuilder app,  CookieAuthenticationOptions options] 長度爲2object[]

再次執行

app = Convert(needSignature, app)…>object oneHop = ConvertOneHop(signature, app)

此時的參數app是一個AppFunc,而signature是一個OwinMiddleware,會用到_conversions,這將是OwinMiddlewareAppFunc之間互相轉換的實現,須要用到AppBuilder時候對_conversions初始化的知識,留待下一章再說。

總結AppBuilder.BuildeInternalmiddlewareList遍歷是反向的,雖然如今還不明白爲何如此設計,並且如何在一個PipelineStage中執行多個middleware也還不明朗,曾經覺得是使用相似Invoke += Middleware.Invoke實現的,但既然是反向的,這不就順序反了嗎?

目前能肯定下來的時候每一個PipelineStageEntryPoint已經顯式指定了,剛剛大概又想了一下,爲了保證PipelineStage的規範性,那麼每一個PipelineStage應該都是一個Func<AppFunc, AppFunc>形式的纔對,而Middleware應該是被封裝在這兩個AppFunc之間的,這麼說,應該是_conversions來完成了同一個PipelineStage中的Middleware的串聯工做了,理應如此。下一節再驗證這個問題。

相關文章
相關標籤/搜索