AppBuilder(四)SignatureConversions

源碼參見Microsoft.Owin.Builder.AppBuilderapp

                 Microsoft.Owin.Infrastructure.SignatureConversions異步

AppBuilder中遇到了_middleware三元組Item1,微軟工程師稱之爲signature不一致的問題,一種是AppFunc,一種是OwinMiddleware,所以須要用到SignatureConversions,這是在AppBuilder實例化的時候完成的工做,先看看AppBuilder的構造函數。ide

 1 public AppBuilder()
 2         {
 3             _properties = new Dictionary<string, object>();    //初始化環境字典
 4             _conversions = new Dictionary<Tuple<Type, Type>, Delegate>();    //初始化_conversion字典
 5             _middleware = new List<Tuple<Type, Delegate, object[]>>();    //初始化_middleware鏈表
 6 
 7             _properties[Constants.BuilderAddConversion] = new Action<Delegate>(AddSignatureConversion);    //綁定AddSignatureConversion方法
 8             _properties[Constants.BuilderDefaultApp] = NotFound;    //綁定默認最後一步處理流程
 9 
10             SignatureConversions.AddConversions(this);    //開始往_conversions中添加具體的處理方法
11         }

實際的_conversions完成初始化由SignatureConversions.AddConversions完成。函數

 1 public static class SignatureConversions
 2     {
 3         /// <summary>
 4         /// Adds adapters between <typeref name="Func&lt;IDictionary&lt;string,object&gt;, Task&gt;"/> and OwinMiddleware.
 5         /// </summary>
 6         /// <param name="app"></param>
 7         public static void AddConversions(IAppBuilder app)    //其實是對Conversion1和Conversion2的包裝,調用的是AppBuilderExtension中的方法
 8         {
 9             app.AddSignatureConversion<AppFunc, OwinMiddleware>(Conversion1);    //完成從AppFunc到OwinMiddleware的轉換
10             app.AddSignatureConversion<OwinMiddleware, AppFunc>(Conversion2);    //完成從OwinMiddleware到AppFunc的轉換
11         }
12 
13         private static OwinMiddleware Conversion1(AppFunc next)
14         {
15             return new AppFuncTransition(next);
16         }
17 
18         private static AppFunc Conversion2(OwinMiddleware next)
19         {
20             return new OwinMiddlewareTransition(next).Invoke;
21         }
22     }

來看看AddSignatureConversion,仍是一層封裝ui

 1 public static void AddSignatureConversion<T1, T2>(this IAppBuilder builder, Func<T1, T2> conversion)
 2         {
 3             AddSignatureConversion(builder, (Delegate)conversion);    //實際會調用下面的方法
 4         }
 5 public static void AddSignatureConversion(this IAppBuilder builder, Delegate conversion)
 6         {
 7             if (builder == null)
 8             {
 9                 throw new ArgumentNullException("builder");
10             }
11             object obj;
12             if (builder.Properties.TryGetValue("builder.AddSignatureConversion", out obj))    //尋找AppBuilder構造函數中綁定的AddSignatureConversion,是Action<Delegate>
13             {
14                 var action = obj as Action<Delegate>;    //還原爲Action<Delegate>
15                 if (action != null)
16                 {
17                     action(conversion);    //conversion存入_conversion字典
18                     return;
19                 }
20             }
21             throw new MissingMethodException(builder.GetType().FullName, "AddSignatureConversion");
22         }

來看看這個Action<Delegate>在拿到private static OwinMiddleware Conversion1(AppFunc next)這個方法以後作了什麼。this

 1 private void AddSignatureConversion(Delegate conversion)
 2         {
 3             if (conversion == null)
 4             {
 5                 throw new ArgumentNullException("conversion");
 6             }
 7 
 8             Type parameterType = GetParameterType(conversion);    //以Conversion1爲例,這裏的parameterType爲AppFunc,ReturnType爲OwinMiddleware
 9             if (parameterType == null)
10             {
11                 throw new ArgumentException(Resources.Exception_ConversionTakesOneParameter, "conversion");
12             }
13             Tuple<Type, Type> key = Tuple.Create(conversion.Method.ReturnType, parameterType);    //使用conversion的ReturnType和parameterType做爲key,至關於簽名
14             _conversions[key] = conversion;    //記錄conversion
15         }

同理Conversion2也是這樣的操做,不過parameterTypeOwinMiddleware,而ReturnTypeAppFuncspa

解釋一下轉換原理,Conversion1這個方法return了一個AppFuncTransition實例,而AppFuncTransition繼承自OwinMiddleware,天然就完成了轉換code

Conversion2這個方法返回的是OwinMiddlewareTransition實例的Invoke方法,天然就是一個AppFuncorm

可見兩種簽名對應的是OwinMiddleware實例和AppFunc委託的相互轉換。server

回顧AppBuilder(三)中的_middleware.Reverse遍歷操做:

第一次取到的是app.Use(decoupler)對應的middleware第一次Convert操做完成了PipelineStage的切換,並且使得PreHandlerExcute這一StageEntryPointNotFound,新建的Authenticate這一StageNextStage指向PreHandlerExcute這一Stage第二次Convert操做很快返回,如今app指向(AppFunc)IntegratedPipelineContext.ExitPointInvoked

第二次取到的是app.Use(typeof(CookieAuthenticationMiddleware), app, options)對應的CookieAuthenticationMiddleware,三元組解耦以後

Item1

OwinMiddlewareType

Item2

CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options)  構造函數

Item3

[IAppBuilder app,  CookieAuthenticationOptions options] 長度爲2object[]

