AppBuilder(三)【BuildInternal】

風靈使發表於2018-07-09

原始碼參見Microsoft.Owin.Builder.AppBuilder

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

Delegate.CreateDelegate Method (Type,?Object,?MethodInfo) 官方文件

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

c#委託(delegate)揭祕

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

C#中delegate的機制原理

http://blog.csdn.net/argpunk/article/details/42121099

前文講到的AppBuilder.Build方法開始pipeline的重建,其實際上是對AppBuilder.BuildInternal的封裝,傳入引數為typeof(Func<IDictionary<string, object>, Task>)Func的引數為IDictionary<string, object>,返回一個Task,這就是middleware能串起來所要遵循的規範之一,微軟工程師將其稱為middleware的簽名。

先看看NotFound的初始化

private static readonly AppFunc NotFound = new NotFound().Invoke;    //將NotFound.Invoke繫結到AppBuilder.NotFound上
internal class NotFound
    {
        private static readonly Task Completed = CreateCompletedTask();

        private static Task CreateCompletedTask()
        {
            var tcs = new TaskCompletionSource<object>();
            tcs.SetResult(null);
            return tcs.Task;
        }

        public Task Invoke(IDictionary<string, object> env)    //這是一個滿足AppBuilder中對於AppFunc定義的一個方法,之前在這裡老是被繞暈了
        {
            env["owin.ResponseStatusCode"] = 404;    //設定StatusCode
            return Completed;    //返回一個Task
        }
    }

上面的程式碼展示了AppBuilder.NotFound是如何初始化為一個AppFunc的,這是對中介軟體的簽名,對於後面的Convert方法來說至關重要。

private object BuildInternal(Type signature)
        {
            object app;
            if (!_properties.TryGetValue(Constants.BuilderDefaultApp, out app))    //嘗試尋找預設的最後一步處理方法,如果尋找失敗則將app指向NotFound
            {
                app = NotFound;
            }

            foreach (var middleware in _middleware.Reverse())    //對List進行反向遍歷,反向遍歷很重要,這樣上一節所說的UseStageMarker對stage.Name的處理方式才能理解
            {
                Type neededSignature = middleware.Item1;    //解耦三元組
                Delegate middlewareDelegate = middleware.Item2;
                object[] middlewareArgs = middleware.Item3;

                app = Convert(neededSignature, app);    //嘗試將app的Invoke方法建立為一個委託,委託為needSignature所表示的Type,聽起來有點繞,沒關係,慢慢來
    //這將涉及到pipeline中AppFunc與Middleware的轉換,這是OWIN的精華所在
                object[] invokeParameters = new[] { app }.Concat(middlewareArgs).ToArray();    //將app作為第一個引數與args合併
                app = middlewareDelegate.DynamicInvoke(invokeParameters);
                app = Convert(neededSignature, app);    //這一步我也沒大懂,到後面懂了再說
            }

            return Convert(signature, app);    //同理這一步我也沒大懂
        }

從實際例子出發容易理解上面的流程一些,上一章講到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做了什麼。

private object Convert(Type signature, object app)
        {
            if (app == null)
            {
                return null;
            }

            object oneHop = ConvertOneHop(signature, app);
            if (oneHop != null)
            {
                return oneHop;
            }

            object multiHop = ConvertMultiHop(signature, app);
            if (multiHop != null)
            {
                return multiHop;
            }
            throw new ArgumentException(
                string.Format(CultureInfo.CurrentCulture, Resources.Exception_NoConversionExists, app.GetType(), signature),
                "signature");

        }

Covert實際上是對ConvertOneHopConvertMultiHop的封裝。

先看看ConvertOneHop方法。

private object ConvertOneHop(Type signature, object app)
        {
            if (signature.IsInstanceOfType(app))    //針對上面的例子,app確實是signature的一個例項,都對應Func<Idictionary<string, object>, Task>
            {
                return app;    //所以第一次呼叫會直接返回
            }
            if (typeof(Delegate).IsAssignableFrom(signature))    //如果signature是對Delegate的繼承
            {
                Delegate memberDelegate = ToMemberDelegate(signature, app);    //嘗試將app的Invoke方法建立為一個signature所表示的Type型別的委託
                if (memberDelegate != null)
                {
                    return memberDelegate;
                }
            }
            foreach (var conversion in _conversions)    //如果app的Invoke方法與signature的Invoke方法衝突,需要進行轉換
    //這是Middleware與AppFunc之間的重要轉換,也是pipeline的重點,留到後文詳述
            {
                Type returnType = conversion.Key.Item1;
                Type parameterType = conversion.Key.Item2;
                if (parameterType.IsInstanceOfType(app) &&
                    signature.IsAssignableFrom(returnType))
                {
                    return conversion.Value.DynamicInvoke(app);
                }
            }
            return null;
        }

