• AppBuilder(一)Use汇总


    源码参见Microsoft.Owin.Host.SystemWeb.OwinBuilder

       Microsoft.Owin.Builder.AppBuilder

    前文讲到

    internal static OwinAppContext Build()
            {
                Action<IAppBuilder> startup = GetAppStartup();
                return Build(startup);
            }

    GetAppStartup()已经寻找到Startup类,并封装了其中的Configuration方法,接下来就会调用Build(startup)方法。

     1 internal static OwinAppContext Build(Action<IAppBuilder> startup)
     2         {
     3             if (startup == null)
     4             {
     5                 throw new ArgumentNullException("startup");
     6             }
     7 
     8             var appContext = new OwinAppContext();    //实例化OwinAppContext,主要获得网站名称和程序名称
     9             appContext.Initialize(startup);    //进一步初始化OwinAppContext
    10             return appContext;
    11         }

    来看看OwinAppContext.Initialize(IAppBuilder)做了什么

     1 internal void Initialize(Action<IAppBuilder> startup)
     2         {
     3             Capabilities = new ConcurrentDictionary<string, object>();    //初始化为多线程安全的Dictionary,说明为多线程共享资源
     4 
     5             var builder = new AppBuilder();    //初始化AppBuilder,向Properties中填入一些基本信息,这构成了Server环境的快照
     6             builder.Properties[Constants.OwinVersionKey] = Constants.OwinVersion;
     7             builder.Properties[Constants.HostTraceOutputKey] = TraceTextWriter.Instance;
     8             builder.Properties[Constants.HostAppNameKey] = AppName;
     9             builder.Properties[Constants.HostOnAppDisposingKey] = OwinApplication.ShutdownToken;
    10             builder.Properties[Constants.HostReferencedAssemblies] = new ReferencedAssembliesWrapper();
    11             builder.Properties[Constants.ServerCapabilitiesKey] = Capabilities;
    12             builder.Properties[Constants.SecurityDataProtectionProvider] = new MachineKeyDataProtectionProvider().ToOwinFunction();
    13             builder.SetLoggerFactory(new DiagnosticsLoggerFactory());
    14 
    15             Capabilities[Constants.SendFileVersionKey] = Constants.SendFileVersion;
    16 
    17             CompilationSection compilationSection = (CompilationSection)System.Configuration.ConfigurationManager.GetSection(@"system.web/compilation");
    18             bool isDebugEnabled = compilationSection.Debug;
    19             if (isDebugEnabled)
    20             {
    21                 builder.Properties[Constants.HostAppModeKey] = Constants.AppModeDevelopment;
    22             }
    23 
    24             DetectWebSocketSupportStageOne();
    25 
    26             try
    27             {
    28                 startup(builder);    //真正的由用户定义的middleware注入开始了,这将完成pipeline的构造
    29             }
    30             catch (Exception ex)
    31             {
    32                 _trace.WriteError(Resources.Trace_EntryPointException, ex);
    33                 throw;
    34             }
    35 
    36             AppFunc = (AppFunc)builder.Build(typeof(AppFunc));//记录pipeline的第一个middleware,这将是整个pipeline的入口
    37         }

    先从AppBuilder的实例化开始

     1 public AppBuilder()
     2         {
     3             _properties = new Dictionary<string, object>();    //初始化environment资源
     4             _conversions = new Dictionary<Tuple<Type, Type>, Delegate>();    //初始化转换器,这将不是本文重点,后期再涉及
     5             _middleware = new List<Tuple<Type, Delegate, object[]>>();    //初始化middleware列表
     6 
     7             _properties[Constants.BuilderAddConversion] = new Action<Delegate>(AddSignatureConversion);    //记录Conversion
     8             _properties[Constants.BuilderDefaultApp] = NotFound;
     9 
    10             SignatureConversions.AddConversions(this);
    11         }

    上面的源码展示了AppBuilder初始化pipeline流动所需的envrionmentmiddleware,这也是OWIN最重要的两个数据结构。

    builder完成实例化之后会尝试startup(builder)这即是调用用户定义的Configuration(IAppBuilder)方法。

     1 public partial class Startup
     2     {
     3         public void Configuration(IAppBuilder app)
     4         {
     5             ConfigureAuth(app);
     6         }
     7     }
     8 public partial class Startup
     9     {
    10         public void ConfigureAuth(IAppBuilder app)
    11         {
    12 
    13             app.CreatePerOwinContext<BlogContext>(BlogContext.Create);    //将数据库上下文注入envrionment中
    14             app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);    //将AppUserManager注入environment中
    15             app.CreatePerOwinContext<AppSigninManager>(AppSigninManager.Create);    //将AppSigninManager注入environment中
    16 
    17             app.UseCookieAuthentication(new CookieAuthenticationOptions    //标记CookeAuthentication这个middleware在pipeline中的stage,并配置参数
    18             {
    19                 AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    20                 LoginPath = new PathString("/Account/Login"),
    21                 Provider = new CookieAuthenticationProvider
    22                 {
    23                     // Enables the application to validate the security stamp when the user logs in.
    24                     // This is a security feature which is used when you change a password or add an external login to your account. 
    25                     OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<AppUserManager, AppUser>(
    26                         validateInterval: TimeSpan.FromMinutes(30),
    27                         regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
    28                 }
    29             });
    30 
    31             // Enables the application to remember the second login verification factor such as phone or email.
    32             // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
    33             // This is similar to the RememberMe option when you log in.
    34             app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);    //标记stage
    35         }
    36 
    37     }

    以新建MVC工程默认生成的Starup类为例。目前我们可以大致猜测一下这份配置方法中只进行了两个stage的注册,而pipeline11stage之多,具体如下

     1 private static readonly IList<string> StageNames = new[]
     2         {
     3             Constants.StageAuthenticate,
     4             Constants.StagePostAuthenticate,
     5             Constants.StageAuthorize,
     6             Constants.StagePostAuthorize,
     7             Constants.StageResolveCache,
     8             Constants.StagePostResolveCache,
     9             Constants.StageMapHandler,
    10             Constants.StagePostMapHandler,
    11             Constants.StageAcquireState,
    12             Constants.StagePostAcquireState,
    13             Constants.StagePreHandlerExecute,
    14         };

    接下来我们通过对app.UseCookieAuthentication(new CookieAuthenticationOptions)的追踪来了解如何进行middleware的注册。

     1 public static class CookieAuthenticationExtensions
     2     {
     3         public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options)
     4         {
     5             return app.UseCookieAuthentication(options, PipelineStage.Authenticate);    //在PipelineStage.Authenticate阶段执行
     6         }
     7 
     8         public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options, PipelineStage stage)
     9         {
    10             if (app == null)
    11             {
    12                 throw new ArgumentNullException("app");
    13             }
    14 
    15             app.Use(typeof(CookieAuthenticationMiddleware), app, options);    //app.Use实际上就是将middleware压入middleware的List中
    16             app.UseStageMarker(stage);    //标记middleware所在stage
    17             return app;
    18         }
    19     }

    这里涉及到AppBuilder的两个核心操作app.Useapp.UseStageMarker。

    先来看看app.Use,在AppBuilder中定义了Use(object middleware, params object[] args),而在AppBuilderUseExtensions还有两种定义,我们逐个来解释。

      public IAppBuilder Use(object middleware, params object[] args)
            {
                _middleware.Add(ToMiddlewareFactory(middleware, args));
                return this;
            }

    可见这个方法主要做的操作是将middleware进行转换并压入middlewareList中,转换过程实际上对middleware进行参数检查和签名,这将是一个非常复杂的过程,一步一步地来。

    先看看ToMiddlewareFactory方法。

     1 private static Tuple<Type, Delegate, object[]> ToMiddlewareFactory(object middlewareObject, object[] args)    //接受两个参数,返回一个三元组
     2         {
     3             if (middlewareObject == null)
     4             {
     5                 throw new ArgumentNullException("middlewareObject");
     6             }
     7 
     8             var middlewareDelegate = middlewareObject as Delegate;    //尝试将middlewareObject类型转换为Delegate,而我们上面传入的是一个Type,将会转换失败
     9             if (middlewareDelegate != null)    //如果转换成功
    10             {
    11                 return Tuple.Create(GetParameterType(middlewareDelegate), middlewareDelegate, args);    //直接返回三元组,这似乎是最理想的情况
    12             }
    13 
    14             Tuple<Type, Delegate, object[]> factory = ToInstanceMiddlewareFactory(middlewareObject, args);    //尝试在middlewareObject类中寻找Initialize方法,并检查args参
    15     //数是否符合Initialize方法的参数
    16             if (factory != null)
    17             {
    18                 return factory;
    19             }
    20 
    21             factory = ToGeneratorMiddlewareFactory(middlewareObject, args);    //尝试在middlewareObject类中寻找Invoke方法,并作参数检查
    22 
    23             if (factory != null)
    24             {
    25                 return factory;
    26             }
    27 
    28             if (middlewareObject is Type)    //这是本文的app.Use方法传入的参数类型,所以会进入这一流程中
    29             {
    30                 return ToConstructorMiddlewareFactory(middlewareObject, args, ref middlewareDelegate);
    31             }
    32 
    33             throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture,
    34                 Resources.Exception_MiddlewareNotSupported, middlewareObject.GetType().FullName));
    35 
    36         }

    接下来将分别对ToInstanceMiddlewareFactory,ToGeneratorMiddlewareFactory,ToConstructorMiddlewareFactory进行介绍。

     1 private static Tuple<Type, Delegate, object[]> ToInstanceMiddlewareFactory(object middlewareObject, object[] args)
     2         {
     3             MethodInfo[] methods = middlewareObject.GetType().GetMethods();    //将middleware当作一个实例处理,获取其Type,再获得类中所有方法
     4             foreach (var method in methods)    //遍历方法
     5             {
     6                 if (method.Name != Constants.Initialize)    //寻找Initialize方法
     7                 {
     8                     continue;
     9                 }
    10                 ParameterInfo[] parameters = method.GetParameters();    //获取Initialize的参数表
    11                 Type[] parameterTypes = parameters.Select(p => p.ParameterType).ToArray();    //获取参数表的Type类型
    12 
    13                 if (parameterTypes.Length != args.Length + 1)    //Initialize的参数应该比args的参数多一个,因为第一个参数应该是下一个middleware
    14                 {
    15                     continue;
    16                 }
    17                 if (!parameterTypes
    18                     .Skip(1)
    19                     .Zip(args, TestArgForParameter)
    20                     .All(x => x))
    21                 {    //对Initialize第一个以后的参数与args进行一对一验证,args应该为对应项的实例
    22                     continue;
    23                 }
    24          
    25                 // DynamicInvoke can't handle a middleware with multiple args, just push the args in via closure.
    26                //开发者在此处注释动态Invoke无法处理middleware有多个参数的情况,所以进行了一次封装,简化参数表
    27                 Func<object, object> func = app =>
    28                 {
    29                     object[] invokeParameters = new[] { app }.Concat(args).ToArray();    //将app(实际上为envrionment)参数与args合并
    30                     method.Invoke(middlewareObject, invokeParameters);    //真正调用middleware的Initialize方法
    31                     return middlewareObject;    //返回middlewareObject实例
    32                 };
    33 
    34                 return Tuple.Create<Type, Delegate, object[]>(parameters[0].ParameterType, func, new object[0]);
    35             }
    36             return null;
    37         }

    ToInstanceMiddlewareFactory返回的是一个具体的实例,似乎不满足Func<IDictionary<string, object>, Task>类型,这也是目前没弄明白的地方

     1 private static Tuple<Type, Delegate, object[]> ToGeneratorMiddlewareFactory(object middlewareObject, object[] args)
     2         {
     3             MethodInfo[] methods = middlewareObject.GetType().GetMethods();    //将middlewareObject当作一个实例,获取Type并获取所有方法
     4             foreach (var method in methods)
     5             {
     6                 if (method.Name != Constants.Invoke)    //寻找Invoke方法
     7                 {
     8                     continue;
     9                 }
    10                 ParameterInfo[] parameters = method.GetParameters();    //获取Invoke方法的参数表
    11                 Type[] parameterTypes = parameters.Select(p => p.ParameterType).ToArray();    //获取参数的Type表
    12 
    13                 if (parameterTypes.Length != args.Length + 1)
    14                 {
    15                     continue;
    16                 }
    17                 if (!parameterTypes
    18                     .Skip(1)
    19                     .Zip(args, TestArgForParameter)
    20                     .All(x => x))
    21                 {    //将Type表与args进行一对一验证
    22                     continue;
    23                 }
    24                 IEnumerable<Type> genericFuncTypes = parameterTypes.Concat(new[] { method.ReturnType });    //将参数Type表与返回Type合并
    25                 Type funcType = Expression.GetFuncType(genericFuncTypes.ToArray());    //获取方法签名Type
    26                 Delegate middlewareDelegate = Delegate.CreateDelegate(funcType, middlewareObject, method);    //对实例的Invoke方法进行delegate封装
    27                 return Tuple.Create(parameters[0].ParameterType, middlewareDelegate, args);
    28             }
    29             return null;
    30         }

    ToGeneratorMiddlewareFactory返回类型由Invoke方法决定,通常会是一个Task

     1 private static Tuple<Type, Delegate, object[]> ToConstructorMiddlewareFactory(object middlewareObject, object[] args, ref Delegate middlewareDelegate)
     2         {
     3             var middlewareType = middlewareObject as Type;    //尝试将middlwareObject转换为Type
     4             ConstructorInfo[] constructors = middlewareType.GetConstructors();    //获取类的构造方法
     5             foreach (var constructor in constructors)    //遍历构造方法
     6             {
     7                 ParameterInfo[] parameters = constructor.GetParameters();    //获取构造函数的参数表
     8                 Type[] parameterTypes = parameters.Select(p => p.ParameterType).ToArray();    //获取参数的Type表
     9                 if (parameterTypes.Length != args.Length + 1)    //参数表应该比args多一项,第一项应该为下一个middleware
    10                 {
    11                     continue;
    12                 }
    13                 if (!parameterTypes
    14                     .Skip(1)
    15                     .Zip(args, TestArgForParameter)
    16                     .All(x => x))
    17                 {    //对参数表的第一项之后的与args一对一校验
    18                     continue;
    19                 }
    20 
    21                 ParameterExpression[] parameterExpressions = parameters.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();    //提取参数表的Type和Name
    22                 NewExpression callConstructor = Expression.New(constructor, parameterExpressions);    //创建构造函数
    23                 middlewareDelegate = Expression.Lambda(callConstructor, parameterExpressions).Compile();    //对构造函数进行Lambda封装
    24                 return Tuple.Create(parameters[0].ParameterType, middlewareDelegate, args);
    25             }
    26 
    27             throw new MissingMethodException(string.Format(CultureInfo.CurrentCulture,
    28                 Resources.Exception_NoConstructorFound, middlewareType.FullName, args.Length + 1));
    29         }

    上面三种方法让我们看见了微软工程师的花样使用委托、LambdaFunc,如果不是资深C#程序员,谁知道这语言还有这些特性,但能感觉到主要是想将原来类中的初始化方法进行归一化,封装在一个匿名delegate中,并合成一个三元组,后面采用统一的方法进行调用,这个三元组的Item1就是共用的下一个middleware也是当前middleware所在pipelineStage的签名。

    再来看看AppBuilderUseExtension扩展的两个Use方法和一个Run方法。

     1 public static IAppBuilder Use<T>(this IAppBuilder app, params object[] args)
     2         {
     3             if (app == null)
     4             {
     5                 throw new ArgumentNullException("app");
     6             }
     7 
     8             return app.Use(typeof(T), args);
     9 
    10         }

    这个Use方法将middlewareType放在Use<T>中,做为一个模板方法,实际上是对Use(typeof(T), args)的一个封装。

     1 public static IAppBuilder Use(this IAppBuilder app, Func<IOwinContext, Func<Task> /*next*/, Task> handler)
     2         {
     3             if (app == null)
     4             {
     5                 throw new ArgumentNullException("app");
     6             }
     7             if (handler == null)
     8             {
     9                 throw new ArgumentNullException("handler");
    10             }
    11 
    12             return app.Use<UseHandlerMiddleware>(handler);
    13         }

    这个Use方法接受一个Func<IOwinContext, Func<Task> , Task>的委托,并对Use<UseHandlerMiddleware>进行封装,为了后文引述方便我们将其重命名为UseExtensionOne

     1 public static void Run(this IAppBuilder app, Func<IOwinContext, Task> handler)
     2         {
     3             if (app == null)
     4             {
     5                 throw new ArgumentNullException("app");
     6             }
     7             if (handler == null)
     8             {
     9                 throw new ArgumentNullException("handler");
    10             }
    11 
    12             app.Use<UseHandlerMiddleware>(handler);
    13         }

    这个Run方法接受一个 Func<IOwinContext, Task> 的委托,并对Use<UseHandlerMiddleware>进行封装,将其重命名为RunExtensionTwo方法。

    现在查看微软对于Middleware的使用举例页面 http://www.asp.net/aspnet/overview/owin-and-katana/owin-middleware-in-the-iis-integrated-pipeline

    网页中黄色部分对middleware的注入有具体举例,如下

     1 //这是对UseExtensionOne的实际应用
     2             app.Use((context, next) =>
     3             {
     4                 PrintCurrentIntegratedPipelineStage(context, "Middleware 1");
     5                 return next.Invoke();
     6             });
     7             //这是对UseExtensionOne的实际应用
     8             app.Use((context, next) =>
     9             {
    10                 PrintCurrentIntegratedPipelineStage(context, "2nd MW");
    11                 return next.Invoke();
    12             }); 
    13             //这是对RunExtentionTwo的实际应用
    14             app.Run(context =>
    15             {
    16                 PrintCurrentIntegratedPipelineStage(context, "3rd MW");
    17                 return context.Response.WriteAsync("Hello world");
    18             });     

    三个很简单的middleware被注入到pipeline中,但因为实际上是对Use<UseHandlerMiddleware>的封装,也就是对Use(typeof(T), args)的封装,所以实际上与 app.Use(typeof(CookieAuthenticationMiddleware), app, options)差不多,最终都会存成一样的三元组压进middlewareList中,所以他们都会调用UseHandlerMiddleware的构造函数,而查看UseHandlerMiddleware发现其构造函数的第一项就是一个AppFunc,这与middlewareStage的切换和pipeline的流动息息相关,后面将详细介绍其原理。

    总结,本文主要讲了AppBuilderUse方法的具体流程与扩展,三种扩展方法实际上都是对基础Use的封装,而基础Use方法总的来说可以接受四种middlewareObject

    1

    Delegate是最简单的,直接可以封装成三元组压入List

    2

    Initialize方法的类的实例,参数表第一项为一个AppFunc或者OwinMiddleware,只要其Invoke之后能返回一个Task就行,为了避免DynamicInvoke的弊端进行了一次封装,

    3

    Invoke方法的类的实例,参数表也需要汇聚到一个object[]中,这两种设计应该是有不同需求背景的,目前不知道究竟有什么不同

    4

    Type,这需要对这个类的构造方法进行封装,参考UseHandlerMiddleware的构造函数,第一个参数应该是一个AppFunc

    需要理解这四种Use方法的不同,还需要了解pipeline重构是如何做的,这就是下一节AppBuilder.Build中的具体内容了,里面有很多细节的东西,这将直接构建整个pipeline,也是整个OWIN最核心的地方。

  • 相关阅读:
    2-SIFT简介
    1-SIFT资源整理
    1-vs2015+opencv 3.2.0配置
    5-load-on-startup
    4-监听器
    3-过滤器
    第09组 Alpha冲刺 (1/6)
    第09组(71) 需求分析报告
    第9组(71) 团队展示
    结对编程作业
  • 原文地址:https://www.cnblogs.com/hmxb/p/5299216.html
Copyright © 2020-2023  润新知