Convert(neededSignature, app)的時候等同於Convert(typeof(OwinMiddleware), AppFunc)

signature.IsInstanceOfType(app)typeof(Delegate).IsAssignableFrom(signature)均會返回false,因此會進入本文的重點

 1 foreach (var conversion in _conversions)    //通過推斷會調用Conversion1
 2             {
 3                 Type returnType = conversion.Key.Item1;    //returnType爲OwinMiddleware
 4                 Type parameterType = conversion.Key.Item2;    //parameterType爲AppFunc
 5                 if (parameterType.IsInstanceOfType(app) &&
 6                     signature.IsAssignableFrom(returnType))
 7                 {
 8                     return conversion.Value.DynamicInvoke(app);    //等同於調用new AppFuncTransition(app)
 9                 }
10             }

_conversions字典中有兩個conversion,分別爲Conversion1Conversion2,因爲咱們須要從AppFuncOwinMiddleware的轉換,通過參數和返回值的檢查,會調用Conversion1進行轉換實例化了一個AppFuncTransition參數爲app

來看看AppFuncTransition

 1 internal sealed class AppFuncTransition : OwinMiddleware
 2     {
 3         private readonly AppFunc _next;
 4 
 5         /// <summary>
 6         /// 
 7         /// </summary>
 8         /// <param name="next"></param>
 9         public AppFuncTransition(AppFunc next) : base(null)    //調用的是這個構造函數,base(null)父對象實例化一個空的middleware
10         {
11             _next = next;    //使_next指向app
12         }
13 
14         /// <summary>
15         /// 
16         /// </summary>
17         /// <param name="context"></param>
18         /// <returns></returns>
19         public override Task Invoke(IOwinContext context)
20         {
21             if (context == null)
22             {
23                 throw new ArgumentNullException("context");
24             }
25 
26             return _next(context.Environment);
27         }
28     }

可見上面的代碼巧妙的返回一個Next=nullOwinMiddleware,而利用了(AppFunc)_next來記錄連接關係,當調用這個OwinMiddlewareInvoke方法的時候,實際執行的仍是_next(context.Environment)等同於仍是執行的AppFunc(context.Envrionment),與原來並無什麼區別。

再看看OwinMiddlewareTransition

 1 internal sealed class OwinMiddlewareTransition
 2     {
 3         private readonly OwinMiddleware _next;
 4 
 5         /// <summary>
 6         /// 
 7         /// </summary>
 8         /// <param name="next"></param>
 9         public OwinMiddlewareTransition(OwinMiddleware next)
10         {
11             _next = next;
12         }
13 
14         /// <summary>
15         /// 
16         /// </summary>
17         /// <param name="environment">OWIN environment dictionary which stores state information about the request, response and relevant server state.</param>
18         /// <returns></returns>
19         public Task Invoke(IDictionary<string, object> environment)
20         {
21             return _next.Invoke(new OwinContext(environment));
22         }
23     }

咱們須要的是OwinMiddlewareTransition.Invoke方法,這是一個AppFunc,也是Conversion2返回的,當調用這個Invoke方法的時候實際執行的是_next.Invoke(new OwinContext(environment))等同於執行OwinMiddleware.Invoke(new OwinContext(envrionment)),與原來也並無什麼區別。

這裏能夠看出雖然實例和方法之間實現了轉換,但由於都會調用Invoke方法,與不轉換以前並無什麼區別,不改變執行的邏輯,只是改變了承載這個Invoke方法的載體而已,這也是pipelinemiddlewarestage更換可以無縫銜接的緣由。

如今咱們知道通過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的切換工做。

此時appCookieAuthenticationMiddleware的實例,同理此次的app = Convert(neededSignature, app)會很快返回app不變

至此已經能夠解釋不少東西了。

1 爲何要反向遍歷?

由於每一個OwinMiddleware的構造函數的第一個參數或者Func<AppFunc,AppFunc>的參數都是一個next,指向下一個要運行的組件,那麼這個next不該該爲空,並且要真實有效,反向遍歷會先生成後面OwinMiddleware或者Func,而後用其做爲前一個的參數,這能保證構造的pipeline的有效性。

2 OwinMiddleware或者Func是如何串起來的?

如上所述,每一個OwinMiddleware或者Func的第一個參數都是一個nextOwinMiddlewareFunc的方法都會調用其Invoke方法,不一樣的是OwinMiddlewareInvoke是一個能夠重寫的方法,參數爲OwinContext,而FuncDelegate,其Invoke方法等同執行這個Func,參數爲Envrionment。在Invoke中作了本身的工做以後,執行next.Invoke方法,並返回其結果,這樣就串起來了。

3 PipelineStage是如何切換的?

這將是下一節所要涉及的內容,每一個PipelineStage都記錄了NextStagePipeline調度部分能夠在全部異步處理完成以後啓用NextStage,這主要是未開源的System.Web.Application來完成調度的,採用了事件的機制。

總結,每一個PipelineStage有個EntryPointExitPoint,他們以及他們以前的其餘OwinMiddleware或者Func經過next串聯起來,執行的時候,由HttpApplication觸發相應的事件。pipeline能流動的關鍵因素是每一個組件對於下一組件都有合法有效引用,因此採用反向遍歷的方法來重建,Func調用下一Funcnext.Invoke(environment)OwinMiddleware調用下一OwinMiddlewareNext.Invoke(context),因此conversion主要是OwinMiddleware或者Func看到的next都是跟本身一個類型的。OwinMiddleware爲了與Func一致,都採用了Invoke做爲入口。

相關文章
相關標籤/搜索