再回頭看看_middleware.Rerverse遍歷的第一次中,Convert(needSignature,app)會很快返回,值就是app,也就是Func<Idictionary<string, object>, Task>的一個例項,再執行app = middlewareDelegate.DynamicInvoke(invokeParameters)的時候,因為app已經合併進invokeParameters中所以,等同於執行

app =>
    {
                    if (string.Equals(name, stage.Name, StringComparison.OrdinalIgnoreCase))    //name = "Authenticate", stage.Name = "PreHandlerExecute",返回false
                    {
                        // no decoupling needed when pipeline is already split at this name
                        return app ;
                    }
                    if (!IntegratedPipelineContext.VerifyStageOrder(name, stage.Name))    //name = "Authenticate", stage.Name = "PreHandlerExecute",name < stage.Name,返回false,注意前面有個'!'
                    {
                        // Stage markers added out of order will be ignored.
                        // Out of order stages/middleware may be run earlier than expected.
                        // TODO: LOG
                        return app ;
                    }
                    stage.EntryPoint = app ;    //設定PreHandlerExecute這一Stage的EntryPoint為app,此時的app就是NotFound.Invoke方法
                    stage = new IntegratedPipelineBlueprintStage    //為Authenticate新建一個IntegratedPipelineBlueprintStage,NextStage繫結到PreHandlerExcute這一Stage上
    //所以兩個PipelineStage就連結起來了
                    {
                        Name = name,
                        NextStage = stage,
                    };
                    onStageCreated(stage);    //更新firstStage,使其指向Autenticate這一Stage
                    return (AppFunc)IntegratedPipelineContext.ExitPointInvoked;    //返回ExitPointInvoked方法
                };

上面的程式碼演示了PreHandlerExcuteAuthenticate兩個PipelineStage是如何串接在一起的,再來看看IntegratedPipelineContext.ExitPointInvoked到底幹了什麼。

public static Task ExitPointInvoked(IDictionary<string, object> env)
        {
            object value;
            if (env.TryGetValue(Constants.IntegratedPipelineContext, out value))    //嘗試從environment中獲取IntegratedPipelineContext例項,
            {
                var self = (IntegratedPipelineContext)value;
                return self._state.ExecutingStage.ExitPointInvoked(env);    //改變當前管道狀態,使其可以流入下一管道
            }
            throw new InvalidOperationException();
        }
 public Task ExitPointInvoked(IDictionary<string, object> env)
        {
            _context.PreventNextStage = false;    //改變當前管道狀態
            return Epilog(env);    //進行最後的收尾工作
        }

        private Task Epilog(IDictionary<string, object> env)
        {
            var tcs = new TaskCompletionSource<object>();
            _responseShouldEnd = false;    //開啟response,因為即將進行Stage的切換,與Stage剛開始執行的時候關閉response相對應
            _context.PushLastObjects(env, tcs);    //驗證當前pipeline所在Stage中的environment為null,TaskCompletionSource<object>為null,因為即將離開Stage,而Stage是公用的
    //這與IntegratedPipelineContextStage.BeginEvent中的TakeLastEnvironment,TakeLastCompletionSource相對應,都是原子操作
            StageAsyncResult result = Interlocked.Exchange(ref _result, null);
            if (result != null)
            {
                result.TryComplete();
            }
            return tcs.Task;
        }

扯了好遠,在沒有進行除錯的情況下推斷這些執行流程還真是很累的一件事兒。這對於前面沒有搞懂的地方有很大幫助,看程式碼。

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 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,這將是OwinMiddlewareAppFunc之間互相轉換的實現,需要用到AppBuilder時候對_conversions初始化的知識,留待下一章再說。

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

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