AppBuilder(二)【UseStageMarker】
原始碼參見
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
是本stage
的EntryPoint
,第二個AppFunc
是IntegratedPipelineContext.ExitPointInvoked
,第二個AppFunc
主要負責本stage
完成之後的收尾工作,之後將呼叫本stage
中的NextStage
找到下一個stage
,從下一個stage.EntryPoint
開始執行。
在前文的InitializeBlueprint
中OwinBuilder.Build
中完成了EnableIntegratedPipeline
操作,接下來就是Startup
的Configuration
方法被呼叫,以新建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
方法,標記了當前stage
為PipelineStage.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
中,而這個委託作的工作是當前的PipelineStage
從PreHandlerExcute
提前到了Authenticate
,並完成了AuthenticateStage
中的NextStage
指向PreHandlerExcuteStage
,PreHandlerExcuteStage
的EntryPoint
是最開始初始化的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);
}
可見定義了returnType
為Func<IDictionary<string, object>, Task>
的委託,而Build
是對BuildInternal
的封裝,下一章將閱讀BuilderInternal
方法。
總結middleware
注入與重建是兩個逆向的過程,按順序注入,反向遍歷重建,微軟工程師巧妙地保證了pipeline
注入順序,且保證了在兩個StageMarker
的middleware
被包裝在一個Func<AppFunc,AppFunc>
中,經過約定每個middleware
返回next
,在未遇到ExitPointInvoked
之前,都不會發生PipelineStage
的切換。這將需要PipelineStage
切換機制的支援和對middleware
輸入引數、輸出引數的約定。現在還有些模糊的地方,在下一章將通過對IntegratedPipeline
以及AppBuilder.Build
的閱讀來說清楚這些流程。
相關文章
- AppBuilder(三)【BuildInternal】APPUI
- AppBuilder(四)【SignatureConversions】APPUI
- AppBuilder(一)【Use彙總】APPUI
- 二
- Android 二維碼相關(二)Android
- 二維字首和 & 二維差分
- 滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹(二叉查詢樹)和最優二叉樹二叉樹
- 同步(二)
- Fiddler(二)
- Runtime(二)
- mipmap 二
- CSS二CSS
- 二、列表
- wqs二分(帶權二分)
- 二維碼管理平臺 生成二維碼
- 二進位制檔案視覺化(二)視覺化
- 巧用 TypeScript(二)TypeScript
- 泛型(二)泛型
- Spring Security(二)Spring
- ElasticSearch(二):MappingElasticsearchAPP
- this全面解析(二)
- 二維碼
- Hadoop HDFS(二)Hadoop
- webpack教程(二)Web
- 二級市盈率
- 逆向WeChat (二)
- 隨筆(二)
- 002、感遇二
- 重走springboot(二)Spring Boot
- MySQL 二 索引MySql索引
- 日記二
- 第二題
- 2024.10.1(週二)
- 二分
- 二轉十
- 2024.10.29(週二)
- 二值影像
- 作業二