[Asp.net 5] ApplicationBuilder详解

ApplicationBuilder(IApplicationBuilder接口),是OWIN的基础,而且里面都是代理、代理的代理,各种lambda表达式,估计要看这部分代码,很多人得头昏脑涨。今天就对个类以及几个扩展方法进行讲解。

按惯例先贴代码(这是我修改后的,将接口继承去掉了、HttpContext类修改成自己的MyHttpContext类)

public class ApplicationBuilder
    {
        private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();

        public ApplicationBuilder() { }

        private ApplicationBuilder(ApplicationBuilder builder)
        {
        }

        public ApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
        {
            _components.Add(middleware);
            return this;
        }

        public ApplicationBuilder New()
        {
            return new ApplicationBuilder(this);
        }

        public RequestDelegate Build()
        {
            RequestDelegate app = context =>
            {
                context.StatusCode = "404";
                System.Console.WriteLine("404");
                return Task.FromResult(0);
            };

            foreach (var component in _components.Reverse())
            {
                app = component(app);
            }

            return app;
        }

    }

RequestDelegate的定义如下:

public delegate Task RequestDelegate(MyHttpContext context);

从ApplicationBuilder的源代码中我们可以关注3个点:_components、Use方法、Build方法。

  • _components是也一个列表(IList)对象,不过里面类型有点特殊——是以代理RequestDelegate为参数、代理RequestDelegate为返回值的一个代理。这里用代理说有点别嘴,可以把代理叫做函数,就是里面的类型是一个函数,这个函数的参数也是函数,返回值也是函数。
  • Use方法,就是在上面的列表对象后面添加一条新记录。
  • Build方法就是将_components数组按照反向顺序,制作成一个链式结构(有点类似链表的感觉)。下面用俩幅图说明下:

  Build之前

 

  Build之后

  我们还可以从代码中看到Item1的参数给的是“404”,而返回结果是RequestDelegate类型。也就是说这个返回类似于void RequestDelegate(MyHttpContext context)。如果系统给我们一个context变量,那么这个管道就可以从头到尾的跑下去了。而事实上在Asp.net5中,这个管道就是用于替代传统的IHttpModule的(可能不准确),那现在问题就来了,Item1的参数是这个管道的第一环还是最后一环呢?从图形来看应该是第一环,但是事实上这是一个误解。因为箭头两面一个是参数,一个是执行体(参数是一个方法,会在执行体内调用执行)。在执行体内,可能在开始就执行参数的内容,之后执行具体的内容;也可以是先执行具体内容,之后执行参数,最后在执行一部分具体内容;还可以先执行具体内容,之后参数;还可能无视参数,直接直接自己的内容,那么之前的参数就会被忽略。也就是说无所谓顺序,404可能是管道的第一环,也可能是最后一环,也可能是中间环节,还可能压根就不执行。这个和Item1、Item2等内容具体的写法有关系。(虽然也是链式结构是不是和链表感觉不一样

  是不是感觉太零活了,源码还对ApplicationBuilder做了俩个扩展方法,代码整理如下:

   public static class RunExtensions
    {

        public static ApplicationBuilder Use(this ApplicationBuilder app, Func<MyHttpContext, Func<Task>, Task> middleware)
        {
            return app.Use(next =>
            {
                return context =>
                {
                    Func<Task> simpleNext = () => next(context);
                    return middleware(context, simpleNext);
                };
            });
        }

        public static void Run(this ApplicationBuilder app, RequestDelegate handler)
        {
            if (app == null)
            {
                throw new ArgumentNullException("why?");
            }

            if (handler == null)
            {
                throw new ArgumentNullException("How?");
            }
            app.Use(_ => handler);
        }
    }

  首先说Use方法,改方法是对之前Use方法的一个更改。将传入的参数更改为 Func<MyHttpContext, Func<Task>, Task>。这样做有什么好处?之前的Func<RequestDelegate, RequestDelegate>对象并不能给人清楚的明了的感觉,而Func<MyHttpContext, Func<Task>, Task>就非常明确了。传入的参数:MyHttpContext就是Context对象,Func<Task>就是next的执行体。返回值是一个Task(类似于void)。一目了然。

  再说Run方法,显而易见,Run方法只执行自己的内容,并没有执行参数体。所以链式结构的在其前的都会被舍弃,不会被执行。

  最后把自己的测试例子贴出来,供大家参考

示例1:

static void Main(string[] args)
        {

            MyHttpContext context = new MyHttpContext() { StatusCode = "A" };

            Func<MyHttpContext, Func<Task>, Task> middleware =
                (x, y) => { context.StatusCode += "C"; System.Console.WriteLine(context.StatusCode); return y(); };

            Func<MyHttpContext, Func<Task>, Task> middleware2 =
              (x, y) => { context.StatusCode += "End1"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); };

            ApplicationBuilder builder = new ApplicationBuilder();

            builder.Use(
                next =>
                {
                    return (MyHttpContext o) =>
                    {
                        o.StatusCode += "B";
                        System.Console.WriteLine(context.StatusCode);
                        next(o);
                        return Task.FromResult(0);
                    };
                }
            );

            builder.Use(middleware);
            //builder.Use(middleware2);
            //builder.Use(middleware);
            //builder.Run(o => { o.StatusCode += "End2"; return Task.FromResult(0); });

            builder.Build().Invoke(context);
            System.Console.ReadLine();
        }

