Owin Katana 的底层源码分析
最近看了一下開源項(xiàng)目asp.net katana,感覺公開的接口非常的簡(jiǎn)潔優(yōu)雅,channel 9 說是受到node.js的啟示設(shè)計(jì)的,Katana是一個(gè)比較老的項(xiàng)目,現(xiàn)在已經(jīng)整合到asp.net core中。 從github克隆下來的項(xiàng)目,這個(gè)博客專門是從代碼視點(diǎn)去了解katana項(xiàng)目,所以本篇隨筆針對(duì)已經(jīng)對(duì)OWIN有所了解的人,假如僅僅入門的話能夠跑一下MSDN的源碼再來閱覽本篇文章。 代碼結(jié)構(gòu)如上,簡(jiǎn)略剖析一下各個(gè)文件夾的含義,這對(duì)于了解katana項(xiàng)目的全體結(jié)構(gòu)有一個(gè)大的輪廓。 .build文件夾顧名思義便是編譯的文件夾,在沒使用vs的時(shí)分你能夠單擊build.cmd 去編譯這個(gè)項(xiàng)目,非常的便利。 .Nuget便是包辦理東西的配置文件,這個(gè)咱們能夠忽略。同理.Prerelease。 Development是本次的研究要點(diǎn),當(dāng)你打開這個(gè)文件夾的時(shí)分你會(huì)發(fā)現(xiàn)一個(gè)類庫Microsoft.Owin的類庫,這個(gè)是OWIN組件的經(jīng)典完成。 FunctionTests是單元測(cè)試的類庫 Hosting 是server的抽象層,OWIN 將服務(wù)器進(jìn)行抽象化,Hosting 便是能夠辦理Server的一層,像WebApp就能敞開一個(gè)httplister服務(wù),詳細(xì)稍后再講。 Middleware是一些中間件的完成,在katana已經(jīng)將管道模型虛擬化成中間件 Performance 和Sandbox 是微軟的一些測(cè)試東西 Security 是微軟已經(jīng)寫好的驗(yàn)證中間件,其中包括JWT和Oauth的驗(yàn)證方式 Server 便是服務(wù)器的完成 Owin.Analysis是我自己建的web程序用來debug 上面已經(jīng)介紹了各個(gè)文件夾所對(duì)應(yīng)的功用,相必大部分人都是一臉遮蓋,但是不必?fù)?dān)心,下面就來看看詳細(xì)的代碼,當(dāng)然是從最小的比如動(dòng)身。點(diǎn)擊 getting started with owin and katan 你就能 跳到MSDN得到最小的比如。里邊的一系列操作就為了增加下面的一個(gè)類和幾個(gè)reference.現(xiàn)在咱們看一下這個(gè)類。 仿制代碼 1 using Microsoft.Owin; 2 3 [assembly: OwinStartup(typeof(Owin.Analysis.Startup))] 4 namespace Owin.Analysis 5 { 6 public class Startup 7 { 8 public void Configuration(IAppBuilder app) 9 { 10 app.Run(context => 11 { 12 context.Response.ContentType = "text/plain"; 13 return context.Response.WriteAsync("Hello World"); 14 }); 15 } 16 } 17 } 仿制代碼 看起來這個(gè)代碼非常的優(yōu)雅,增加幾個(gè)reference和一個(gè)類就讓懇求抵達(dá)Hello World。咱們先剖析這個(gè)類,首先程序集特性O(shè)winStartupAtribute將當(dāng)前類保存在元數(shù)據(jù)中。然后寫了一個(gè) Configuration辦法,獲取一個(gè)IAppBuilder 參數(shù)調(diào)用Run辦法,Run辦法傳遞一個(gè)托付進(jìn)去,咱們的處理邏輯就在這一個(gè)托付里。 這兒邊咱們剖析一下核心接口IAppBuilder的經(jīng)典完成者AppBuilder,IAppBuilder的接口如下, 仿制代碼 using System; using System.Collections.Generic; namespace Owin { public interface IAppBuilder { IDictionary?Properties { get; }//懇求的參數(shù) object Build(Type returnType);//中間件鏈接 IAppBuilder New();//創(chuàng)立一個(gè)新的目標(biāo) IAppBuilder Use(object middleware, params object[] args);//注冊(cè)中間件 } } 仿制代碼 好的咱們來剖析一下AppBuilder中間件的注冊(cè)完成。在app.Run 打完break point你就能夠進(jìn)入app.use辦法,首先在AppBuilderUseExtensions這個(gè)類里對(duì)use的入口寫了一大堆擴(kuò)展辦法。app.Run就 是其中的一個(gè),當(dāng)你用app.Run注冊(cè)中間件的時(shí)分是沒有下一個(gè)中間件的引用的。 仿制代碼 public static void Run(this IAppBuilder app, Func?handler) { if (app == null) { throw new ArgumentNullException("app"); } if (handler == null) { throw new ArgumentNullException("handler"); } app.Use(handler); } 仿制代碼 在經(jīng)典的完成中,參數(shù)middleware會(huì)有兩種狀況,一種是delegate,一種是type,假如是type類型,則他的結(jié)構(gòu)辦法接受next為參數(shù),并且里邊有一個(gè)公開的Invoke辦法。假如是托付,當(dāng)前托付作為 參數(shù)傳遞到next中。 public IAppBuilder Use(object middleware, params object[] args) { _middleware.Add(ToMiddlewareFactory(middleware, args)); return this; } 下面的代碼是ToMiddlewareFactory的完成,在第9行和第27行別離判別了中間件目標(biāo)是托付類型還是type類型,因?yàn)楸绢}比如是type目標(biāo),咱們剖析一下ToConstructorMiddlewareFactory辦法。 仿制代碼 1 private static Tuple?ToMiddlewareFactory(object middlewareObject, object[] args) 2 { 3 if (middlewareObject == null) 4 { 5 throw new ArgumentNullException("middlewareObject"); 6 } 7 8 var middlewareDelegate = middlewareObject as Delegate; 9 if (middlewareDelegate != null) 10 { 11 return Tuple.Create(GetParameterType(middlewareDelegate), middlewareDelegate, args); 12 } 13 14 Tuple?factory = ToInstanceMiddlewareFactory(middlewareObject, args); 15 if (factory != null) 16 { 17 return factory; 18 } 19 20 factory = ToGeneratorMiddlewareFactory(middlewareObject, args); 21 if (factory != null) 22 { 23 return factory; 24 } 25 26 if (middlewareObject is Type) 27 { 28 return ToConstructorMiddlewareFactory(middlewareObject, args, ref middlewareDelegate); 29 } 30 31 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, 32 Resources.Exception_MiddlewareNotSupported, middlewareObject.GetType().FullName)); 33 } 仿制代碼 在第5行獲取type類型的一切結(jié)構(gòu)辦法,第8行獲取結(jié)構(gòu)辦法的一切參數(shù),在第14行有個(gè)trick,用zip辦法判別參數(shù)類型是否和結(jié)構(gòu)辦法類型是共同的。假如是共同的則持續(xù)往下走,在第22行和第23行 使用托付將結(jié)構(gòu)辦法創(chuàng)立成lambda表達(dá)式,然后生成元祖,第一個(gè)是next的類型,第二個(gè)是type的結(jié)構(gòu)辦法,第三個(gè)是type結(jié)構(gòu)辦法所需的參數(shù)。然后將元祖加入到AppBuild所保護(hù)的中間件目標(biāo)。 仿制代碼 1 2 private static Tuple?ToConstructorMiddlewareFactory(object middlewareObject, object[] args, ref Delegate middlewareDelegate) 3 { 4 var middlewareType = middlewareObject as Type; 5 ConstructorInfo[] constructors = middlewareType.GetConstructors(); 6 foreach (var constructor in constructors) 7 { 8 ParameterInfo[] parameters = constructor.GetParameters(); 9 Type[] parameterTypes = parameters.Select(p => p.ParameterType).ToArray(); 10 if (parameterTypes.Length != args.Length + 1) 11 { 12 continue; 13 } 14 if (!parameterTypes 15 .Skip(1) 16 .Zip(args, TestArgForParameter) 17 .All(x => x)) 18 { 19 continue; 20 } 21 22 ParameterExpression[] parameterExpressions = parameters.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray(); 23 NewExpression callConstructor = Expression.New(constructor, parameterExpressions); 24 middlewareDelegate = Expression.Lambda(callConstructor, parameterExpressions).Compile(); 25 return Tuple.Create(parameters[0].ParameterType, middlewareDelegate, args); 26 } 27 28 throw new MissingMethodException(string.Format(CultureInfo.CurrentCulture, 29 Resources.Exception_NoConstructorFound, middlewareType.FullName, args.Length + 1)); 30 } 仿制代碼 這個(gè)時(shí)分咱們已經(jīng)將中間件注冊(cè)到AppBuilder目標(biāo)了。注冊(cè)完中間件的目標(biāo)咱們還需求做一件事便是將這些中間件chained together,這些完成便是Build 辦法中,而Build辦法BuildInternal辦法, 這個(gè)時(shí)分會(huì)產(chǎn)生一個(gè)entry point供調(diào)用。 現(xiàn)在咱們要點(diǎn)看一下這個(gè)build辦法。 public object Build(Type returnType) { return BuildInternal(returnType); } Build辦法調(diào)用私有的BuildInternal的辦法。 仿制代碼 private object BuildInternal(Type signature) { object app; if (!_properties.TryGetValue(Constants.BuilderDefaultApp, out app)) { app = NotFound; } foreach (var middleware in _middleware.Reverse()) { Type neededSignature = middleware.Item1; Delegate middlewareDelegate = middleware.Item2; object[] middlewareArgs = middleware.Item3; app = Convert(neededSignature, app); object[] invokeParameters = new[] { app }.Concat(middlewareArgs).ToArray(); app = middlewareDelegate.DynamicInvoke(invokeParameters); app = Convert(neededSignature, app); } return Convert(signature, app); } 仿制代碼 咱們能夠看到它是怎樣將中間件chained together的,在咱們之前注冊(cè)的時(shí)分實(shí)際上middleware元祖會(huì)保存三個(gè)信息,第一個(gè)type便是結(jié)構(gòu)函數(shù)的第一個(gè)類型,第二個(gè)托付是useHandlerMiddleware的 結(jié)構(gòu)辦法,第三個(gè)是結(jié)構(gòu)辦法的參數(shù)(除了第一個(gè)),Reverse的辦法會(huì)將中間件逆序,這樣保證調(diào)用的順序便是你注冊(cè)的順序,后面的是chain的邏輯,app的變量實(shí)際上便是下一個(gè)中間件結(jié)構(gòu)函數(shù) 的next,當(dāng)?shù)玫降谝粋€(gè)中間件的時(shí)分,里邊的next會(huì)保存第二個(gè)中間件的處理邏輯,同樣第二個(gè)next便是第三個(gè)...,這樣chained together得到的便是第一個(gè)中間件的邏輯,所以你們?cè)谟胊pp.Use的 辦法就會(huì)有一個(gè)參數(shù)next,并且需求手動(dòng)調(diào)用一下。 得到這些之后需求的便是要將中間件注冊(cè)到application管道事情呢。因?yàn)閍sp.net的是一個(gè)大的切面結(jié)構(gòu)。 仿制代碼 public void Initialize(HttpApplication application) { for (IntegratedPipelineBlueprintStage stage = _blueprint.FirstStage; stage != null; stage = stage.NextStage) { var segment = new IntegratedPipelineContextStage(this, stage); switch (stage.Name) { case Constants.StageAuthenticate: application.AddOnAuthenticateRequestAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StagePostAuthenticate: application.AddOnPostAuthenticateRequestAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StageAuthorize: application.AddOnAuthorizeRequestAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StagePostAuthorize: application.AddOnPostAuthorizeRequestAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StageResolveCache: application.AddOnResolveRequestCacheAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StagePostResolveCache: application.AddOnPostResolveRequestCacheAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StageMapHandler: application.AddOnMapRequestHandlerAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StagePostMapHandler: application.AddOnPostMapRequestHandlerAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StageAcquireState: application.AddOnAcquireRequestStateAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StagePostAcquireState: application.AddOnPostAcquireRequestStateAsync(segment.BeginEvent, segment.EndEvent); break; case Constants.StagePreHandlerExecute: application.AddOnPreRequestHandlerExecuteAsync(segment.BeginEvent, segment.EndEvent); break; default: throw new NotSupportedException( string.Format(CultureInfo.InvariantCulture, Resources.Exception_UnsupportedPipelineStage, stage.Name)); } } // application.PreSendRequestHeaders += PreSendRequestHeaders; // Null refs for async un-buffered requests with bodies. application.AddOnEndRequestAsync(BeginFinalWork, EndFinalWork); } 仿制代碼 這兒邊有一個(gè)概念便是IntegratedPipelineBlueprintStage,這個(gè)是一個(gè)鏈表結(jié)構(gòu),每個(gè)對(duì)應(yīng)的便是管道事情,每個(gè)stage都有entry point,這樣便利咱們?cè)诓煌墓艿朗虑橹羞\(yùn)行中間件,在 BeginEvent里咱們得到stage 的entry point,然后異步調(diào)用得到成果。entry point 便是咱們上例build得到的成果。 仿制代碼 private async Task RunApp(AppFunc entryPoint, IDictionary?environment, TaskCompletionSourcetcs, StageAsyncResult result) { try { await entryPoint(environment); tcs.TrySetResult(null); result.TryComplete(); } catch (Exception ex) { // Flow the exception back through the OWIN pipeline. tcs.TrySetException(ex); result.TryComplete(); } } 仿制代碼 然后咱們剖析一下怎樣在不同的管道中注冊(cè)事情。在MSDN的文檔描述的。 app.UseStageMarker(PipelineStage.Authenticate) 這個(gè)api會(huì)創(chuàng)立一個(gè)IntegratedPipelineBlueprintStage,上文說這是一個(gè)鏈表結(jié)構(gòu),之間用next屬性連接。在不同的stage中會(huì)有entry point,然后在上面的比如中注冊(cè)到不同的管道中去調(diào)用。下 圖是api的代碼。 仿制代碼 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)) { var addMarker = (Action)obj; addMarker(app, stageName); } return app; } 仿制代碼 好的,到這兒了,謝謝我們閱覽,假如有任何不了解的歡迎溝通:)
本文來自:https://www.jmwww.net/a/13201.html?轉(zhuǎn)載請(qǐng)注明
總結(jié)
以上是生活随笔為你收集整理的Owin Katana 的底层源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win11彻底卸载微软bing输入法,包
- 下一篇: Parallel GC