• AppBuilder(四)SignatureConversions


    源码参见Microsoft.Owin.Builder.AppBuilder

                     Microsoft.Owin.Infrastructure.SignatureConversions

    AppBuilder中遇到了_middleware三元组Item1,微软工程师称之为signature不一致的问题,一种是AppFunc,一种是OwinMiddleware,因此需要用到SignatureConversions,这是在AppBuilder实例化的时候完成的工作,先看看AppBuilder的构造函数。

     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,还是一层封装

     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)这个方法之后做了什么。

     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,而ReturnTypeAppFunc

    解释一下转换原理,Conversion1这个方法return了一个AppFuncTransition实例,而AppFuncTransition继承自OwinMiddleware,自然就完成了转换

    Conversion2这个方法返回的是OwinMiddlewareTransition实例的Invoke方法,自然就是一个AppFunc

    可见两种签名对应的是OwinMiddleware实例和AppFunc委托的相互转换。

    回顾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作为入口。

  • 相关阅读:
    GetTickCount 和getTickCount
    载入其他同名源文件导致vs编译错误
    opencv的配置
    VS05错误:部署WEB文件失败
    c++移动文件夹
    opencv2.4.0版本不支持Mat的大小自动调整?
    关于c++中public & private方法调用问题
    c++读取文件夹及子文件夹数据
    深入理解java虚拟机-第四章
    深入理解java虚拟机-第三章
  • 原文地址:https://www.cnblogs.com/hmxb/p/5303940.html
Copyright © 2020-2023  润新知