执行结果:

示例2:

static void Main(string[] args)
        {

            MyHttpContext context = new MyHttpContext() { StatusCode = "A" };

            Func<MyHttpContext, Func<Task>, Task> middleware =
                (x, y) => { context.StatusCode += "C"; System.Console.WriteLine(context.StatusCode); return y(); };

            Func<MyHttpContext, Func<Task>, Task> middleware2 =
              (x, y) => { context.StatusCode += "End1"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); };

            ApplicationBuilder builder = new ApplicationBuilder();

            builder.Use(
                next =>
                {
                    return (MyHttpContext o) =>
                    {
                        o.StatusCode += "B";
                        System.Console.WriteLine(context.StatusCode);
                        next(o);
                        return Task.FromResult(0);
                    };
                }
            );

            builder.Use(middleware);
            builder.Use(middleware2);
            //builder.Use(middleware);
            //builder.Run(o => { o.StatusCode += "End2"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); });

            builder.Build().Invoke(context);
            System.Console.ReadLine();
        }

执行结果:

示例3:

static void Main(string[] args)
        {

            MyHttpContext context = new MyHttpContext() { StatusCode = "A" };

            Func<MyHttpContext, Func<Task>, Task> middleware =
                (x, y) => { context.StatusCode += "C"; System.Console.WriteLine(context.StatusCode); return y(); };

            Func<MyHttpContext, Func<Task>, Task> middleware2 =
              (x, y) => { context.StatusCode += "End1"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); };

            ApplicationBuilder builder = new ApplicationBuilder();

            builder.Use(
                next =>
                {
                    return (MyHttpContext o) =>
                    {
                        o.StatusCode += "B";
                        System.Console.WriteLine(context.StatusCode);
                        next(o);
                        return Task.FromResult(0);
                    };
                }
            );

            builder.Use(middleware);
            //builder.Use(middleware2);
            //builder.Use(middleware);
            builder.Run(o => { o.StatusCode += "End2"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); });

            builder.Build().Invoke(context);
            System.Console.ReadLine();
        }

执行结果:

时间: 2024-07-28 21:28:50

[Asp.net 5] ApplicationBuilder详解的相关文章

ASP.NET生命周期详解 [转]

最近一直在学习ASP.NET MVC的生命周期,发现ASP.NET MVC是建立在ASP.NET Framework基础之上的,所以原来对于ASP.NET WebForm中的很多处理流程,如管道事件等,对于ASP.NET MVC同样适用.只是MVC URLRouting Module对进入到server的request进行了拦截,然后对此次request的handler进行了特殊的处理.总结来说,就是 ASP.NET管道是所有ASP.NET Web Applicaiton,包括WebForm,

