Owin Katana 的底层源码分析

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 }

复制代码

看起来这个代码十分的优雅,添加几个reference和一个类就让请求到达Hello World。我们先分析这个类,首先程序集特性OwinStartupAtribute将当前类保存在元数据中。然后写了一个Configuration方法,获取一个IAppBuilder 参数调用Run方法,Run方法传递一个委托进去,我们的处理逻辑就在这一个委托里。

这里面我们分析一下核心接口IAppBuilder的经典实现者AppBuilder,IAppBuilder的接口如下,

复制代码

using System;

using System.Collections.Generic;

namespace Owin

{

    public interface IAppBuilder

    {

        IDictionary Properties { get; }//请求的参数

        object Build(Type returnType);//中间件链接

        IAppBuilder New();//创建一个新的对象

        IAppBuilder Use(object middleware, params object[] args);//注册中间件

    }

}

复制代码

好的我们来分析一下AppBuilder中间件的注册实现。在app.Run 打完break point你就可以进入app.use方法,首先在AppBuilderUseExtensions这个类里对use的入口写了一大堆扩展方法。app.Run就是其中的一个,当你用app.Run注册中间件的时候是没有下一个中间件的引用的。

复制代码

        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);

        }

复制代码

在经典的实现中,参数middleware会有两种情况,一种是delegate,一种是type,如果是type类型,则他的构造方法接受next为参数,并且里面有一个公开的Invoke方法。如果是委托,当前委托作为参数传递到next中。

public IAppBuilder Use(object middleware, params object[] args)

        {

            _middleware.Add(ToMiddlewareFactory(middleware, args));

            return this;

        }

下面的代码是ToMiddlewareFactory的实现,在第9行和第27行分别判断了中间件对象是委托类型还是type类型,由于本题例子是type对象,我们分析一下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类型的所有构造方法,第8行获取构造方法的所有参数,在第14行有个trick,用zip方法判断参数类型是否和构造方法类型是一致的。如果是一致的则继续往下走,在第22行和第23行利用委托将构造方法创建成lambda表达式,然后生成元祖,第一个是next的类型,第二个是type的构造方法,第三个是type构造方法所需的参数。然后将元祖加入到AppBuild所维护的中间件对象。

复制代码

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        }

复制代码

这个时候我们已经将中间件注册到AppBuilder对象了。注册完中间件的对象我们还需要做一件事就是将这些中间件chained together,这些实现就是Build 方法中,而Build方法BuildInternal方法,这个时候会产生一个entry point供调用。

现在我们重点看一下这个build方法。

    public object Build(Type returnType)

        {

            return BuildInternal(returnType);

        }

Build方法调用私有的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的,在我们之前注册的时候实际上middleware元祖会保存三个信息,第一个type就是构造函数的第一个类型,第二个委托是useHandlerMiddleware的构造方法,第三个是构造方法的参数(除了第一个),Reverse的方法会将中间件逆序,这样保证调用的顺序就是你注册的顺序,后面的是chain的逻辑,app的变量实际上就是下一个中间件构造函数的next,当得到第一个中间件的时候,里面的next会保存第二个中间件的处理逻辑,同样第二个next就是第三个...,这样chained together得到的就是第一个中间件的逻辑,所以你们在用app.Use的方法就会有一个参数next,并且需要手动调用一下。

得到这些之后需要的就是要将中间件注册到application管道事件呢。因为asp.net的是一个大的切面框架。

复制代码

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);

        }

复制代码

这里面有一个概念就是IntegratedPipelineBlueprintStage,这个是一个链表结构,每个对应的就是管道事件,每个stage都有entry point,这样方便我们在不同的管道事件中运行中间件,在BeginEvent里我们得到stage 的entry point,然后异步调用得到结果。entry point 就是我们上例build得到的结果。

复制代码

  private async Task RunApp(AppFunc entryPoint, IDictionary environment, TaskCompletionSource tcs, 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();

            }

        }

复制代码

然后我们分析一下怎么在不同的管道中注册事件。在MSDN的文档描述的。

app.UseStageMarker(PipelineStage.Authenticate)

这个api会创建一个IntegratedPipelineBlueprintStage,上文说这是一个链表结构,之间用next属性连接。在不同的stage中会有entry point,然后在上面的例子中注册到不同的管道中去调用。下图是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;

        }

亚马逊测评 www.yisuping.com

你可能感兴趣的:(Owin Katana 的底层源码分析)