AppBuilder(三)【BuildInternal】
原始碼參見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
實際上是對ConvertOneHop
和ConvertMultiHop
的封裝。
先看看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方法
};
上面的程式碼演示了PreHandlerExcute
和Authenticate
兩個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(一)
那一節所分析的結果,因為傳入的引數是一個Type
,args
長度為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
,這將是OwinMiddleware
與AppFunc
之間互相轉換的實現,需要用到AppBuilder
時候對_conversions
初始化的知識,留待下一章再說。
總結AppBuilder.BuildeInternal
對middleware
的List
遍歷是反向的,雖然現在還不明白為什麼如此設計,而且如何在一個PipelineStage
中執行多個middleware
也還不明朗,曾經以為是使用類似Invoke += Middleware.Invoke
實現的,但既然是反向的,這不就順序反了嗎?
目前能確定下來的時候每個PipelineStage
的EntryPoint
已經顯式指定了,剛剛大概又想了一下,為了保證PipelineStage
的規範性,那麼每個PipelineStage
應該都是一個Func<AppFunc, AppFunc>
形式的才對,而Middleware
應該是被封裝在這兩個AppFunc
之間的,這麼說,應該是_conversions
來完成了同一個PipelineStage
中的Middleware
的串聯工作了,理應如此。下一節再驗證這個問題。
相關文章
- AppBuilder(二)【UseStageMarker】APPUI
- AppBuilder(一)【Use彙總】APPUI
- AppBuilder(四)【SignatureConversions】APPUI
- “川大軟體”開發的MFC視覺化開發工具AppBuilder for mfc 3.07簡體中文版註冊碼 (2千字)視覺化APPUI
- 第三週週三
- Redis叢集搭建 三主三從Redis
- Redis叢集搭建(三主三從)Redis
- 第三週作業(三):wc程式C程式
- MySQL(三)MySql
- JVM(三)JVM
- canvas(三)Canvas
- redis(三)Redis
- jQuery(三)jQuery
- Oracle 系統監控建置三(共三)Oracle
- 三種方法實現CSS三欄佈局CSS
- 京東小程式的三生三世
- HP-UX老三篇 新三篇UX
- 資訊系統三怕三不怕
- 三菱 PLC 星-三角啟動
- 面試篇三面試
- 泛型(三)泛型
- Flutter 三探Flutter
- 巧用 TypeScript(三)TypeScript
- 三、函式函式
- (三)陣列陣列
- (三)需求分析
- Seastar 教程(三)AST
- 《敏捷革命》(三)敏捷
- OkHttp - Interceptors(三)HTTP
- PHP面試(三)PHP面試
- Nim教程【三】
- 網頁遊戲(三)網頁遊戲
- 三個ERROR!Error
- 三、SpaceVim配置
- 三中
- 三個元素
- 逆向WeChat(三)
- 第三週