源码参见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的初始化
1 private static readonly AppFunc NotFound = new NotFound().Invoke; //将NotFound.Invoke绑定到AppBuilder.NotFound上 2 internal class NotFound 3 { 4 private static readonly Task Completed = CreateCompletedTask(); 5 6 private static Task CreateCompletedTask() 7 { 8 var tcs = new TaskCompletionSource<object>(); 9 tcs.SetResult(null); 10 return tcs.Task; 11 } 12 13 public Task Invoke(IDictionary<string, object> env) //这是一个满足AppBuilder中对于AppFunc定义的一个方法,之前在这里老是被绕晕了 14 { 15 env["owin.ResponseStatusCode"] = 404; //设置StatusCode 16 return Completed; //返回一个Task 17 } 18 }
上面的代码展示了AppBuilder.NotFound是如何初始化为一个AppFunc的,这是对中间件的签名,对于后面的Convert方法来说至关重要。
1 private object BuildInternal(Type signature) 2 { 3 object app; 4 if (!_properties.TryGetValue(Constants.BuilderDefaultApp, out app)) //尝试寻找默认的最后一步处理方法,如果寻找失败则将app指向NotFound 5 { 6 app = NotFound; 7 } 8 9 foreach (var middleware in _middleware.Reverse()) //对List进行反向遍历,反向遍历很重要,这样上一节所说的UseStageMarker对stage.Name的处理方式才能理解 10 { 11 Type neededSignature = middleware.Item1; //解耦三元组 12 Delegate middlewareDelegate = middleware.Item2; 13 object[] middlewareArgs = middleware.Item3; 14 15 app = Convert(neededSignature, app); //尝试将app的Invoke方法创建为一个委托,委托为needSignature所表示的Type,听起来有点绕,没关系,慢慢来 16 //这将涉及到pipeline中AppFunc与Middleware的转换,这是OWIN的精华所在 17 object[] invokeParameters = new[] { app }.Concat(middlewareArgs).ToArray(); //将app作为第一个参数与args合并 18 app = middlewareDelegate.DynamicInvoke(invokeParameters); 19 app = Convert(neededSignature, app); //这一步我也没大懂,到后面懂了再说 20 } 21 22 return Convert(signature, app); //同理这一步我也没大懂 23 }
从实际例子出发容易理解上面的流程一些,上一章讲到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做了什么。
1 private object Convert(Type signature, object app) 2 { 3 if (app == null) 4 { 5 return null; 6 } 7 8 object oneHop = ConvertOneHop(signature, app); 9 if (oneHop != null) 10 { 11 return oneHop; 12 } 13 14 object multiHop = ConvertMultiHop(signature, app); 15 if (multiHop != null) 16 { 17 return multiHop; 18 } 19 throw new ArgumentException( 20 string.Format(CultureInfo.CurrentCulture, Resources.Exception_NoConversionExists, app.GetType(), signature), 21 "signature"); 22 23 }
Covert实际上是对ConvertOneHop和ConvertMultiHop的封装。
先看看ConvertOneHop方法。
1 private object ConvertOneHop(Type signature, object app) 2 { 3 if (signature.IsInstanceOfType(app)) //针对上面的例子,app确实是signature的一个实例,都对应Func<Idictionary<string, object>, Task> 4 { 5 return app; //所以第一次调用会直接返回 6 } 7 if (typeof(Delegate).IsAssignableFrom(signature)) //如果signature是对Delegate的继承 8 { 9 Delegate memberDelegate = ToMemberDelegate(signature, app); //尝试将app的Invoke方法创建为一个signature所表示的Type类型的委托 10 if (memberDelegate != null) 11 { 12 return memberDelegate; 13 } 14 } 15 foreach (var conversion in _conversions) //如果app的Invoke方法与signature的Invoke方法冲突,需要进行转换 16 //这是Middleware与AppFunc之间的重要转换,也是pipeline的重点,留到后文详述 17 { 18 Type returnType = conversion.Key.Item1; 19 Type parameterType = conversion.Key.Item2; 20 if (parameterType.IsInstanceOfType(app) && 21 signature.IsAssignableFrom(returnType)) 22 { 23 return conversion.Value.DynamicInvoke(app); 24 } 25 } 26 return null; 27 }
再回头看看_middleware.Rerverse遍历的第一次中,Convert(needSignature,app)会很快返回,值就是app,也就是Func<Idictionary<string, object>, Task>的一个实例,再运行app = middlewareDelegate.DynamicInvoke(invokeParameters)的时候,因为app已经合并进invokeParameters中所以,等同于执行
1 app => 2 { 3 if (string.Equals(name, stage.Name, StringComparison.OrdinalIgnoreCase)) //name = "Authenticate", stage.Name = "PreHandlerExecute",返回false 4 { 5 // no decoupling needed when pipeline is already split at this name 6 return app ; 7 } 8 if (!IntegratedPipelineContext.VerifyStageOrder(name, stage.Name)) //name = "Authenticate", stage.Name = "PreHandlerExecute",name < stage.Name,返回false,注意前面有个'!' 9 { 10 // Stage markers added out of order will be ignored. 11 // Out of order stages/middleware may be run earlier than expected. 12 // TODO: LOG 13 return app ; 14 } 15 stage.EntryPoint = app ; //设置PreHandlerExecute这一Stage的EntryPoint为app,此时的app就是NotFound.Invoke方法 16 stage = new IntegratedPipelineBlueprintStage //为Authenticate新建一个IntegratedPipelineBlueprintStage,NextStage绑定到PreHandlerExcute这一Stage上 17 //所以两个PipelineStage就链接起来了 18 { 19 Name = name, 20 NextStage = stage, 21 }; 22 onStageCreated(stage); //更新firstStage,使其指向Autenticate这一Stage 23 return (AppFunc)IntegratedPipelineContext.ExitPointInvoked; //返回ExitPointInvoked方法 24 };
上面的代码演示了PreHandlerExcute和Authenticate两个PipelineStage是如何串接在一起的,再来看看IntegratedPipelineContext.ExitPointInvoked到底干了什么。
1 public static Task ExitPointInvoked(IDictionary<string, object> env) 2 { 3 object value; 4 if (env.TryGetValue(Constants.IntegratedPipelineContext, out value)) //尝试从environment中获取IntegratedPipelineContext实例, 5 { 6 var self = (IntegratedPipelineContext)value; 7 return self._state.ExecutingStage.ExitPointInvoked(env); //改变当前管道状态,使其可以流入下一管道 8 } 9 throw new InvalidOperationException(); 10 } 11 public Task ExitPointInvoked(IDictionary<string, object> env) 12 { 13 _context.PreventNextStage = false; //改变当前管道状态 14 return Epilog(env); //进行最后的收尾工作 15 } 16 17 private Task Epilog(IDictionary<string, object> env) 18 { 19 var tcs = new TaskCompletionSource<object>(); 20 _responseShouldEnd = false; //开启response,因为即将进行Stage的切换,与Stage刚开始执行的时候关闭response相对应 21 _context.PushLastObjects(env, tcs); //验证当前pipeline所在Stage中的environment为null,TaskCompletionSource<object>为null,因为即将离开Stage,而Stage是公用的 22 //这与IntegratedPipelineContextStage.BeginEvent中的TakeLastEnvironment,TakeLastCompletionSource相对应,都是原子操作 23 StageAsyncResult result = Interlocked.Exchange(ref _result, null); 24 if (result != null) 25 { 26 result.TryComplete(); 27 } 28 return tcs.Task; 29 }
扯了好远,在没有进行调试的情况下推断这些运行流程还真是很累的一件事儿。这对于前面没有搞懂的地方有很大帮助,看代码。
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的串联工作了,理应如此。下一节再验证这个问题。