AppBuilder(二)【UseStageMarker】

風靈使發表於2018-07-09

原始碼參見

Microsoft.Owin.Host.SystemWeb.OwinBuilder

Microsoft.Owin.Builder.AppBuilder

Microsoft.Owin.Host.SystemWeb.OwinHttpModule

本節主要涉及app.UseStageMarker

先提提遇到的的三篇文章,講得很詳細的(鄙視那些轉載不註明出處的)

C# 中的委託和事件(詳解)

http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html

Difference Between Invoke and DynamicInvoke

http://stackoverflow.com/questions/12858340/difference-between-invoke-and-dynamicinvoke

C#中dynamic的正確用法

http://www.cnblogs.com/qiuweiguo/archive/2011/08/03/2125982.html

前文講到OWIN初始化的時候最開始的入口點不知道在哪兒,經過兩天的閱讀,發現這個了這個入口

Microsoft.Owin.Host.SystemWeb.PreApplicationStart這個類上有個Attribute定義

[assembly: PreApplicationStartMethod(typeof(PreApplicationStart), "Initialize")]

這應該是告訴未開源的部分進行初始化的,PreApplicationStart.Initialize方法將被呼叫,其工作為

HttpApplication.RegisterModule(typeof(OwinHttpModule))

因而在原始碼中可以預見OwinHttpModule將被初始化,其初始化程式碼

public void Init(HttpApplication context)
        {
            IntegratedPipelineBlueprint blueprint = LazyInitializer.EnsureInitialized(
                ref _blueprint,
                ref _blueprintInitialized,
                ref _blueprintLock,
                InitializeBlueprint);

            if (blueprint != null)
            {
                var integratedPipelineContext = new IntegratedPipelineContext(blueprint);
                integratedPipelineContext.Initialize(context);
            }
        }

上面的程式碼描述的是先確保_blueprint_blueprintInitialized_blueprintLock已被初始化,初始狀態很明顯_blueprint未被初始化(似乎_blueprint一直未被初始化),所以會呼叫InitializeBlueprint進行初始化。

private IntegratedPipelineBlueprint InitializeBlueprint()
        {
            IntegratedPipelineBlueprintStage firstStage = null;

            Action<IAppBuilder> startup = OwinBuilder.GetAppStartup();    //這就到了前面所講的流程了,尋找Startup,但在Invoke之前進行了EnableIntegratedPipeline呼叫
            OwinAppContext appContext = OwinBuilder.Build(builder =>
            {
                EnableIntegratedPipeline(builder, stage => firstStage = stage);
                startup.Invoke(builder);
            });

            string basePath = Utils.NormalizePath(HttpRuntime.AppDomainAppVirtualPath);    //獲取虛擬路徑
            return new IntegratedPipelineBlueprint(appContext, firstStage, basePath);    //pipeline已經構建完畢,返回第一個stage
        }

上面做的程式碼在AppBuilder進行pipeline中的middleware構建之前,啟用IntegratedPipelineBlueprint