ASP.NET运行时详解 中篇

遗留问题 在ASP.NET运行时详解 上篇中遗留两个问题,包括Application的InitInternal方法执行细节.IIS6和II7经典模式请求管道管理类ApplicationStepManager和IIS7请求管道管理类PipelineStepManager的实现细节.这两个问题贯穿了整个ASP.NET运行过程.所以,要把ASP.NET运行过程了解清楚,这两个问题不得不解决.    为了大家更容易切入该篇的内容,我们先回顾下这两个问题: 1. Application的InitInter

ASP.NET Global.asax详解

http://blog.csdn.net/xiarenwang/article/details/7633160 文档来源:http://club.topsage.com/thread-485397-1-1.html global.asax是一个文本文件,它提供全局可用代码.这些代码包括应用程序的事件处理程序以及会话事件.方法和静态变量.有时该文件也被称为应用程序文件. global.asax 文件中的任何代码都是它所在的应用程序的一部分.每个应用程序在其根目录下只能有一个global.asax文

ASP.NET 运行时详解 揭开请求过程神秘面纱

对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就一步步揭开请求管道神秘面纱. 上篇回顾 在介绍本篇内容之前,让我们先回顾下上一篇<ASP.NET运行时详解 集成模式和经典模式>的主要内容.在上一篇随笔中,我们提到ASP.NET运行时通过Application的InitInternal方法初始化运行管道.ASP.NET运行时提供了两种初始化管道模

ASP.NET状态管理详解,让你明明白白

开发WinFrom的程序员可能不会在意维护应用程序的状态,因为WinFrom本身就在客户端运行,可以直接在内存中维护其应用程序状态.但ASP.NET应用程序在服务器端运行,客户端使用无状态的http协议对ASP.NET应用程序发出请求,ASP.NET应用程序响应用户请求,向客户端发送请求的HTML代码,服务器并不会维护任何客户端状态.考虑一个有成千上万并发用户的服务器,如果为每一个用户都维护状态的话会耗费非常多的资源. 由于使用无状态的http协议作为web应用程序的通信协议,当客户端每次请求页

ASP.NET 操作Cookie详解 增加,修改,删除

Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密).定义于RFC2109.它是网景公司的前雇员Lou Montulli在1993年3月的发明. 服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态.Cookies最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续,这些都是Cookies的功用.另一个重要应用场合是“购

asp.net webconfig文件详解

asp.net webconfig文件详解 一.认识Web.config文件 Web.config 文件是一个xml文本文件,它用来储存 asp.NET Web 应用程序的配置信息(如最常用的设置asp.NET Web 应用程序的身份验证方式),它可以出现在应用程序的每一个目录中.当你通过.NET新建一个Web应用程序后,默认情况下会在根目录自动创建一个默认的Web.config文件,包括默认的配置设置,所有的子目录都继承它的配置设置.如果你想修改子目录的配置设置,你可以在该子目录下新建一个We

ASP.NET性能监视参数详解

性能监视器- Performance Monitor 性能监视器是Windows自带的系统资源和性能监视工具. 性能监视器能够量化地提供CPU使用率, 内存分配状况, 异常派发情况, 线程调度频率等信息. ASP.NET能够提供每秒钟的请求数目, 请求响应时间等等. 性能监视器能够监视一段时间内上述资源的利用情况, 提供平均值和峰值. 性能监视器有助于获取关于性能的具体指标, 监视问题出现时系统资源的变化情况. 通过检查性能监视器中一些重要计数器的变化情况, 往往能够找到一些比较有用的线索. 比

asp.net MVC ViewData详解

控制器向视图中传值ViewData详解 1.将一个字符串传值到视图中 在action中我们将字符串保存在ViewData(或ViewBag [asp.net 3或以上才可用])中代码如下: public ActionResult Index()        {            ViewData["str1"]= "这是一个字符串"; //也可以使用ViewBag来传递值 ViewBag.str2="这是另外一个字符串"; return V