AppBuilder(四)【SignatureConversions】
原始碼參見
Microsoft.Owin.Builder.AppBuilder
Microsoft.Owin.Infrastructure.SignatureConversions
在AppBuilder
中遇到了_middleware
三元組的Item1
,微軟工程師稱之為signature
不一致的問題,一種是AppFunc
,一種是OwinMiddleware
,因此需要用到SignatureConversions
,這是在AppBuilder
例項化的時候完成的工作,先看看AppBuilder
的建構函式。
public AppBuilder()
{
_properties = new Dictionary<string, object>(); //初始化環境字典
_conversions = new Dictionary<Tuple<Type, Type>, Delegate>(); //初始化_conversion字典
_middleware = new List<Tuple<Type, Delegate, object[]>>(); //初始化_middleware連結串列
_properties[Constants.BuilderAddConversion] = new Action<Delegate>(AddSignatureConversion); //繫結AddSignatureConversion方法
_properties[Constants.BuilderDefaultApp] = NotFound; //繫結預設最後一步處理流程
SignatureConversions.AddConversions(this); //開始往_conversions中新增具體的處理方法
}
實際的_conversions
完成初始化由SignatureConversions.AddConversions
完成。
public static class SignatureConversions
{
/// <summary>
/// Adds adapters between <typeref name="Func<IDictionary<string,object>, Task>"/> and OwinMiddleware.
/// </summary>
/// <param name="app"></param>
public static void AddConversions(IAppBuilder app) //實際上是對Conversion1和Conversion2的包裝,呼叫的是AppBuilderExtension中的方法
{
app.AddSignatureConversion<AppFunc, OwinMiddleware>(Conversion1); //完成從AppFunc到OwinMiddleware的轉換
app.AddSignatureConversion<OwinMiddleware, AppFunc>(Conversion2); //完成從OwinMiddleware到AppFunc的轉換
}
private static OwinMiddleware Conversion1(AppFunc next)
{
return new AppFuncTransition(next);
}
private static AppFunc Conversion2(OwinMiddleware next)
{
return new OwinMiddlewareTransition(next).Invoke;
}
}
來看看AddSignatureConversion
,還是一層封裝
public static void AddSignatureConversion<T1, T2>(this IAppBuilder builder, Func<T1, T2> conversion)
{
AddSignatureConversion(builder, (Delegate)conversion); //實際會呼叫下面的方法
}
public static void AddSignatureConversion(this IAppBuilder builder, Delegate conversion)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
object obj;
if (builder.Properties.TryGetValue("builder.AddSignatureConversion", out obj)) //尋找AppBuilder建構函式中繫結的AddSignatureConversion,是Action<Delegate>
{
var action = obj as Action<Delegate>; //還原為Action<Delegate>
if (action != null)
{
action(conversion); //將conversion存入_conversion字典
return;
}
}
throw new MissingMethodException(builder.GetType().FullName, "AddSignatureConversion");
}
來看看這個Action<Delegate>
在拿到private static OwinMiddleware Conversion1(AppFunc next)
這個方法之後做了什麼。
private void AddSignatureConversion(Delegate conversion)
{
if (conversion == null)
{
throw new ArgumentNullException("conversion");
}
Type parameterType = GetParameterType(conversion); //以Conversion1為例,這裡的parameterType為AppFunc,ReturnType為OwinMiddleware
if (parameterType == null)
{
throw new ArgumentException(Resources.Exception_ConversionTakesOneParameter, "conversion");
}
Tuple<Type, Type> key = Tuple.Create(conversion.Method.ReturnType, parameterType); //使用conversion的ReturnType和parameterType作為key,相當於簽名
_conversions[key] = conversion; //記錄conversion
}
同理Conversion2
也是這樣的操作,不過parameterType
為OwinMiddleware
,而ReturnType
為AppFunc
。
解釋一下轉換原理,Conversion1
這個方法return
了一個AppFuncTransition
例項,而AppFuncTransition
繼承自OwinMiddleware
,自然就完成了轉換。
而Conversion2
這個方法返回的是OwinMiddlewareTransition
例項的Invoke
方法,自然就是一個AppFunc
了
可見兩種簽名對應的是OwinMiddleware
例項和AppFunc
委託的相互轉換。
回顧AppBuilder(三)中的_middleware.Reverse
遍歷操作:
第一次取到的是app.Use(decoupler)
對應的middleware
,第一次Convert
操作完成了PipelineStage
的切換,而且使得PreHandlerExcute
這一Stage
的EntryPoint
為NotFound
,新建的Authenticate
這一Stage
的NextStage
指向PreHandlerExcute
這一Stage
,第二次Convert
操作很快返回,現在app
指向(AppFunc)IntegratedPipelineContext.ExitPointInvoked
。
第二次取到的是app.Use(typeof(CookieAuthenticationMiddleware), app, options)
對應的CookieAuthenticationMiddleware
,三元組解耦之後
在Convert(neededSignature, app)
的時候等同於Convert(typeof(OwinMiddleware), AppFunc)
signature.IsInstanceOfType(app)
和typeof(Delegate).IsAssignableFrom(signature)
均會返回false
,所以會進入本文的重點
foreach (var conversion in _conversions) //經過推斷會呼叫Conversion1
{
Type returnType = conversion.Key.Item1; //returnType為OwinMiddleware
Type parameterType = conversion.Key.Item2; //parameterType為AppFunc
if (parameterType.IsInstanceOfType(app) &&
signature.IsAssignableFrom(returnType))
{
return conversion.Value.DynamicInvoke(app); //等同於呼叫new AppFuncTransition(app)
}
}
_conversions
字典中有兩個conversion
,分別為Conversion1
和Conversion2
,由於我們需要從AppFunc
到OwinMiddleware
的轉換,經過引數和返回值的檢查,會呼叫Conversion1
進行轉換例項化了一個AppFuncTransition
,引數為app
來看看AppFuncTransition
internal sealed class AppFuncTransition : OwinMiddleware
{
private readonly AppFunc _next;
/// <summary>
///
/// </summary>
/// <param name="next"></param>
public AppFuncTransition(AppFunc next) : base(null) //呼叫的是這個建構函式,base(null)父物件例項化一個空的middleware
{
_next = next; //使_next指向app
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task Invoke(IOwinContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
return _next(context.Environment);
}
}
可見上面的程式碼巧妙的返回一個Next=null的OwinMiddleware,而利用了(AppFunc)_next來記錄連結關係,當呼叫這個OwinMiddleware的Invoke方法的時候,實際執行的還是_next(context.Environment),等同於還是執行的AppFunc(context.Envrionment),與原來並沒有什麼區別。
再看看OwinMiddlewareTransition
internal sealed class OwinMiddlewareTransition
{
private readonly OwinMiddleware _next;
/// <summary>
///
/// </summary>
/// <param name="next"></param>
public OwinMiddlewareTransition(OwinMiddleware next)
{
_next = next;
}
/// <summary>
///
/// </summary>
/// <param name="environment">OWIN environment dictionary which stores state information about the request, response and relevant server state.</param>
/// <returns></returns>
public Task Invoke(IDictionary<string, object> environment)
{
return _next.Invoke(new OwinContext(environment));
}
}
我們需要的是OwinMiddlewareTransition.Invoke
方法,這是一個AppFunc
,也是Conversion2
返回的,當呼叫這個Invoke
方法的時候實際執行的是_next.Invoke(new OwinContext(environment))
,等同於執行OwinMiddleware.Invoke(new OwinContext(envrionment))
,與原來也並沒有什麼區別。
這裡可以看出雖然例項和方法之間實現了轉換,但因為都會呼叫Invoke
方法,與不轉換之前並沒有什麼區別,不改變執行的邏輯,只是改變了承載這個Invoke
方法的載體而已,這也是pipeline
中middleware
和stage
更換能夠無縫銜接的原因。
現在我們知道經過Conversion1
轉換之後,app
更新為Convert
的返回值,由一個AppFunc
變成了一個OwinMiddleware
。
app = middlewareDelegate.DynamicInvoke(invokeParameters)
執行的時候等同於執行OwinMiddleware.Invoke(OwinMiddleware, IAppBuilder app, CookieAuthenticationOptions options)
,返回一個CookieAuthenticationMiddleware
。
對應的建構函式為
public CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options) : base(next, options)
最終達到的效果是CookieAuthenticationMiddleware
執行Next.Invoke(conetxt)
方法,實際上是執行的IntegratedPipelineContext.ExitPointInvoked(context.Environment)
,執行PipelineStage
的切換工作。
此時app
為CookieAuthenticationMiddleware
的例項,同理這次的app = Convert(neededSignature, app)
會很快返回,app
不變。
至此已經可以解釋很多東西了。
1 為什麼要反向遍歷?
因為每個OwinMiddleware
的建構函式的第一個引數或者Func<AppFunc,AppFunc>
的引數都是一個next
,指向下一個要執行的元件,那麼這個next
不應該為空,而且要真實有效,反向遍歷會先生成後面OwinMiddleware
或者Func
,然後用其作為前一個的引數,這能保證構造的pipeline
的有效性。
2 OwinMiddleware
或者Func
是如何串起來的?
如上所述,每個OwinMiddleware
或者Func
的第一個引數都是一個next
,OwinMiddleware
或Func
的方法都會呼叫其Invoke
方法,不同的是OwinMiddleware
的Invoke
是一個可以重寫的方法,引數為OwinContext
,而Func
是Delegate
,其Invoke
方法等同執行這個Func
,引數為Envrionment
。在Invoke
中做了自己的工作之後,執行next.Invoke
方法,並返回其結果,這樣就串起來了。
3 PipelineStage
是如何切換的?
這將是下一節所要涉及的內容,每個PipelineStage
都記錄了NextStage
,Pipeline
排程部分可以在所有非同步處理完成之後啟用NextStage
,這主要是未開源的System.Web.Application
來完成排程的,採用了事件的機制。
總結,每個PipelineStage
有個EntryPoint
和ExitPoint
,他們以及他們之前的其他OwinMiddleware
或者Func
通過next
串聯起來,執行的時候,由HttpApplication
觸發相應的事件。pipeline
能流動的關鍵因素是每個元件對於下一元件都有合法有效引用,所以採用反向遍歷的方法來重建,Func
呼叫下一Func
為next.Invoke(environment)
,OwinMiddleware
呼叫下一OwinMiddleware
為Next.Invoke(context)
,所以conversion
主要是OwinMiddleware
或者Func
看到的next
都是跟自己一個型別的。OwinMiddleware
為了與Func
一致,都採用了Invoke
作為入口。
相關文章
- AppBuilder(二)【UseStageMarker】APPUI
- AppBuilder(三)【BuildInternal】APPUI
- AppBuilder(一)【Use彙總】APPUI
- 四劍客第四關
- 四
- redis(四)Redis
- CSS(四)CSS
- 科目四
- pandas(四)
- 四、字典
- 泛型(四)泛型
- 測試四
- Scala(四):物件物件
- 巧用 TypeScript(四)TypeScript
- scala(四)集合
- ZooKeeper系列(四)
- OkHttp Interceptors(四)HTTP
- 四、陣列陣列
- SpringCloud(四)DockerSpringGCCloudDocker
- MySQL 四 鎖MySql
- 作業四
- 2024.10.3(週四)
- 實驗四
- 2024.10.31(週四)
- 2024.9.19(週四)
- 四元數
- 逆向WeChat(四)
- SpringCloud(四) configSpringGCCloud
- javaWeb(四)----- DOMJavaWeb
- 四則運算+-×÷同時成立的四連環(2)
- 四則運算+-×÷同時成立的四連環(4)
- 四則運算+-×÷同時成立的四連環(3)
- 四則運算+-×÷同時成立的四連環(1)
- 四則運算+-×÷同時成立的四連環(6)
- C++ 四捨五入與不四捨五入C++
- gRPC學習之四:實戰四類服務方法RPC
- TypeScript 學習(四)TypeScript
- RUST 筆記(四)Rust筆記