//EnableIntegratedPipeline的第二個引數是一個Action
private static void EnableIntegratedPipeline(IAppBuilder app, Action<IntegratedPipelineBlueprintStage> onStageCreated)
        {
            var stage = new IntegratedPipelineBlueprintStage { Name = "PreHandlerExecute" };    //新建一個pipelineStage,只有 Name = "PreHandlerExecute",其他屬性未初始化
            onStageCreated(stage);    //實際上就是使firstStage指向剛新建的pipelineStage,如果你足夠仔細的話
    //你會發現這實際上是定義的pipelineStage的最後一個
            Action<IAppBuilder, string> stageMarker = (builder, name) =>
            {
                Func<AppFunc, AppFunc> decoupler = next =>
                {    //next是一個AppFunc委託,如果當前stage.Name與傳入的name相同,則仍然返回next,next實際上就是下一個stage的入口
                    if (string.Equals(name, stage.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        // no decoupling needed when pipeline is already split at this name
                        return next;
                    }
                    if (!IntegratedPipelineContext.VerifyStageOrder(name, stage.Name))    //如果當前的stage.Name的order小於傳入的name,仍然返回next
                    {
                        // Stage markers added out of order will be ignored.
                        // Out of order stages/middleware may be run earlier than expected.
                        // TODO: LOG
                        return next;
                    }    //如果當前的stage.Name的order大於傳入的name,將當前stage的入口點設定為next,新建一個pipelineStage
                    stage.EntryPoint = next;
                    stage = new IntegratedPipelineBlueprintStage
                    {
                        Name = name,
                        NextStage = stage,
                    };
                    onStageCreated(stage);    //更新firstStage
                    return (AppFunc)IntegratedPipelineContext.ExitPointInvoked;    //當前stage已經構建完畢,再用一個AppFunc作為當前middleware的返回值
                };
                app.Use(decoupler);    //decouper是一個Func<AppFunc,AppFunc>委託,所以會使用前一章所說的第一種app.Use方法,直接將其壓入List中
            };
            app.Properties[Constants.IntegratedPipelineStageMarker] = stageMarker;    //這裡將stageMarker繫結到app.Properties中,這將是本文的重點
            app.Properties[Constants.BuilderDefaultApp] = (Func<IDictionary<string, object>, Task>)IntegratedPipelineContext.DefaultAppInvoked;
        }

上文的原始碼與我們通常的思維有些出入,各個middleware是按順序壓入List的,而遍歷List重建的時候確實反向的,所以是從pipelineStage的最後一項PreHandlerExecute一直向前,並將屬於一個stage的所有middleware包裝在一個Func<AppFunc,AppFunc>中,第一個AppFunc是本stageEntryPoint,第二個AppFuncIntegratedPipelineContext.ExitPointInvoked,第二個AppFunc主要負責本stage完成之後的收尾工作,之後將呼叫本stage中的NextStage找到下一個stage,從下一個stage.EntryPoint開始執行。

在前文的InitializeBlueprintOwinBuilder.Build中完成了EnableIntegratedPipeline操作,接下來就是StartupConfiguration方法被呼叫,以新建MVC生成的預設的Configuration為例,UseCookieAuthentication將會被呼叫。

public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options, PipelineStage stage)
        {
            if (app == null)
            {
                throw new ArgumentNullException("app");
            }

            app.Use(typeof(CookieAuthenticationMiddleware), app, options);
            app.UseStageMarker(stage);
            return app;
        }

對於上面的原始碼我們只關注其先使用Use方法,再使用了UseStageMarker方法,標記了當前stagePipelineStage.Authenticate

public static IAppBuilder UseStageMarker(this IAppBuilder app, string stageName)
        {
            if (app == null)
            {
                throw new ArgumentNullException("app");
            }

            object obj;
            if (app.Properties.TryGetValue(IntegratedPipelineStageMarker, out obj))    //尋找app.Propertise中的StageMarker方法,也就是上文OwinHttpModule存進去的方法
            {
                var addMarker = (Action<IAppBuilder, string>)obj;
                addMarker(app, stageName);
            }
            return app;

        }

stageMarker主要做的工作上面已經介紹,在這裡的實際效果是將一個委託壓進List中,而這個委託作的工作是當前的PipelineStagePreHandlerExcute提前到了Authenticate,並完成了AuthenticateStage中的NextStage指向PreHandlerExcuteStagePreHandlerExcuteStageEntryPoint是最開始初始化的stage,只不過這個委託將在AppBuilder進行Build的時候才會Invoke

那麼AppBuilder是在何時進行Build的呢?在前面的某章曾提到Microsoft.Owin.Host.SystemWeb.OwinAppContext類中的Initialize方法的最後一行,其程式碼如下

AppFunc = (AppFunc)builder.Build(typeof(AppFunc))

上面的程式碼即開始了AppBuilder.Build方法,也是pipeline重建的開始的地方,沒想到卻在如此不起眼的地方。

public object Build(Type returnType)
        {
            return BuildInternal(returnType);
        }

可見定義了returnTypeFunc<IDictionary<string, object>, Task>的委託,而Build是對BuildInternal的封裝,下一章將閱讀BuilderInternal方法。

總結middleware注入與重建是兩個逆向的過程,按順序注入,反向遍歷重建,微軟工程師巧妙地保證了pipeline注入順序,且保證了在兩個StageMarkermiddleware被包裝在一個Func<AppFunc,AppFunc>中,經過約定每個middleware返回next,在未遇到ExitPointInvoked之前,都不會發生PipelineStage的切換。這將需要PipelineStage切換機制的支援和對middleware輸入引數、輸出引數的約定。現在還有些模糊的地方,在下一章將通過對IntegratedPipeline以及AppBuilder.Build的閱讀來說清楚這些流程。