1. 程式人生 > 程式設計 >詳解Asp.net 5中的ApplicationBuilder

詳解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之前

詳解Asp.net 5中的ApplicationBuilder

Build之後

詳解Asp.net 5中的ApplicationBuilder

我們還可以從程式碼中看到Item1的引數給的是“404”,而返回結果是RequestDelegate型別。也就是說這個返回類似於voidRequestDelegate(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,Task>。這樣做有什麼好處?之前的Func<RequestDelegate,RequestDelegate>物件並不能給人清楚的明瞭的感覺,而Func<MyHttpContext,Task>就非常明確了。傳入的引數:MyHttpContext就是Context物件,Func<Task>就是next的執行體。返回值是一個Task(類似於void)。一目瞭然。

再說Run方法,顯而易見,Run方法只執行自己的內容,並沒有執行引數體。所以鏈式結構的在其前的都會被捨棄,不會被執行。

最後把自己的測試例子貼出來,供大家參考

示例1:

static void Main(string[] args)
        {

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

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

            Func<MyHttpContext,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();
        }

執行結果:

詳解Asp.net 5中的ApplicationBuilder

示例2:

static void Main(string[] args)
        {

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

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

            ApplicationBuildQhezWZctxer builder = new ApplicationBuilder();

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

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

執行結果:

詳解Asp.net 5中的ApplicationBuilder

示例3:

static void Main(string[] args)
        {

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

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

            ApplicationBuilder builder = new ApplicationBuilder();

            builder.Use(
                next =>
                {
                    return (MyHttpContexthttp://www.cppcns.com 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();
        }

執行結果:

詳解Asp.net 5中的ApplicationBuilder

到此這篇關於詳解Asp.net 5中的ApplicationBuilder的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援我們。