zoukankan      html  css  js  c++  java
  • 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的初始化

     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实际上是对ConvertOneHopConvertMultiHop的封装。

    先看看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                 };

    上面的代码演示了PreHandlerExcuteAuthenticate两个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(一)那一节所分析的结果,因为传入的参数是一个Typeargs长度为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

    OwinMiddlewareType

    Item2

    CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options)  构造函数

    Item3

    [IAppBuilder app,  CookieAuthenticationOptions options] 长度为2object[]

    再次执行

    app = Convert(needSignature, app)…>object oneHop = ConvertOneHop(signature, app)

    此时的参数app是一个AppFunc,而signature是一个OwinMiddleware,会用到_conversions,这将是OwinMiddlewareAppFunc之间互相转换的实现,需要用到AppBuilder时候对_conversions初始化的知识,留待下一章再说。

    总结AppBuilder.BuildeInternalmiddlewareList遍历是反向的,虽然现在还不明白为什么如此设计,而且如何在一个PipelineStage中执行多个middleware也还不明朗,曾经以为是使用类似Invoke += Middleware.Invoke实现的,但既然是反向的,这不就顺序反了吗?

    目前能确定下来的时候每个PipelineStageEntryPoint已经显式指定了,刚刚大概又想了一下,为了保证PipelineStage的规范性,那么每个PipelineStage应该都是一个Func<AppFunc, AppFunc>形式的才对,而Middleware应该是被封装在这两个AppFunc之间的,这么说,应该是_conversions来完成了同一个PipelineStage中的Middleware的串联工作了,理应如此。下一节再验证这个问题。

  • 相关阅读:
    file is universal (3 slices) but does not contain a(n) armv7s slice error for static libraries on iOS
    WebImageButton does not change images after being enabled in Javascript
    ajax OPTION
    编程遍历页面上所有TextBox控件并给它赋值为string.Empty?
    获取海洋天气预报
    C#线程系列教程(1):BeginInvoke和EndInvoke方法
    js控制只能输入数字和小数点
    Response.AddHeader(,)
    ManualResetEvent的理解
    Convert.ToInt32、int.Parse(Int32.Parse)、int.TryParse、(int) 区别
  • 原文地址:https://www.cnblogs.com/hmxb/p/5302866.html
Copyright © 2011-2022 走看看