AppBuilder(二)UseStageMarker

源碼參見Microsoft.Owin.Host.SystemWeb.OwinBuilderhtml

      Microsoft.Owin.Builder.AppBuilderapp

     Microsoft.Owin.Host.SystemWeb.OwinHttpModuleui

本節主要涉及app.UseStageMarkerthis

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

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

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

Difference Between Invoke and DynamicInvokehtm

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

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將被初始化,其初始化代碼

 1 public void Init(HttpApplication context)
 2         {
 3             IntegratedPipelineBlueprint blueprint = LazyInitializer.EnsureInitialized(
 4                 ref _blueprint,
 5                 ref _blueprintInitialized,
 6                 ref _blueprintLock,
 7                 InitializeBlueprint);
 8     
 9             if (blueprint != null)
10             {
11                 var integratedPipelineContext = new IntegratedPipelineContext(blueprint);
12                 integratedPipelineContext.Initialize(context);
13             }
14         }

上面的代碼描述的是先確保_blueprint_blueprintInitialized_blueprintLock已被初始化,初始狀態很明顯_blueprint未被初始化(彷佛_blueprint一直未被初始化),因此會調用InitializeBlueprint進行初始化。

 1 private IntegratedPipelineBlueprint InitializeBlueprint()
 2         {
 3             IntegratedPipelineBlueprintStage firstStage = null;
 4 
 5             Action<IAppBuilder> startup = OwinBuilder.GetAppStartup();    //這就到了前面所講的流程了,尋找Startup,但在Invoke以前進行了EnableIntegratedPipeline調用
 6             OwinAppContext appContext = OwinBuilder.Build(builder =>
 7             {
 8                 EnableIntegratedPipeline(builder, stage => firstStage = stage);
 9                 startup.Invoke(builder);
10             });
11 
12             string basePath = Utils.NormalizePath(HttpRuntime.AppDomainAppVirtualPath);    //獲取虛擬路徑
13             return new IntegratedPipelineBlueprint(appContext, firstStage, basePath);    //pipeline已經構建完畢,返回第一個stage
14         }

上面作的代碼在AppBuilder進行pipeline中的middleware構建以前,啓用IntegratedPipelineBlueprint

 1 //EnableIntegratedPipeline的第二個參數是一個Action
 2 private static void EnableIntegratedPipeline(IAppBuilder app, Action<IntegratedPipelineBlueprintStage> onStageCreated)
 3         {
 4             var stage = new IntegratedPipelineBlueprintStage { Name = "PreHandlerExecute" };    //新建一個pipelineStage,只有 Name = "PreHandlerExecute",其餘屬性未初始化
 5             onStageCreated(stage);    //實際上就是使firstStage指向剛新建的pipelineStage,若是你足夠仔細的話
 6     //你會發現這其實是定義的pipelineStage的最後一個
 7             Action<IAppBuilder, string> stageMarker = (builder, name) =>
 8             {
 9                 Func<AppFunc, AppFunc> decoupler = next =>
10                 {    //next是一個AppFunc委託,若是當前stage.Name與傳入的name相同,則仍然返回next,next實際上就是下一個stage的入口
11                     if (string.Equals(name, stage.Name, StringComparison.OrdinalIgnoreCase))
12                     {
13                         // no decoupling needed when pipeline is already split at this name
14                         return next;
15                     }
16                     if (!IntegratedPipelineContext.VerifyStageOrder(name, stage.Name))    //若是當前的stage.Name的order小於傳入的name,仍然返回next
17                     {
18                         // Stage markers added out of order will be ignored.
19                         // Out of order stages/middleware may be run earlier than expected.
20                         // TODO: LOG
21                         return next;
22                     }    //若是當前的stage.Name的order大於傳入的name,將當前stage的入口點設置爲next,新建一個pipelineStage
23                     stage.EntryPoint = next;
24                     stage = new IntegratedPipelineBlueprintStage
25                     {
26                         Name = name,
27                         NextStage = stage,
28                     };
29                     onStageCreated(stage);    //更新firstStage
30                     return (AppFunc)IntegratedPipelineContext.ExitPointInvoked;    //當前stage已經構建完畢,再用一個AppFunc做爲當前middleware的返回值
31                 };
32                 app.Use(decoupler);    //decouper是一個Func<AppFunc,AppFunc>委託,因此會使用前一章所說的第一種app.Use方法,直接將其壓入List中
33             };
34             app.Properties[Constants.IntegratedPipelineStageMarker] = stageMarker;    //這裏將stageMarker綁定到app.Properties中,這將是本文的重點
35             app.Properties[Constants.BuilderDefaultApp] = (Func<IDictionary<string, object>, Task>)IntegratedPipelineContext.DefaultAppInvoked;
36         }

上文的源代碼與咱們一般的思惟有些出入,各個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將會被調用。

 1 public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options, PipelineStage stage)
 2         {
 3             if (app == null)
 4             {
 5                 throw new ArgumentNullException("app");
 6             }
 7 
 8             app.Use(typeof(CookieAuthenticationMiddleware), app, options);
 9             app.UseStageMarker(stage);
10             return app;
11         }

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

 1 public static IAppBuilder UseStageMarker(this IAppBuilder app, string stageName)
 2         {
 3             if (app == null)
 4             {
 5                 throw new ArgumentNullException("app");
 6             }
 7 
 8             object obj;
 9             if (app.Properties.TryGetValue(IntegratedPipelineStageMarker, out obj))    //尋找app.Propertise中的StageMarker方法,也就是上文OwinHttpModule存進去的方法
10             {
11                 var addMarker = (Action<IAppBuilder, string>)obj;
12                 addMarker(app, stageName);
13             }
14             return app;
15 
16         }

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的閱讀來講清楚這些流程。

相關文章
相關標籤/搜索